Summary
Add support for Codex subagents (CollabAgent system) in Harnss, bringing feature parity with how we already handle Claude subagents via the Task tool.
Codex CLI and the Codex Desktop app support multi-agent orchestration through collabAgentToolCall items — spawning parallel subagents, routing instructions between threads, waiting for results, and closing threads. Harnss already has the protocol types generated but doesn't process or render these events.
Background: How Codex Subagents Work
The Codex app-server protocol supports a CollabAgent system with five operations:
| Tool |
Purpose |
spawnAgent |
Creates a new subagent thread with a prompt |
sendInput |
Routes follow-up instructions to an active agent thread |
resumeAgent |
Reopens a paused/completed agent |
wait (wait_agent) |
Synchronizes — blocks until agent(s) finish |
closeAgent |
Terminates a finished agent thread |
Each collabAgentToolCall item includes:
senderThreadId — the parent thread that spawned/messaged the agent
receiverThreadIds — the target thread(s)
prompt — the instruction sent
agentsStates — a map of agent nicknames → { status, message } tracking lifecycle
status — overall call status (inProgress | completed | failed)
Subagent threads are identified by sourceKind: "subAgentThreadSpawn" and carry metadata like agent_nickname, agent_role, and depth via the SubAgentSource type.
Configuration (Codex CLI)
max_threads: Max concurrent open threads (default: 6)
max_depth: Nesting depth (default: 1 — child can't spawn grandchildren)
- Custom agent files define
name, description, developer_instructions + optional model/sandbox overrides
spawn_agents_on_csv: Batch fan-out for processing rows in parallel
What We Already Have
Protocol types (fully generated)
shared/types/codex-protocol/v2/ThreadItem.ts — collabAgentToolCall item type
shared/types/codex-protocol/v2/CollabAgentTool.ts — "spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent"
shared/types/codex-protocol/v2/CollabAgentStatus.ts — "pendingInit" | "running" | "completed" | "errored" | "shutdown" | "notFound"
shared/types/codex-protocol/v2/CollabAgentState.ts — { status, message }
shared/types/codex-protocol/v2/CollabAgentToolCallStatus.ts — "inProgress" | "completed" | "failed"
shared/types/codex-protocol/SubAgentSource.ts — spawn metadata (parent_thread_id, depth, agent_nickname, agent_role)
shared/types/codex-protocol/CollabAgentSpawnBeginEvent.ts / CollabAgentSpawnEndEvent.ts
Claude subagent infrastructure (reference implementation)
parentToolMap (Map<parentToolId, msgId>) for routing child events to parent Task messages
SubagentToolStep type with toolName, toolInput, toolResult, toolUseId, toolError
handleSubagentEvent() in useClaude.ts — processes assistant + tool_result events from subagents
background-claude-handler.ts — mirrors subagent routing for background sessions
TaskTool.tsx — rich collapsible UI card with live step count, duration, prompt, and step list
Shared infrastructure
BackgroundSessionStore.InternalState.parentToolMap — already shared across all engines
codex-adapter.ts — translates Codex items to UIMessage tool calls (extensible)
What Needs to Be Built
1. Event Handling in useCodex.ts
Handle collabAgentToolCall items in the Codex event loop:
-
item/started with type: "collabAgentToolCall":
- Create a
tool_call UIMessage for the collab operation
- Register
itemId → msgId in parentToolMap for spawnAgent calls
- Track agent nicknames and lifecycle states from
agentsStates
-
item/completed with type: "collabAgentToolCall":
- Finalize the tool message with result/status
- For
spawnAgent: mark with subagentStatus: "completed" when the spawned thread finishes
- Update
agentsStates to reflect final CollabAgentStatus
-
Subagent thread events: Items from spawned threads should be routed to the parent spawnAgent message as subagentSteps[], similar to how Claude routes via parent_tool_use_id
2. Background Handler (background-codex-handler.ts)
Mirror the active session handling for background sessions:
- Process
collabAgentToolCall items in handleCodexBackgroundEvent()
- Maintain
parentToolMap entries for spawn operations
- Accumulate
subagentSteps on the parent message
3. Codex Adapter (codex-adapter.ts)
Add collabAgentToolCall → UIMessage mapping:
- Map tool operations to display names (e.g.,
spawnAgent → "Spawn Agent", wait → "Wait for Agent")
- Extract prompt, agent nicknames, and states for tool input display
- Format final
agentsStates map as the tool result
4. UI Rendering
Create a CodexSubagentTool renderer (or extend TaskTool.tsx) to display:
- Agent nickname and role
- Operation type (
spawnAgent / sendInput / wait / closeAgent)
- Live agent status badges (
pendingInit → running → completed / errored)
- Nested subagent steps (reusing
SubagentStepRow pattern)
- Multi-agent state dashboard when multiple agents are tracked in
agentsStates
- Prompt text (collapsible for long prompts)
- Duration tracking
5. Sidebar / Session Indicators
- Show active subagent count on Codex sessions in the sidebar
- Visual indicator when subagents are running (spinner/badge)
Implementation Notes
- The
collabAgentToolCall item has a different shape from Claude's parent_tool_use_id routing — Codex uses explicit senderThreadId / receiverThreadIds instead. The adapter layer needs to bridge this to the existing parentToolMap pattern.
- Multiple agents can run in parallel (up to
max_threads: 6), so the UI should handle concurrent subagent cards gracefully.
- Agent nicknames (from
SubAgentSource.thread_spawn.agent_nickname) should be displayed prominently — Codex CLI uses these instead of opaque thread IDs.
- The
wait tool is a synchronization primitive — it should render as a "Waiting for agents..." state rather than a tool execution.
References
Summary
Add support for Codex subagents (CollabAgent system) in Harnss, bringing feature parity with how we already handle Claude subagents via the Task tool.
Codex CLI and the Codex Desktop app support multi-agent orchestration through
collabAgentToolCallitems — spawning parallel subagents, routing instructions between threads, waiting for results, and closing threads. Harnss already has the protocol types generated but doesn't process or render these events.Background: How Codex Subagents Work
The Codex app-server protocol supports a CollabAgent system with five operations:
spawnAgentsendInputresumeAgentwait(wait_agent)closeAgentEach
collabAgentToolCallitem includes:senderThreadId— the parent thread that spawned/messaged the agentreceiverThreadIds— the target thread(s)prompt— the instruction sentagentsStates— a map of agent nicknames →{ status, message }tracking lifecyclestatus— overall call status (inProgress|completed|failed)Subagent threads are identified by
sourceKind: "subAgentThreadSpawn"and carry metadata likeagent_nickname,agent_role, anddepthvia theSubAgentSourcetype.Configuration (Codex CLI)
max_threads: Max concurrent open threads (default: 6)max_depth: Nesting depth (default: 1 — child can't spawn grandchildren)name,description,developer_instructions+ optional model/sandbox overridesspawn_agents_on_csv: Batch fan-out for processing rows in parallelWhat We Already Have
Protocol types (fully generated)
shared/types/codex-protocol/v2/ThreadItem.ts—collabAgentToolCallitem typeshared/types/codex-protocol/v2/CollabAgentTool.ts—"spawnAgent" | "sendInput" | "resumeAgent" | "wait" | "closeAgent"shared/types/codex-protocol/v2/CollabAgentStatus.ts—"pendingInit" | "running" | "completed" | "errored" | "shutdown" | "notFound"shared/types/codex-protocol/v2/CollabAgentState.ts—{ status, message }shared/types/codex-protocol/v2/CollabAgentToolCallStatus.ts—"inProgress" | "completed" | "failed"shared/types/codex-protocol/SubAgentSource.ts— spawn metadata (parent_thread_id,depth,agent_nickname,agent_role)shared/types/codex-protocol/CollabAgentSpawnBeginEvent.ts/CollabAgentSpawnEndEvent.tsClaude subagent infrastructure (reference implementation)
parentToolMap(Map<parentToolId, msgId>) for routing child events to parent Task messagesSubagentToolSteptype withtoolName,toolInput,toolResult,toolUseId,toolErrorhandleSubagentEvent()inuseClaude.ts— processes assistant + tool_result events from subagentsbackground-claude-handler.ts— mirrors subagent routing for background sessionsTaskTool.tsx— rich collapsible UI card with live step count, duration, prompt, and step listShared infrastructure
BackgroundSessionStore.InternalState.parentToolMap— already shared across all enginescodex-adapter.ts— translates Codex items to UIMessage tool calls (extensible)What Needs to Be Built
1. Event Handling in
useCodex.tsHandle
collabAgentToolCallitems in the Codex event loop:item/startedwithtype: "collabAgentToolCall":tool_callUIMessage for the collab operationitemId → msgIdinparentToolMapforspawnAgentcallsagentsStatesitem/completedwithtype: "collabAgentToolCall":spawnAgent: mark withsubagentStatus: "completed"when the spawned thread finishesagentsStatesto reflect finalCollabAgentStatusSubagent thread events: Items from spawned threads should be routed to the parent
spawnAgentmessage assubagentSteps[], similar to how Claude routes viaparent_tool_use_id2. Background Handler (
background-codex-handler.ts)Mirror the active session handling for background sessions:
collabAgentToolCallitems inhandleCodexBackgroundEvent()parentToolMapentries for spawn operationssubagentStepson the parent message3. Codex Adapter (
codex-adapter.ts)Add
collabAgentToolCall→ UIMessage mapping:spawnAgent→ "Spawn Agent",wait→ "Wait for Agent")agentsStatesmap as the tool result4. UI Rendering
Create a
CodexSubagentToolrenderer (or extendTaskTool.tsx) to display:spawnAgent/sendInput/wait/closeAgent)pendingInit→running→completed/errored)SubagentStepRowpattern)agentsStates5. Sidebar / Session Indicators
Implementation Notes
collabAgentToolCallitem has a different shape from Claude'sparent_tool_use_idrouting — Codex uses explicitsenderThreadId/receiverThreadIdsinstead. The adapter layer needs to bridge this to the existingparentToolMappattern.max_threads: 6), so the UI should handle concurrent subagent cards gracefully.SubAgentSource.thread_spawn.agent_nickname) should be displayed prominently — Codex CLI uses these instead of opaque thread IDs.waittool is a synchronization primitive — it should render as a "Waiting for agents..." state rather than a tool execution.References