Repository navigation
Conversation
This was referenced Oct 25, 2026
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary First independent extraction from #655. This PR targets `main` directly and does not depend on the standard-subagent implementation or any other extraction. - Raise the minimum ACP SDK patch from `~2.0.0` to `~2.0.2`. - Update only the SDK's lockfile version, URL, and integrity. No other dependencies or adapter source files change. - SDK 2.0.1/2.0.2 include HTTP stream-conflict isolation, CR-only SSE line-ending support, and request-scoped POST failure handling. This adapter's existing stdio behavior is checked against the new package; no HTTP-specific adapter changes or new subagent behavior are included. ## Validation Run on this branch based directly on `main` at `a77829f`, without #655's test configuration or runtime changes: - `npm ci --ignore-scripts`: SDK resolves to 2.0.2; Codex remains 0.164.0 and Vitest remains 5.0.5. - `npm run typecheck` - `npm test`: **1,879 passed**, 36 skipped - `npm run test:integration`: **7 passed**, using the real Codex app-server with a local mock model - `npm run build` - `npm run bundle:all`: all six targets compile - `git diff --check` The full live-model E2E suite, direct HTTP transport tests, and execution of every cross-platform bundle were not run. The existing five npm audit findings are outside this dependency-only extraction. ## Split plan This can be reviewed and merged independently. #655 stays open as the reference implementation; subsequent changes will be extracted into separate, independently validated PRs rather than landing that monolithic diff.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Second independent extraction from #655. **One file, 11 additions.** Based directly on `main`; it does not depend on the SDK update in #676 or on any subagent code. Codex integration fixtures can download plugins containing their own tests under `tmp/`. Vitest's default repository-wide discovery can collect those foreign tests. Scope discovery to the repository-owned `src/__tests__/` and `scripts/` directories instead. - Reuse `configDefaults.include`, preserving Vitest's supported test/spec filename patterns rather than narrowing to `.test.ts`. - Keep the release-tooling tests under `scripts/`. - Use an ESM `.mjs` configuration, which does not introduce a root-level TypeScript file outside the application's `rootDir: src`. - Leave test implementations, skip behavior, command-line exclusions, dependencies, and production code unchanged. ## Validation Validated independently on `main` at `a77829f`, using its SDK **2.0.0**, Codex **0.164.0**, and Vitest **5.0.5**: - Controlled before/after discovery check using `vitest list --filesOnly --json`: - Default discovery included a temporary `tmp/vitest-discovery-probe.spec.ts`. - The new configuration excluded only that foreign file; every other file was preserved. - An owned `.spec.mjs` probe and `scripts/next-preview-version.test.mjs` remained discoverable. - Temporary probes were removed before running the suites and are not part of this PR. - `npm run typecheck` - `npm test`: **1,879 passed**, 36 skipped, matching the unmodified discovery run on the SDK extraction - `npm run test:integration`: **7 passed**, with the real Codex app-server and a local mock model - `npm run build` - `git diff --check` No snapshots changed. The full live-model E2E suite and local cross-platform bundling were not run for this config-only change.
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.
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.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Scope Sixth standalone extraction from #655, directly on current `main` (`fe3ae92`) after the live core #682 landed. **16 files, +864/-50**, mostly direct tests and snapshots. No negotiation, capability, dependency, or generated app-server changes. Enhances only the existing standard-subagent path: non-AIR v1 still requires client opt-in; non-AIR v2 uses it by default. AIR retains its separate replay and identity rules. ## Changes - Follow both recorded `subAgentActivity` and collab spawn formats, replaying each conversation once, recursively under its immediate parent. Resolve recoverable reversed ancestry, bound cycles, and never guess or change parentage. - Read legacy or paginated child items lazily. Current-provider ownership verification and live subscription restoration happen before item reads. A retained transcript remains displayable without granting ownership or current work state. - Send all selected history before the load/resume response, preserving the live core's actual batch-write freshness boundary. - Reconcile tool/rendering bookkeeping before suppressing duplicate presentation. Replayed completed tools cannot be failed by later cleanup or resurrected by stale running snapshots. Live completion bookkeeping still runs, and completed runnable items do not leave a live permission incorrectly reported as running. - Restore in-progress command classification without resetting streamed-byte tracking. Duplicate live starts do not reset the reporter, and completion does not resend already streamed output. - Report known gaps once at the parent while continuing available sibling replay. Reject mismatched thread identities. Empty history alone invents no gap, ending, failure, or runtime. ### Conservative limits Agent-owned input is not reinterpreted as human input. Incomplete text snapshots have no safe delta cursor and are omitted rather than duplicated over newer buffered deltas; unfinished command replay carries metadata rather than previously streamed bytes. Only renderers with a safe partial form are used for unfinished items. Open-ended provider status strings (such as image generation status) are not treated as completion enums: those items use recorded completion metadata instead, with legacy and paginated regressions. These omissions produce an advisory. Complete retained content is replayed normally. Historical work-state outcomes, directed-session messages, new child controls, and broader configuration reporting remain deferred. Existing provider-parentage checks still prevent copied fork history from stealing the original runtime. ## Coverage **36 new v1/v2 cases** cover both spawn formats, one visit per conversation, nested/reversed ancestry, paginated item ordering, missing/mismatched snapshots, completed-tool reconciliation, running-command restoration, incomplete-text overlap, stale running history, live permissions, cyclic records, close-during-read, and no-replay behavior. Wire assertions verify recorded content precedes the response; no recorded permission or elicitation request is reissued. Real-Codex integration cases now reload finished children and assert that their retained answers replay under the same child IDs, on v1 and v2. The existing fork/cancel/close/terminal-lifetime checks remain in place. ## Validation - `npm run typecheck` - `npm run build` - `npm test`: **1,995 passed**, 38 skipped - Focused live/replay suites: **77 passed**, 2 v2-only cases skipped on v1 - `npm run test:integration`: **15 passed**, with the real Codex 0.164.0 app-server and a local scripted model - `npm run bundle:all`: all six targets compile - Live-model no-tool smoke completed with `end_turn` - `git diff --check` Existing golden files are unchanged. One live-core assertion changes intentionally: an empty retained transcript no longer produces the blanket “child transcripts are not included” advisory. New replay cases verify advisories for actual failures and incomplete/omitted content. The full live-model E2E suite and execution of every OS bundle were not run. Native integration uses the default provider history store; explicit paginated-branch coverage uses SDK-level fixtures. ## Contract references ACP specification `53620d0b325d6815b3cff1ca7e909351d488fdf0`: [best-effort replay, per-session identity/order, gaps, and freshness](https://github.com/agentclientprotocol/agent-client-protocol/blob/53620d0b325d6815b3cff1ca7e909351d488fdf0/docs/rfds/subagents.mdx#L713-L836). Official TypeScript SDK `c7e07388feed7a530f8211d0dae4951cb6d26d09` / published 2.0.2 is runtime evidence, not the normative contract.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Step 7 of the incremental extraction tracked in #655, directly against current `main` after #684. This is read-only configuration reporting, not historical work outcomes or child controls. - Extend standard child configuration from model-only reports to complete reported model/reasoning-effort snapshots. Every select offers only its observed current value; neither parent values nor requested spawn arguments supply child values. - Preserve the existing model-source precedence and model labels. Newer observations remove unset/unavailable effort, including empty snapshots when no fields remain reportable. A model change cannot carry the previous model's effort. - Keep recorded configuration historical. Reattachment drops cached confirmation; fresh observations and verified, loaded provider metadata establish current configuration after the actual response write, including whole JSON-RPC batches. - Run optional metadata reads outside the notification lane and rejoin that lane only to publish. Newer observations, unload/close, or another attachment invalidate late results. Reads verify the physical child ID and immediate parent without loading or changing the child. - Ignore foreign runtime starts for history-only children; copied associations do not establish current configuration or ownership. V1 standard opt-in, v2 defaults, empty child capabilities, mutation restrictions, and AIR's existing contract remain unchanged. AIR's only source change is exporting the existing source-priority constant for reuse. Dependencies and generated app-server types are untouched. Existing AIR and parent-session snapshots have no changes. ## Review size 9 files, +653/-36 lines. About four fifths of the additions are tests/snapshots. Runtime changes stay within the standard router, its server wiring, and a small shared snapshot builder used by live and historical reporting. ## Validation - `npm run typecheck` - `npm test`: **2,028 passed**, 39 skipped. Includes **33 new v1/v2 wire regressions**, with one v2-only batch case skipped on v1. - `npm run build` - `npm run test:integration`: **15 passed** against real Codex 0.164.0 and a local mock Responses API. Native child tests now compare reported effort with the child's actual Responses request, both initially and after parent reattachment. - `npm run bundle:all`: all six platform targets compiled. - `npm run codex-test -- -p 'Reply with exactly child-configuration-ok. Do not read files or use tools.' -c <isolated-scratch-directory> -o summary`: live-model smoke completed with `end_turn`. - `git diff --check` Wire tests cover complete singleton snapshots, effort-only/unknown/cleared values, model changes, source precedence and pre-announcement buffering, no-op mutation rejection, changed and unchanged reattachment snapshots, full and no-replay attachment, delayed batches, pending reads without blocked output, newer native settings, late reads across attachments/unload/close, untrusted metadata, and historical-only configuration. The full live-model E2E suite was not run. Cross-platform bundles were compiled, not executed on each target OS. ## Protocol / reference check - Specification `main` at `53620d0b325d6815b3cff1ca7e909351d488fdf0`: [RFD child configuration](https://github.com/agentclientprotocol/agent-client-protocol/blob/53620d0b325d6815b3cff1ca7e909351d488fdf0/docs/rfds/subagents.mdx#L252-L285) supplies the proposed child reporting, singleton-choice, and reattachment semantics. Existing draft configuration notifications require complete state in [v1](https://github.com/agentclientprotocol/agent-client-protocol/blob/53620d0b325d6815b3cff1ca7e909351d488fdf0/docs/protocol/v1/draft/session-config-options.mdx#L337-L368) and [v2](https://github.com/agentclientprotocol/agent-client-protocol/blob/53620d0b325d6815b3cff1ca7e909351d488fdf0/docs/protocol/v2/draft/session-config-options.mdx#L333-L382). - TypeScript SDK `main` at `c7e07388feed7a530f8211d0dae4951cb6d26d09`, with the installed SDK **2.0.2** used for real wire decoding/schema checks. SDK runtime evidence is separate from the specification; the existing actual-write response boundary and ordered session-transition helper are reused, not replaced. - Codex's generated `Thread` metadata documents model/effort as current configured values when loaded, or persisted values otherwise. Readback accepts only loaded, correctly parented children; persisted metadata alone does not confirm current configuration. Historical work outcomes remain the next extraction. This PR does not merge or replace #655 wholesale.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Final planned extraction (step 8) from #655, directly against current `main` after #685. - Replay the **latest recorded child turn's** normal completion, cancellation, or failure as historical `idle` before the load/resume response, using the same stop-reason/error mapping as live turn endings. - Emit no outcome for missing or still-running turn metadata. Do not substitute an older completed turn for a latest running turn, or infer endings from activity-item kind, missing content, or runtime loss. - Reuse the legacy transcript's turn payload without a second full read. Paginated history adds one optional latest-turn summary read (`limit: 1`, descending, no items); an unavailable summary retains available transcript content and uses the existing history-gap advisory. - Publish historical state directly, without changing the live foreground cache, ending current interactions, establishing runtime ownership, or enabling mutations. Parent closure fences late summaries. V1 opt-in gating, v2 defaults, AIR behavior, current configuration reporting, child identities, ancestry, and empty capabilities remain unchanged. Dependencies and generated app-server types are untouched. Existing AIR and parent-session golden transcripts are unchanged. Two existing v2 child-replay snapshots gain the intended historical `state_update`. ## Review size 15 files, +422/-22 lines. Most additions are tests/snapshots; runtime changes touch only the history reader, standard replay dispatcher, and standard child router. The router shares one explicit terminal-status mapping between live and historical reporting, but historical publication bypasses the live state cache. ## Validation - `npm run typecheck` - `npm test`: **2,064 passed**, 39 skipped, including **36 new v1/v2 outcome regressions**. - `npm run build` - `npm run test:integration`: **15 passed** with real Codex 0.164.0 and a local mock Responses API. The v1/v2 completion cases now close the parent and cold-load it before checking the recorded ending, avoiding a cached live ending as a false positive. - `npm run bundle:all`: all six targets compiled. - `npm run codex-test -- -p 'Reply with exactly historical-outcomes-ok. Do not read files or use tools.' -c <isolated-scratch-directory> -o summary`: live-model smoke completed with `end_turn`. - `git diff --check` Wire regressions cover both history stores, completion/cancellation/failure, unavailable or running final turns, response ordering, no mutation/interrupt authority, independent paginated summary failures, latest-turn selection, a subsequent non-replaying resume, newer live permissions and actual work completion, nested ancestry and repeated spawn records, late summaries after close, and failures without details. The full live-model E2E suite was not run. Cross-platform bundles were compiled, not executed on each target OS. ## Specification / reference check - Current specification `main`, `5a5ab96ea8e0f6d3d5c5d7de3a7d29a87c79ddd2`: the [subagent RFD replay rules and freshness boundary](https://github.com/agentclientprotocol/agent-client-protocol/blob/5a5ab96ea8e0f6d3d5c5d7de3a7d29a87c79ddd2/docs/rfds/subagents.mdx#L724-L805) distinguish historical pre-response state from confirmed current activity and controls, and prohibit inventing outcomes from missing history. - The [explicit replayed-idle clarification](https://github.com/agentclientprotocol/agent-client-protocol/blob/e839564daab1da2c68cb8a006c786dfcbf0e68f1/docs/rfds/subagents.mdx#L763-L809) is still on the pending `rfd-replayed-subagent-outcomes` branch, **not specification main**. It permits keeping a recorded idle outcome visible without treating it as current activity or authorization. This implementation is compatible with both versions and requires no new wire fields. - TypeScript SDK `main`, `c7e07388feed7a530f8211d0dae4951cb6d26d09`, checked separately as reference/type evidence. Installed SDK **2.0.2** performs real wire decoding in the regression tests; this change reuses the existing v1/v2 state publication and actual response-write freshness boundary. This finishes the eight planned extraction scopes once merged; it does not merge the old combined #655 wholesale. Child mutation controls and directed-message extensions remain outside this work.
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary Small replay omission found while auditing the completed #655 extraction after #686 merged. All eight planned slices are already on `main`; this is a narrowly scoped correctness follow-up, not another feature tranche. A legacy `spawnAgent` record can establish a coordinator's identity and parent without an agent path. Later replay may supply that coordinator's path and newly recorded nested child activity. Previously `announceHistory` returned immediately for the known coordinator, discarding the available path. The nested activity then could not resolve its immediate parent, so valid descendant history was omitted with a gap advisory. Fill a known child's **missing** normalized path from recorded activity before descending into its history. Do not replace an already observed path with older history. Identities, established parent links, titles, runtime ownership, capabilities, AIR behavior, and v1/v2 negotiation remain unchanged. **2 files, +39/-1 lines. No snapshot changes.** ## Regression evidence - Four v1/v2 warm/cold replay cases failed on current `main` before the fix: a live or restored pathless coordinator prevented newly recovered descendant text from replaying. - Those four cases now replay the text under the correct immediate parent without a false gap. - Two additional v1/v2 cases preserve an already observed parent path when an older spawn record disagrees. ## Validation - `npm run typecheck` - `npm test`: **2,070 passed**, 39 skipped - `npm run build` - `npm run test:integration`: **15 passed** against real Codex 0.164.0 with a local mock Responses API - `npm run bundle:all`: all six targets compiled - `git diff --check` The full live-model E2E suite and an additional live-model smoke test were not run for this fix. Cross-platform bundles were compiled, not executed on each target OS. ## Audit context The remaining old #655 diff largely reflects replaced implementations, differently organized tests/snapshots, and intervening trunk work such as native model reporting. In particular its older queue-completion helper and model implementation must not replace the corrected trunk versions. The combined PR should not be merged wholesale. Checked current specification revision `c0897908cb2305e89203218c348d69ab4acf9d61`. This fix retains the [RFD's truthful identity, parentage, and parent-before-child replay ordering](https://github.com/agentclientprotocol/agent-client-protocol/blob/c0897908cb2305e89203218c348d69ab4acf9d61/docs/rfds/subagents.mdx#L724-L755); it adds no wire fields or mutation authority.
Member
Author
|
Superseded by the independently validated and merged incremental PRs: #676, #677, #680, #681, #682, #684, #685, and #686, plus the completion-audit fix #687 (main at 6cce035). Closing without merging the old combined diff. The reference branch is intentionally retained. The tracker records the remaining optional hardening/test follow-ups separately; child controls and directed-message extensions remain outside this work. |
benbrandt
added a commit
that referenced
this pull request
Oct 26, 2026
## Summary
Expose Codex's existing child-turn interruption through standard ACP
`session/cancel`, without opening other child mutation controls.
- Advertise `capabilities.cancel: {}` only for owned, observable
standard children whose current turn can be identified. Revoke with
`cancel: null` when the work ends or becomes unobservable. Idle or
history-only children get no grant.
- Accept the opaque ACP child ID, never a raw provider-ID bypass.
Interrupt that child's current turn and observed owned descendant work,
not its parent or siblings. A later assignment retains the same child
identity and can receive a fresh grant.
- Withdraw affected permission/elicitation requests locally and reject
accepted late replies. Deduplicate in-flight interrupts and permit retry
after provider failure.
- Use receipt-time completion/observability facts and the latest
provider turn ID even while rendering is backpressured. A successful
interrupt acknowledgement does not fabricate a cancelled outcome; actual
provider completion supplies it.
- Suspend existing grants at parent setup admission. Only the latest
successful, non-cancelled setup response can restore them, after its
actual wire write (including the full JSON-RPC batch). Failed attempts
cannot release current child controls.
Standard v1 opt-in, v2 defaults, and AIR's existing contract are
unchanged. Prompt/configuration/load/close/private-extension mutations
remain forbidden for standard children. Copied history cannot acquire
the original runtime's controls. Dependencies and generated app-server
types are unchanged.
## Review scope
19 files, +602/-20 lines, mostly tests/snapshots. Runtime work is
concentrated in the standard child router, the cancel dispatch, and the
successful setup-response boundary. Eight existing standard-child
snapshots gain the intended cancel grant/revocation; AIR and ordinary
parent-session snapshots are unchanged.
## Validation
- `npm run typecheck`
- `npm test`: **2,095 passed**, 40 skipped; final run did not update
snapshots. Includes **25 new v1/v2 wire regressions** (one v2-only batch
case skipped on v1).
- `npm run build`
- `npm run test:integration`: **17 passed** against real Codex 0.164.0
with a local mock Responses API. New v1/v2 cases receive a current
cancel grant after reattachment, send `session/cancel` to the child,
observe Codex's actual cancelled ending, and continue prompting the
parent independently.
- `npm run bundle:all`: all six platform targets compiled.
- `npm run codex-test -- -p 'Reply with exactly child-cancel-ok. Do not
read files or use tools.' -c <isolated-scratch-directory> -o summary`:
live-model smoke completed with `end_turn`.
- `git diff --check`
Wire coverage includes sibling/ancestor isolation, nested descendant
cascade, real-ending confirmation and later work, request withdrawal and
late replies, in-flight deduplication, raw/foreign/stale IDs, unload
under backpressure, newest provider turn targeting, provider-error
retry, loaded foreign fork history, delayed replay responses, failures
before/after provider setup, and whole-batch freshness.
The full live-model E2E suite was not run. Cross-platform binaries were
compiled, not executed on every target OS.
## Protocol / provider evidence
- ACP specification revision `36b3fcc14d1834d6cc2a5ea3f317a49f198c2f79`:
the [subagent RFD cancellation and ownership
rules](https://github.com/agentclientprotocol/agent-client-protocol/blob/36b3fcc14d1834d6cc2a5ea3f317a49f198c2f79/docs/rfds/subagents.mdx#L859-L930)
define the object-valued cancel capability, child-directed cancellation,
descendant policy, actual completion evidence, and pending requests. The
[replay freshness
boundary](https://github.com/agentclientprotocol/agent-client-protocol/blob/36b3fcc14d1834d6cc2a5ea3f317a49f198c2f79/docs/rfds/subagents.mdx#L763-L805)
forbids historical grants and invalidates failed attempts.
- SDK reference revision `c7e07388feed7a530f8211d0dae4951cb6d26d09`,
separately checked for capability/notification shapes. Installed SDK
**2.0.2** decodes the real v1/v2 test wire. No protocol/schema changes
or new RPC methods are needed.
- Codex's existing `turn/interrupt` method is exercised directly by the
real-app-server child cancellation cases, not inferred from schema
alone.
This follows the completed native-subagent extraction; it does not
reopen or merge the superseded combined #655.
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.
Incremental extraction — completed; reference only
This PR was split into independently reviewable and shippable changes; all eight planned extractions are now merged. Keep it as the reference implementation; do not merge the full diff wholesale. The extraction PRs target
maindirectly, not this branch or each other.Completion audit — main at
9091b73All eight planned slices above are on trunk, including #685 and #686. Compared the old reference tree directly against current main rather than treating the stale three-dot PR diff as unlanded work. Most remaining differences are superseded implementation, differently organized/expanded tests, and intervening trunk changes. Do not copy back the old non-awaiting transition helper, weaker replay reconciliation, pre-#678 model reporting, older dependency/release files, or old tests wholesale.
Merged audit follow-up: #687. A coordinator already known through a pathless spawn did not learn its available recorded path before nested child replay. Four new v1/v2 warm/cold regressions fail without the fix; two more preserve an already observed path against older history. The fix is two files, +39/-1 lines, with no identity, parent, title, ownership, capability, or AIR changes. Current validation: typecheck/build, 2,070 unit tests, 15 real-app-server integration tests, and all six bundles. The follow-up merged to main as
6cce035. All planned extractions and this audit fix are merged; the old combined diff is superseded and must not be merged wholesale. The original branch remains available for reference.Separate follow-up candidates, not bundled into #687: the reference uses iterative drains where two standard-router pending-buffer drains still use argument spreading (a byte cap is not an argument-count cap), and contains some additional focused assertion coverage for existing foreground/writer behavior. These are hardening/test follow-ups, not missing planned feature scopes. Child controls and directed-message extensions remain deliberately outside this extraction.
Main also includes native child model reporting from #678. Subsequent runtime extractions must preserve/reuse that implementation rather than overwrite it with the older reference code.
Tests and snapshots travel with the behavior they verify. New feature pieces must be validated separately; this combined PR's green CI does not establish that an arbitrary subset is standalone.
Known gap to close before the configuration piece lands
A focused runtime probe confirmed that
enqueueSessionTransitioncompletes before an async callback settles. Configuration publication passes such a callback. Fixed in the #682 live-core extraction: async completion and rejection propagation now participate in the notification lane, with a permanent ordering/rejection regression. The old reference branch itself still contains the earlier helper; do not copy it back over the extracted implementation. Neither #676 nor #677 includes this helper or depends on the subagent implementation.Summary
Implement the first coherent slice of the unstable standard ACP subagent contract, independently of AIR's existing private contract.
Review corrections
Second review: lifecycle corrections
26 permanent lifecycle regressions cover these paths on both protocol versions, plus real-Codex terminal-lifetime tests. Existing AIR golden transcripts are unchanged. The terminal fixture uses full-access mode only for its fixed local script in an isolated workspace, avoiding CI hosts that cannot create sandbox network namespaces; production sandbox policy is unchanged.
CodeQL correction
Unexpected mock response-handler exceptions are logged server-side and return only a fixed HTTP 500 message. Regression tests cover synchronous exceptions, rejected stack-trace strings, and recovery on the next request. No production adapter behavior is changed.
Latest specification / SDK alignment
~2.0.2(lockfile resolves 2.0.2).session/set_config_option.idlebefore the response, with its actual success/cancellation/failure reason. Do not promote it into the foreground-state cache, runtime ownership, or mutation authorization. Missing or still-running history invents no outcome. Both legacy and paginated histories are covered; unavailable turn summaries do not discard available transcripts.Adds 22 reporting regressions across v1/v2, plus real-app-server assertions for initial/read-only child configuration and refresh on parent replay. The replayed-idle wording is currently on the upstream
rfd-replayed-subagent-outcomesbranch, not specmain; this historical-only reporting is compatible with both.Deliberate limits / next package
session_message/ directed-message reporting remains follow-up work; ordinary runtime operation reports remain available.npm auditreports 5 findings (3 high, 2 moderate) in unchanged dependencies. Unrelated dependency remediation is not included here.Validation
Local validation below is for merge commit
05d625c, which brings Codexmainata77829finto the feature branch without rewriting history. Dependency conflicts retain ACP SDK 2.0.2 alongside Codex 0.164.0 and the main lockfile's Vitest 5.0.5. Compatibility fixes only add the new nullable activity fields to typed feature-test fixtures; no additional production subagent behavior was changed. The PR itself has not been merged into main. CI passed for this merge commit, including typecheck, unit and integration tests, and binary bundling; both CodeQL workflows also passed.npm run typechecknpm test: 1,980 passed, 39 skippednpm run buildnpm run test:integration: 15 passed, including actual Codex 0.164.0 active-child replay, fork identity isolation, cancellation, close, and child terminal completion across independent parent prompts through both ACP versions with a local mock model; also verifies child configuration snapshots and replay refreshnpm run bundle:all: all six targets compilenpm run codex-test: live-model no-tool smoke test completed withend_turngit diff --checkIncludes 97 focused selection/wire/lifecycle tests (94 run; 3 v2-batch cases skipped on v1), four response-delivery tests, and a foreground-state regression, with golden transcripts for both protocol versions. Existing AIR snapshots are unchanged.
The parallel validation run also reproduced a test-discovery bug: Codex downloaded plugins containing tests into an integration fixture under
tmp/. Vitest now discovers only the repository-ownedsrc/__tests__/andscripts/suites, using its default filename patterns, instead of collecting generated third-party fixtures.References checked
mainat41c6a98caa3dea7d8e05a8013348e266dd4286e1: read-only child configuration and replay / current-state separation.e839564daab1da2c68cb8a006c786dfcbf0e68f1: historical idle outcomes may remain visible without proving current activity.mainatc7e07388feed7a530f8211d0dae4951cb6d26d09, alongside published 2.0.2 and real SDK wire decoding in tests. The specification is the protocol source of truth; the SDK is runtime evidence.739dde3ae5bb848bdb90e2c9150e2cc4687d480c: observed model / configuration reporting and historical outcomes / current-state handling. Used as implementation reference, not a normative contract or proof of Codex runtime ownership.