Repository navigation
feat: report the model of each native subagent session - #678
Merged
nikita-ashihmin merged 9 commits intoOct 26, 2026
Merged
nikita-ashihmin merged 9 commits into
nikita-ashihmin merged 9 commits into
Conversation
After the adapter announces a native subagent session, it sends a config_option_update on the child session ID. The update holds one select option of category "model". The only entry is the current model of the child, because the client may not change it. The value and name of the entry are those of the same model in the model option of the parent session, so a client can compare them. A model that the parent option does not offer keeps its full ID. The model comes from the resolved model of the subAgentActivity item, then from the spawnAgent request. A spawn without a model gets the model of its parent. A child thread start or settings change that reports another model sends a new snapshot. A reopened child and a replayed child get the same update. This follows "Child configuration" in the subagents RFD.
History replay now sends the model option of a child only when the history names its model: the resolved model of the subAgentActivity item, or the model of the spawnAgent request. The current model of the parent can differ from the model at the spawn, so replay does not use it. A child without a named model gets no model option.
- A spawn without a model takes the model of its parent at the spawn, so a root model change before the announcement or a reopen does not change the reported model. - A client without session configuration (JetBrains 2026.1) gets no model update for a subagent. - A parent without models has no model option, so the child keeps the full model ID as the value and the name. - A failed model report is logged and does not stop the announcement. - A thread model that gives the same option value sends no new update.
The router now keeps the source of each child model. At the creation the order is the subAgentActivity model, then the thread/started model, then the spawnAgent model, then the model of the parent. A report from a lower source does not replace a higher one, so a thread/started of a resume keeps the activity model. A thread/settings/updated is a real model change and always wins. - A settings change of a pending child is read before the router buffers it, so the announcement has that model and the replay sends no copy. - A thread/started that arrives before the spawn names its child is kept until the spawn. The root turn end clears these models. - A child that requests no model inherits the root model of the running turn, not a model that the client set during the turn.
The adapter no longer guesses the model of a child from the spawnAgent request or from the parent. A role config can override the requested model, and the parent model can differ from the child model. The model now comes only from what Codex states about the child: the resolved subAgentActivity model, the model of the child thread in thread/started, and a thread/settings/updated change, ranked in that order. A child without a stated model gets no model update, which the subagents RFD allows. History replay sends only the activity model of the child. Codex 0.164 sets the subAgentActivity model of a multi-agent v2 spawn to the resolved model of the child, and only these activities announce a native subagent session. So a live child does not lose its model.
- The router reads a settings change of a pending child once. The buffered copy that comes back after the announcement is not read again, so two changes give one update with the last model. - An early thread/started model is kept only when the thread names this root, or a child of it, as its parent, or while a spawn of this root is in flight. The root turn end and the end of the outstanding children clear these models. - A model report checks the child just before it sends. A child that ended, or that a reopen gave another session, gets nothing. - The live session and the replay share one function that sends the model update and logs its failure.
This was referenced Oct 26, 2026
This was referenced Oct 26, 2026
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Third standalone extraction from #655, after #676 and #677 landed. Based directly on current `main` (`12e2d16`), including #678; no subagent runtime or production files change. - Add an optional request-body response selector so concurrent root, child, and title requests can receive the correct scripted responses without depending on arrival order. - Support synchronous handlers and handlers that await a response. Preserve FIFO behavior when no selector is installed; explicit HTTP errors still take precedence, and an undefined selection is an unexpected request rather than a queue fallback. - Return a fixed generic HTTP 500 on handler failure and keep diagnostics in server-side logs. Do not write replies to disconnected clients. - Generalize function-call SSE replies to named/namespaced tools. The existing shell helper delegates without changing its payload. ## Standalone coverage Seven direct tests cover FIFO compatibility, concurrent out-of-order completion, error/path precedence, unhandled requests, synchronous/asynchronous failure and recovery, and named/namespaced function serialization. Snapshots contain stable payloads rather than changing response IDs. The original #655 only tested handler failures directly; this extraction adds coverage for the fixture's useful behavior without needing the later subagent feature. ## Validation - `npm ci --ignore-scripts` - `npm run typecheck` - Focused mock-server/model-request suite: **10 passed** - `npm test`: **1,915 passed**, 36 skipped - `npm run test:integration`: **7 passed**, using the real Codex 0.164.0 app-server and local mock model, including existing shell-tool flows - `npm run build` - `git diff --check` Only test infrastructure is changed. The full live-model E2E suite and local cross-platform bundling were not run for this test-only extraction. Existing dependency audit findings are outside this scope.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Fourth standalone extraction from #655, after #676, #677, and #680 landed. **Two source files, +34/-27 lines.** Based directly on current `main` (`1685cde`), preserving native child model reporting from #678. Group the existing command/fuzzy-search/guardian reporters, image bookkeeping, reasoning deduplication, and message-phase map into `CodexItemStreamState`. - Each event handler still allocates exactly one private instance, with the same seven field types and initializers. - Every existing access goes through that instance. No state sharing, injection, lifetime extension, routing changes, or new standard-subagent code is introduced. - Plans, errors, tool-call tracking, session state, and subagent ownership are untouched. This makes the existing rendering-state boundary explicit and usable today without landing the later lifecycle changes. It is not the change that makes child rendering survive parent handler replacement; that belongs in the complete live-subagent core. ## Behavior-preservation evidence A source comparison against `main` verified that the constructor and all method bodies are byte-identical after removing the added `this.itemStreams.` prefix, and that all seven initializers match their original declarations. The three reporter classes have no constructor side effects. No tests or snapshots were added, updated, or removed. Existing event-driven suites provide the output-regression coverage, including AIR and v1/v2 paths. ## Validation - `npm ci --ignore-scripts` - `npm run typecheck` - `npm run build` - `npm test`: **1,915 passed**, 36 skipped - `npm run test:integration`: **7 passed**, with the real Codex 0.164.0 app-server and a local mock model - `npm run bundle:all`: all six targets compile - Live-model no-tool smoke: ACP text chunks formed `item-state-refactor-ok` - `git diff --check` The full live-model E2E suite and execution of every cross-platform binary were not run. Existing dependency audit findings are outside this refactor.
Rizzen
pushed a commit
that referenced
this pull request
Oct 26, 2026
🤖 I have created a release *beep* *boop* --- ## [2.3.0](v2.2.2...v2.3.0) (2026-10-26) ### Features * add ACP v2 support again ([#620](#620)) ([9e1690f](9e1690f)) * mark session list rows of worktrees Codex made (agentWorktree) ([#631](#631)) ([aa02d43](aa02d43)) * narrow models to what the providers/set gateway lists ([#627](#627)) ([3f555f9](3f555f9)) * report the model of each native subagent session ([#678](#678)) ([51005a6](51005a6)) * return initial ACP v2 session commands ([#652](#652)) ([5e9b7c5](5e9b7c5)) * support AIR sessionIndex extension ([#590](#590)) v2 ([#622](#622)) ([2fd758c](2fd758c)) * support append-mode ACP system prompts ([#653](#653)) ([5993a12](5993a12)) ### Bug Fixes * disable Codex's Visualize skill, whose output ACP clients show as raw text ([#667](#667)) ([0ba94f5](0ba94f5)) * display the selected wire provider as JetBrains Air Gateway ([#589](#589)) ([cc54abf](cc54abf)) * explain Codex config load errors caused by a deleted working directory ([#626](#626)) ([a636804](a636804)) * explain invalid_encrypted_content errors after a provider change ([#668](#668)) ([a77829f](a77829f)) * hold a fork back from the session list until its session/fork response is out ([#642](#642)) ([59974c7](59974c7)) * include subagent edits in the agent file-change report ([#632](#632)) ([7d07123](7d07123)) * leave codex exec runs out of the session list without sessionIndex ([#639](#639)) ([df75d2b](df75d2b)) * migrate to ACP SDK 2.0 ([#649](#649)) ([c8fd166](c8fd166)) * publish the commands when the skills change ([#643](#643)) ([a62b568](a62b568)) * report structured ACP v2 failures ([#651](#651)) ([5377bfe](5377bfe)) * show a Codex subagent's follow-up turn in a new subagent session ([#634](#634)) ([7c761c7](7c761c7)) * synchronize ACP v2 foreground state ([#650](#650)) ([986955a](986955a)) * update codex to 0.164.0 ([#666](#666)) ([e227c0e](e227c0e)) * wait for the compact turn to finish ([#654](#654)) ([ced4c34](ced4c34)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: acp-release-bot[bot] <246668977+acp-release-bot[bot]@users.noreply.github.com>
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Scope: standalone live core Fifth incremental extraction from #655, on current `main` (`091f9b2`), after #676, #677, #680, and #681 landed. This is the larger atomic feature slice discussed in the split plan: **43 files, +2,898/-181**, including 1,573 added test/snapshot lines and 1,301 added runtime lines. No dependency or generated app-server files change. ### Included - Standard associations for opted-in non-AIR v1 clients and non-AIR v2 clients. AIR retains its existing opt-ins, private updates, generation identities, and fallback. - One opaque conversation ID across assignments, idle work, and handler replacement. Announce immediate parents before children; buffer unresolved ancestry rather than flattening it. - Child-owned streamed-item/tool state, plans, titles, permissions, and elicitation. Accepted URL completion state survives parent attachment changes; late approval replies are rejected locally when actual work ends. - Empty child capabilities and an allowlist boundary for all client mutations, including private extensions and raw provider IDs. Known raw IDs are restricted even while announcement publication is backpressured. Parent cancellation cascades to observed live children; an actual ending produces idle, observability loss produces unknown. - Association-only replay and safe reattachment: current provider parentage must match through known ancestors, inactive owned children receive future events without loading them, and copied forks cannot steal the original runtime. Parent history still replays normally; an explicit advisory says recorded child transcripts are not included. - Current child snapshots follow the real successful wire write, including complete JSON-RPC batches. Same-parent setup execution is serialized without waiting on each other's shared batch delivery. Closing a parent fences queued reopen attempts. - Preserve the model reporting already landed in #678: reuse its source priority and read-only model option helper. Model observations are not guessed from spawn arguments or parent values; buffered older settings cannot replace newer observations. ### Explicitly deferred No recursive child transcript replay, history/live item reconciliation, recorded historical work outcomes, new configuration readback/effort reporting, directed-session messages, or child mutation controls. Those remain separate changes after this live core is reviewed and landed. The current RFD explicitly allows live support without complete child history. Recoverable associations are replayed truthfully; omitted transcripts are not silently presented as complete. ## Review order 1. Contract selection, connection-owned identities/restrictions, and ACP mutation dispatch. 2. Live router admission, child-owned rendering/work/request state, and existing model reporting. 3. Provider subscriptions and ownership verification; no parent cwd/MCP/instruction overrides when rejoining a child. 4. Conversation-owned interaction handlers and request cancellation scopes. 5. Session setup lane, batch-write freshness boundary, and association-only replay. 6. Behavior-focused tests and wire snapshots for both versions. The async queue-completion gap found while planning the split is fixed here: a session transition waits for async callbacks and propagates rejection without breaking the notification lane. A regression reproduces both ordering and rejection behavior. The old #655 reference helper must not be copied back over this implementation. ## Validation Final validation used main's latest lockfile (including #679): - `npm run typecheck` - `npm run build` - `npm test`: **1,959 passed**, 38 skipped - Focused live/wire/queue suites: **44 passed**, 2 v2-only cases skipped on v1 - `npm run test:integration`: **15 passed** — eight new real-Codex 0.164.0 cases cover active-child reattachment, fork identity/ownership isolation, parent cancel/close, and child terminal output/exit across later parent prompts, on v1 and v2, using a local scripted model - `npm run bundle:all`: all six targets compile - Real live-model no-tool smoke completed with `end_turn` - `git diff --check` Existing AIR and parent-session golden files are unchanged. The fixed-command terminal fixture alone uses full-access mode to avoid CI hosts without sandbox namespace support; production sandbox settings are unchanged. The full live-model E2E suite and execution of every OS bundle were not run. ## References - ACP specification at `8aa92106feba5cdd0da210a87a39dfee4dc24f13`: [identity, announcement, and automatic routing](https://github.com/agentclientprotocol/agent-client-protocol/blob/8aa92106feba5cdd0da210a87a39dfee4dc24f13/docs/rfds/subagents.mdx#L166-L246), [best-effort replay and response freshness](https://github.com/agentclientprotocol/agent-client-protocol/blob/8aa92106feba5cdd0da210a87a39dfee4dc24f13/docs/rfds/subagents.mdx#L713-L840). - Official TypeScript SDK checkout `c7e07388feed7a530f8211d0dae4951cb6d26d09` and published 2.0.2 for transport/runtime evidence; normative behavior comes from the spec, not the SDK implementation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
After the adapter announces a native subagent session (
subagent_spawned), it sends aconfig_option_updatewith the child session ID. The update holds oneselectoption of categorymodel. Its only entry is the current model of the child, because the client may not change it.The entry has the same
valueandnameas the same model in themodeloption of the parent session, so a client can compare them. A model that the parent option does not offer, or a parent without a model option, keeps the full model ID as the value and the name.Where the model comes from. The adapter reports only what Codex states about the child, ranked in this order:
modelof the child thread inthread/started. It also counts when it arrives before the spawn names its child, if the thread names this root (or a child of it) as its parent or a spawn of this root is in flight. The root turn end clears these early models;modelof thesubAgentActivityitem that announces the child;thread/settings/updatedof the child, which is a real model change and always wins.A report from a lower source does not replace a higher one, so a
thread/startedof a resume keeps the activity model. A settings change sends a new snapshot. For a child that is not announced yet, the announcement carries the last settings model, and the buffered changes send no more updates. A model that gives the same option value sends no new snapshot. A reopened child (a new generation) gets the same update. ACP v2 gets the same update in the v2 config option shape.The adapter does not guess: it does not use the
spawnAgentmodel (a role config can override it) or the model of the parent. A child without a stated model gets no update, which the RFD allows. In Codex 0.164 thesubAgentActivitythat announces a multi-agent v2 child carries the resolved model, so a live child normally has one.History replay sends only the
subAgentActivitymodel of the child. Older records without it get no update.No update is sent for a child with no known model, to a client without session configuration (JetBrains 2026.1), or to a client without native subagent sessions. A child that ended, or that a reopen gave another session, gets no report. A failed model report is logged and does not stop the announcement or the replay.
Example (ACP v1):
{"sessionId": "child-1", "update": {"sessionUpdate": "config_option_update", "configOptions": [ {"id": "model", "name": "Model", "description": "Model Codex uses for the subagent", "category": "model", "type": "select", "currentValue": "gpt-5.2-codex", "options": [{"value": "gpt-5.2-codex", "name": "5.2 Codex", "description": "gpt-5.2-codex model"}]} ]}}This follows Child configuration in the subagents RFD (see also agentclientprotocol/agent-client-protocol#2482).
Related