Files
codex/codex-rs/app-server-client/README.md
Tamir Duberstein 6fc6b9d6d2 Prevent unread events from blocking in-process requests (#38381)
## Why

Awaiting a bounded consumer event queue can stall the in-process app-server
worker when notifications are not being drained, preventing it from delivering
a request response queued behind them.

## What changed

- Use an unbounded queue for caller-facing in-process events while keeping
  command and embedded-runtime queues bounded.
- Preserve all events in order instead of dropping best-effort events and
  emitting lag markers when the consumer queue fills.
- Document that callers can await requests without concurrently draining
  notifications.

## Testing

Add a regression test that fills a capacity-one client with unread settings
notifications, verifies subsequent requests complete, and then confirms the
notifications remain readable in order.

GitOrigin-RevId: 6ca6cfb9349dfa04a613236836f813c1f799783e
2026-08-13 13:57:25 +00:00

2.3 KiB

codex-app-server-client

Shared in-process app-server client used by conversational CLI surfaces:

  • codex-exec
  • codex-tui

Purpose

This crate centralizes startup and lifecycle management for an in-process codex-app-server runtime, so CLI clients do not need to duplicate:

  • app-server bootstrap and initialize handshake
  • in-memory request/event transport wiring
  • lifecycle orchestration around caller-provided startup identity
  • graceful shutdown behavior

Startup identity

Callers pass both the app-server SessionSource and the initialize client_info.name explicitly when starting the facade.

That keeps thread metadata (for example in thread/list and thread/read) aligned with the originating runtime without baking TUI/exec-specific policy into the shared client layer.

Transport model

The in-process path uses typed channels:

  • client -> server: ClientRequest / ClientNotification
  • server -> client: InProcessServerEvent
    • ServerRequest
    • ServerNotification
    • LegacyNotification

JSON serialization is still used at external transport boundaries (stdio/websocket), but the in-process hot path is typed.

Typed requests still receive app-server responses through the JSON-RPC result envelope internally. That is intentional: the in-process path is meant to preserve app-server semantics while removing the process boundary, not to introduce a second response contract.

Bootstrap behavior

The client facade starts an already-initialized in-process runtime, but thread bootstrap still follows normal app-server flow:

  • caller sends thread/start or thread/resume
  • app-server returns the immediate typed response
  • richer session metadata may arrive later as a SessionConfigured legacy event

Surfaces such as TUI and exec may therefore need a short bootstrap phase where they reconcile startup response data with later events.

Backpressure and shutdown

  • Command queues and the embedded runtime remain bounded, using DEFAULT_IN_PROCESS_CHANNEL_CAPACITY by default.
  • The facade's local consumer event queue is unbounded and preserves notification order. This keeps the worker draining the bounded runtime while a caller waits for a request, preventing unread notifications from blocking its response.
  • shutdown() performs a bounded graceful shutdown and then aborts if timeout is exceeded.