App Server: let running turns request fresh host input at sampling boundaries
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What variant of Codex are you using?
App Server v2, as an embedding client.
What feature would you like to see?
Summary
Add an opt-in App Server capability that lets a running Codex turn request fresh host-owned input at the two lifecycle boundaries where Codex has authoritative timing:
- before each logical model sampling step, after prior model/tool results are recorded and before the next provider request is built;
- once before the turn is finalized.
The host should be able to answer asynchronously with zero or more typed, model-visible input items. Codex should record accepted items before constructing the corresponding model request. If the final check returns new input, the same turn should continue; if it returns no input, completion should proceed normally.
The exact RPC name and payload shape are open. The important capability is that Codex asks at the boundary it owns, instead of every embedding host having to guess that boundary and race to push input into an active turn.
The ownership problem with push-only input
Long-running agent work often spans multiple model calls and tool executions. Meanwhile, state outside Codex can change:
- a person adds a correction or changes priority;
- an IDE receives new diagnostics or the active selection changes;
- a CI run produces a new failure;
- a remote controller sends follow-up guidance;
- an issue, task, incident, or external workflow changes while Codex is working.
The host knows that external state changed, but it does not know exactly when Codex will build its next model input or decide that the turn is complete. Codex knows those boundaries, but App Server clients currently cannot register a provider that Codex asks for fresh input at those moments.
With only push APIs, each host must build its own coordination around an internal lifecycle it cannot directly observe. A typical integration has to track some combination of:
active thread and turn identity
expectedTurnId preconditions
last input cursor already submitted
one in-flight steer at a time
messages that arrive while steer is in flight
turn completion racing with the final steer
one more external-state check before accepting completion
retry and late-response behavior
This is not complexity unique to one application. It is repeated integration logic caused by placing timing responsibility on the side that does not own the sampling loop.
A host-requested input boundary turns that distributed race into one request/response interaction at the only point where the answer can be applied deterministically.
Desired behavior
Illustrative flow:
External state changes
-> the host records the durable fact
-> the host may update its UI, but does not need to race a steer
Codex reaches the next reasoning boundary
-> App Server requests fresh host input for this thread/turn
-> the host reads everything after its last accepted cursor
-> the host returns zero or more typed items
-> Codex records the items
-> Codex builds and sends the next model request from that updated history
Codex is about to complete the turn
-> App Server requests host input one final time
-> new items: record them and continue the same turn
-> no items: complete the turn
The final check matters even for a turn that would otherwise make only one model request. A correction that arrives after that request starts but before completion can still be incorporated without the host first accepting a stale completion and starting another turn.
General ecosystem value
This would simplify several classes of App Server integrations:
Collaborative and conversational hosts
A participant can add clarification while Codex is running. The host can persist messages in its own system and return them only when Codex is ready to reason again, rather than coordinating turn/steer against an active-turn race.
IDE hosts
Selections, diagnostics, open editors, build results, and workspace metadata can be refreshed at a step-consistent boundary. The model sees one coherent snapshot for the request instead of whatever the host managed to inject between events.
CI, test, log, and incident integrations
The host can return only newly available failures or observations when Codex is about to continue. There is no polling loop inside the prompt and no need to interrupt a tool-heavy turn merely because external evidence changed.
Remote control and orchestration
Mobile clients, supervising agents, and workflow systems can add guidance without duplicating Codex's turn state machine. Multiple concurrent threads remain naturally isolated by the existing request IDs and thread/turn identities.
Future host extensions
A stable model-boundary request is a reusable primitive. Hosts can own product policy, persistence, authentication, cursors, and external resources; Codex can own when those facts become part of a model step. This avoids pushing product-specific schedulers and external-system concepts into Codex core.
Why this aligns with the current Codex architecture
Checked against main at 728cb12fe5794b0c3a8e776fb4994b1650b973a8.
Codex already centralizes the relevant boundary:
- The turn loop drains internal pending input before preparing the next sampling request in
codex-rs/core/src/session/turn.rs(get_pending_input(&sess.active_turn)). - The extension API already models context that can depend on turn-local state through
ContextContributor. - Per-step world-state construction already awaits those contributors in
codex-rs/core/src/session/world_state.rs. - App Server already has bidirectional server-request infrastructure for approvals, user input, elicitation, resolution, and invalidation.
So the runtime already knows where fresh input can be incorporated consistently, and the protocol already knows how to ask a client a question and correlate its eventual response. The missing piece is an opt-in bridge between those two existing concepts for host-owned model input.
Suggested contract properties
The public naming can follow App Server conventions. The behavior would be most useful if it guarantees:
- Optional capability negotiation. A client that does not opt in receives no new request and observes current behavior unchanged.
- Stable correlation. The request identifies the thread, active turn, and logical sampling/finalization boundary strongly enough to reject stale responses.
- Asynchronous response. The host can read its own database, editor, service, or UI without blocking unrelated App Server routing.
- Typed authority. Returned user messages remain user input; application/developer context remains explicitly distinguished. Host data should not be silently promoted to a stronger role.
- Step-consistent acceptance. Returned items are committed to model-visible history before the request snapshot is constructed, so tools, world state, history, and the model request do not observe different versions.
- Once per logical sampling step. Provider HTTP/WebSocket retries must not cause the host input provider to run again or duplicate accepted items.
- Finalization continuation. New input returned at the final boundary continues the same root turn rather than requiring the client to infer that completion was stale.
- Deterministic terminal behavior. Turn interruption, completion, thread shutdown, or transport death invalidates unresolved requests; late and double responses are rejected.
- Bounded failure. A missing or failed host response must not leave a turn pending forever. The exact fail-open/fail-closed choice could be part of the opt-in contract.
- Per-thread concurrency. Independent threads can have independent pending host-input requests without global serialization.
- Normal persistence. Accepted items survive resume and participate in compaction/history the same way equivalent model-visible input does today.
An implementation may choose a request before every sampling step, or optimize by using a cheap host-provided dirty signal while preserving the same atomic boundary. The important property is that the host does not have to predict the sampling boundary itself.
Backward compatibility and risk
This can be entirely additive:
- existing clients do not advertise the capability and receive no new server requests;
turn/steerremains the right API for an explicit immediate push into an active turn;thread/inject_itemsremains useful for explicit history injection;- existing Hooks, TUI, CLI, Desktop, model providers, and provider request formats do not need to change;
- the host already has the ability to submit equivalent input, so this does not inherently grant a new privilege—it gives that input a deterministic acceptance point;
- rollout can begin as an experimental App Server capability.
The main new protocol surface is one optional server-request variant plus its lifecycle handling. It replaces substantial and subtly different state machines in embedding clients rather than imposing a new execution model on existing users.
Why current alternatives are not equivalent
turn/steeris valuable for explicit push, but the client must know an active turn ID, serialize overlapping steers, and handle completion races. It does not let Codex request input at the moment the next request is built.thread/inject_itemscan append model-visible history, but timing still belongs to the host; it is another push surface rather than a sampling-boundary contract.- Lifecycle Hooks are useful extension points, but event coverage is not identical to every logical sampling step.
PostToolUsedoes not cover a no-tool turn or every provider continuation, while a Stop hook only covers finalization. Hooks also force an embedding host to reconstruct authentication and IPC outside its existing App Server connection. - Polling tools or prompt instructions consume model/tool work, add latency, and still do not provide an atomic pre-request snapshot.
- Forking Codex lets one host install an internal contributor, but fragments the ecosystem and makes every downstream integration track Codex internals independently.
Minimal acceptance cases
- A client without the capability runs existing turns with no protocol or behavior change.
- For
model -> tool -> model, input returned at the boundary before the second sample is present in that second provider request. - For a turn that would otherwise stop after one sample, fresh final-boundary input causes the same turn to continue.
- An empty response does not create a mechanical extra model call.
- Retrying the same provider request does not invoke the host provider again or duplicate items.
- Interrupting or completing the turn invalidates a pending host request, and a late/double response is rejected deterministically.
- Two threads can service independent host-input requests concurrently without cross-thread routing.
- Accepted items retain the requested role/authority and are visible after thread resume.
Additional information
I searched existing issues before filing. Related requests cover host-provided hooks (#38371), per-thread model-visible Skill selection (#42440), and deferred SDK handling of server requests (#42219), but I did not find an issue requesting a general host-input boundary owned by the sampling loop.
I am intentionally not proposing a fixed RPC name. The durable request is the ownership model: the runtime that knows when the next model context is built should ask the embedding host for facts that only the host owns.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the turn loop in codex-rs/core/src/session/turn.rs, including get_pending_input, then read ContextContributor in codex-rs/ext/extension-api/src/contributors.rs and per-step construction in codex-rs/core/src/session/world_state.rs. Inspect the existing App Server bidirectional request handling. Done means an opt-in client can provide typed input at both boundaries, with accepted items persisted, final-boundary continuation working, and the listed lifecycle, concurrency, and late-response cases covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100