Repository navigation
feat(codex): surface nested subagents - #9637
brennanb2025 merged 9 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Codex subagent start/stop event support, explicit per-pane subagent rosters, lifecycle normalization, and persisted roster hydration. Status payloads and entries now preserve model metadata and expanded waiting/blocked states, while root provider sessions remain stable during child transitions. Renderer agent rows display model names and preserve derived child states across dashboard and sidebar views. Tests cover normalization, persistence, hook management, store behavior, and rendering. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/agent-hooks/server.ts (1)
552-573: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winSynchronize Codex lead state on inferred interrupts.
Only Claude’s lead cache is updated before synthesizing
done. A later Codex child hook reconciles with its still-workinglead cache and can resurrect the interrupted pane. Set the Codex lead state todonehere while retaining its cached model; add a regression test with a delayedSubagentStop.Proposed fix
if (agentType === 'claude') { markClaudeLeadTurnInterrupted(this.state, existing.paneKey) } + if (agentType === 'codex') { + const lead = this.state.codexLeadStateByPaneKey.get(existing.paneKey) + this.state.codexLeadStateByPaneKey.set(existing.paneKey, { + state: 'done', + model: payload.model ?? lead?.model + }) + }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 5b64cb3f-ab99-46b4-a55a-38bb1c415d65
📒 Files selected for processing (4)
src/main/agent-hooks/server.test.tssrc/main/agent-hooks/server.tssrc/shared/agent-hook-listener.tssrc/shared/codex-subagent-roster.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- src/shared/codex-subagent-roster.test.ts
- src/main/agent-hooks/server.test.ts
- src/shared/agent-hook-listener.ts
…surface-codex-sub-agents-models-and-orch # Conflicts: # resources/skills/release-mapping.json
…8251) (#14627) Codex child rows have carried a model field end-to-end since #9637, but the transcript reader never populated it, so every transcript-discovered child rendered with an empty model chip. Read the child's own turn_context.model from the rollout records already fetched for completion detection, so the sidebar can distinguish an orchestrator model from a subagent model. No added file I/O and no added rows: the model is parsed from records the reconcile pass already read, and both row components already render entry.model.
…tablyai#8251) (stablyai#14627) Codex child rows have carried a model field end-to-end since stablyai#9637, but the transcript reader never populated it, so every transcript-discovered child rendered with an empty model chip. Read the child's own turn_context.model from the rollout records already fetched for completion detection, so the sidebar can distinguish an orchestrator model from a subagent model. No added file I/O and no added rows: the model is parsed from records the reconcile pass already read, and both row components already render entry.model.
…tablyai#8251) (stablyai#14627) Codex child rows have carried a model field end-to-end since stablyai#9637, but the transcript reader never populated it, so every transcript-discovered child rendered with an empty model chip. Read the child's own turn_context.model from the rollout records already fetched for completion detection, so the sidebar can distinguish an orchestrator model from a subagent model. No added file I/O and no added rows: the model is parsed from records the reconcile pass already read, and both row components already render entry.model.
* feat(codex): surface nested subagents * fix(codex): retire child rows on root stop * fix(codex): preserve nested agent state on restart * fix(codex): preserve nested agent identity * fix(codex): preserve subagents across relay restarts * chore(skills): refresh generated manifests * fix(codex): keep inferred interrupts terminal * chore(skills): record latest release snapshots
…tablyai#8251) (stablyai#14627) Codex child rows have carried a model field end-to-end since stablyai#9637, but the transcript reader never populated it, so every transcript-discovered child rendered with an empty model chip. Read the child's own turn_context.model from the rollout records already fetched for completion detection, so the sidebar can distinguish an orchestrator model from a subagent model. No added file I/O and no added rows: the model is parsed from records the reconcile pass already read, and both row components already render entry.model.
Summary
Scope
This PR covers Codex CLI's in-process subagent visibility. The cross-worktree coordinator → worker graph and deduplication of workers visible through both Codex threads and Orca terminals remain follow-up work for #8251, so this PR does not close the umbrella issue.
Refs #8251
Current Codex CLI verification
Validated with two real, persisted-session Codex CLI 0.145.0 probes using
gpt-5.6-sol: one child completed without tools, and one child ran a read-only shell tool before completing. The tool-using flow emitted:SubagentStart→ childPreToolUse→ childPostToolUse→ matchingSubagentStop→ rootStopThe
SubagentStopcarried the same childagent_idasSubagentStart. This verifies that the newest Codex CLI supplies the native lifecycle boundary Orca uses to remove the completed child promptly; rootStopremains a defensive compatibility cleanup path rather than the expected 0.145.0 path.Earlier Codex CLI 0.144.6 end-to-end verification
A real Codex CLI 0.144.6 session (
gpt-5.6-sol high) was also launched inside an isolated Orca dev instance from this worktree. The root spawned a reallifecycle_proofchild, waited for its read-only task, and returned normally.Live child working
The child tool hook creates a nested working row under the root while preserving the root model.
Compatibility lifecycle cleanup
The captured 0.144.6 flow did not emit a child
SubagentStop. Orca used the rootStopas a compatibility cleanup boundary, while later child activity can safely recreate a child that is still running. The completed root remained visible withgpt-5.6-soland no stale child row.Synthetic lifecycle matrix
The loopback Codex hook endpoint was also exercised directly for deterministic coverage of two concurrent children, per-child permission waiting, explicit child-stop ordering, model preservation, resume-session preservation, restart hydration, SSH relay context loss, and delayed child events after an inferred interrupt.
Working child roster
Both child roles and their distinct models render under the Codex root.
Child waiting for permission
A child PermissionRequest moves that child and the root to waiting while the sibling remains working. The root resume session remained
qa-root-sessionin backing state.Explicit lifecycle cleanup
Explicit child Stop events remove child rows incrementally, then the root resolves to Done with its model and resume session intact.
Verification
SubagentStart/SubagentStopidentitiesgit diff --check