Skip to content

Promote background codex exec runs into subagent rows - #158

Merged
badcuban merged 1 commit into
mainfrom
codex-exec-subagent-rows
Aug 15, 2026
Merged

Promote background codex exec runs into subagent rows#158
badcuban merged 1 commit into
mainfrom
codex-exec-subagent-rows

Conversation

@badcuban

Copy link
Copy Markdown
Collaborator

When a Claude thread launches codex exec as a background Bash command, it is a second agent, but the agents panel showed it as a bare background-run row: no model, no reasoning effort, nothing to click into. This promotes it to the same rich subagent row that native codex subagents get.

How it works

  • Detection (server): on the task_started edge for a local_bash background task, the originating Bash command is matched against a strict, quote-aware codex exec matcher (Drivers/CodexExecRollouts.ts). Commands that merely mention codex exec (echoed strings, other programs' arguments) never match; a false positive would demote real shell runs, so the matcher is biased toward false negatives. npx codex exec is deliberately unmatched for now.
  • Promotion: the row is emitted immediately via the existing subagent.metadata.updated event (keyed by the spawning tool call id), so it appears the moment the task starts. Explicit -m/--model and -c model_reasoning_effort=… flags seed provisional model/effort.
  • Rollout correlation: a scoped poll (1s..30s backoff) finds the rollout file the run writes under $CODEX_HOME/sessions/, confirmed by originator, realpath-equal cwd, and the recorded session start time. Mtime alone is not enough: a still-running older rollout keeps a fresh mtime and would steal the match, so candidates that began before the task did are rejected. Once found, the row gains the codex session identity plus the authoritative model and effort from turn_context.
  • Settle: the row settles with the single task completion (completed / failed / interrupted), carrying codex's own final message as its result line — including when the rollout was never found.
  • Transcript: promoted rows use codex-exec:<session-uuid> agent ids. The Claude adapter's transcript route resolves the rollout from the id alone (so it survives server restarts) and maps it into the standard transcript entries: prompt, reasoning, tool calls with output previews, final message.
  • Web: promoted tasks move out of the run list (with hidden-count accounting) into a promotedSubagentRuns map; the matching subagent row borrows the run's stop arm while live, wired through the existing stopBackgroundRun path. Model/effort meta, history, receipts, and transcript drill-in all ride the existing subagent surfaces.

Every codex-exec path is guarded so any failure degrades to today's behavior (a plain run row) rather than disturbing task handling.

Testing

  • vp fmt, vp lint, vp run typecheck
  • Server: CodexExecRollouts (matcher table, correlation incl. stale-rollout rejection and claimed-file exclusivity, transcript mapping), ClaudeAdapter promotion arc (110 tests), projector durable-identity coverage
  • Web: ChatView.logic suppression, agentsPanel stop-arm attachment, full browser suite (469 tests)

Not yet exercised live end-to-end against a real codex exec run from a real thread; that is the next step after merge-readiness review.

A Claude thread that launches `codex exec …` as a background Bash command
is running a second agent, but the SDK only reports it as a `local_bash`
task, so the agents panel showed a bare run row: no model, no effort, no
transcript, nothing to click into.

The Claude adapter now recognises a `codex exec` invocation from the
originating Bash tool call and emits the same `subagent.metadata.updated`
events the Codex adapter emits for native subagents. The row appears on
the task's start edge with the role, the task description as its goal, and
any `-m` / `-c model_reasoning_effort=` the command stated. A scoped poll
then correlates the run with the rollout JSONL codex writes under
`$CODEX_HOME/sessions/`, matching on the exec originator and a realpath-equal
cwd, and upgrades the row with the transcript id, the resolved model and
the effort from the file's first `turn_context`. The task's completion
settles the row, with codex's own final message as the result.

`readSubagentTranscript` serves those rollouts for `codex-exec:<uuid>` ids,
resolving from the id alone so a transcript still opens after a restart.

Web-side, a task whose tool call the thread already tracks as a subagent is
kept off the run list, and the run it would have been is handed to the
matching agent row so the row keeps the same stop arm the run row had.

New payload keys on `SubagentMetadataUpdatedPayload` (status,
transcriptAgentId, resolvedModel, resultBody, resultCreatedAt) were already
read by the subagent projection; they are now declared so emitters can set
them.
@vercel

vercel Bot commented Aug 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
threadlines Skipped Skipped Aug 15, 2026 4:42am

Request Review

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Aug 15, 2026
@badcuban
badcuban merged commit 41a8a6b into main Aug 15, 2026
15 checks passed
@badcuban
badcuban deleted the codex-exec-subagent-rows branch August 15, 2026 04:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant