From 3b678b086663278195b436d12706c856eda43309 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 07:16:07 +0000 Subject: [PATCH 1/2] test(codex): native subagents report their child's command as progress Failing test for #25541: CodexAdapterV2 never sets progress on native subagent updates while the child runs commands. AI-assisted; verified with the tests and tools listed above. --- .../src/orchestration-v2/Adapters/CodexAdapterV2.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts index b4648fe578bc9..1d2a116584196 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts @@ -6185,6 +6185,12 @@ describe("CodexAdapterV2 post-settle continuation", () => { ); assert.equal(childCommandUpdates.at(-1)?.turnItem.status, "interrupted"); assert.equal(harness.subagentUpdates().at(-1)?.subagent.status, "interrupted"); + assert.isDefined( + harness + .subagentUpdates() + .find((event) => event.subagent.status === "running" && event.subagent.progress), + "a running native subagent reports its child's current command as progress", + ); assertChildProviderTerminalBeforeRoot(harness.events, harness.threadId); assert.isFalse(yield* harness.hasPendingBackgroundWork); }).pipe( From b1bdf96e8595808c844d4f45b3978cdf97a83135 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 12 Nov 2026 07:21:46 +0000 Subject: [PATCH 2/2] fix(server): Codex subagent rows show the child's current command CodexAdapterV2 never set progress on native subagent updates, so a spawn_agent child's row showed only Running and a timer. When a child starts a command or tool call, update the parent's subagent (and its turn item) with that line, as the Claude adapter does from task_progress. A reopened child drops its previous turn's line. AI-assisted; verified with the tests and tools listed above. --- .../Adapters/CodexAdapterV2.ts | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts index 5cfd93980a887..1f6252baa7113 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts @@ -2727,9 +2727,12 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( readonly completedAt?: DateTime.Utc | null; readonly startedAt?: DateTime.Utc; readonly reopen?: boolean; + readonly progress?: string; }) { const now = yield* DateTime.now; - const prior = input.subagent.task; + const prior = input.reopen + ? (({ progress: _staleProgress, ...rest }) => rest)(input.subagent.task) + : input.subagent.task; const settled = !isOrchestrationV2WorkActive(prior.status); // Late snapshots must not reopen completed children; only new turns may. if (settled && isOrchestrationV2WorkActive(input.status) && !input.reopen) return; @@ -2747,6 +2750,7 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( } : {}), startedAt: input.startedAt ?? prior.startedAt, + ...(input.progress === undefined ? {} : { progress: input.progress }), result: input.result === undefined ? input.subagent.task.result : input.result, completedAt, updatedAt: now, @@ -2784,11 +2788,28 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( providerInstanceId: task.providerInstanceId, childThreadId: task.childThreadId, prompt: task.prompt, + ...(task.progress === undefined ? {} : { progress: task.progress }), result: task.result, }, }); }); + // A native child's running item is the line its parent's subagent row shows. + const reportSubagentProgress = + /* Provider events include streaming deltas; avoid a span for each update. */ + Effect.fnUntraced(function* (context: ActiveCodexTurnContext, progress: string | null) { + const subagent = context.subagent; + const line = progress?.trim(); + if (subagent === null || !line) return; + if (!isOrchestrationV2WorkActive(subagent.task.status)) return; + if (subagent.task.progress === line) return; + yield* emitSubagentTaskUpdate({ + subagent, + status: subagent.task.status, + progress: line, + }); + }); + const emitSubagentProviderTurnStarted = /* Provider events include streaming deltas; avoid a span for each update. */ Effect.fnUntraced(function* ( @@ -5194,6 +5215,7 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( driver: CODEX_PROVIDER, turnItem: artifacts.turnItem, }); + yield* reportSubagentProgress(context, payload.item.command); return; } @@ -5212,6 +5234,12 @@ export const makeCodexAdapterV2 = Effect.fn("makeCodexAdapterV2")(function* ( driver: CODEX_PROVIDER, turnItem: artifacts.turnItem, }); + if (artifacts.turnItem.type === "dynamic_tool") { + yield* reportSubagentProgress( + context, + artifacts.turnItem.title ?? artifacts.turnItem.toolName, + ); + } return; }