diff --git a/src/agents/subagent-announce-output.ts b/src/agents/subagent-announce-output.ts index b40b105cfb2..02e13cb0cdd 100644 --- a/src/agents/subagent-announce-output.ts +++ b/src/agents/subagent-announce-output.ts @@ -58,6 +58,9 @@ export type AgentWaitResult = { export type SubagentRunOutcome = { status: "ok" | "error" | "timeout" | "unknown"; error?: string; + startedAt?: number; + endedAt?: number; + elapsedMs?: number; }; function extractToolResultText(content: unknown): string { @@ -297,20 +300,30 @@ export function applySubagentWaitOutcome(params: { startedAt: params.startedAt, endedAt: params.endedAt, }; - const waitError = typeof params.wait?.error === "string" ? params.wait.error : undefined; - if (params.wait?.status === "timeout") { - next.outcome = { status: "timeout" }; - } else if (params.wait?.status === "error") { - next.outcome = { status: "error", error: waitError }; - } else if (params.wait?.status === "ok") { - next.outcome = { status: "ok" }; - } if (typeof params.wait?.startedAt === "number" && !next.startedAt) { next.startedAt = params.wait.startedAt; } if (typeof params.wait?.endedAt === "number" && !next.endedAt) { next.endedAt = params.wait.endedAt; } + const timing: Pick = {}; + if (typeof next.startedAt === "number") { + timing.startedAt = next.startedAt; + } + if (typeof next.endedAt === "number") { + timing.endedAt = next.endedAt; + } + if (typeof next.startedAt === "number" && typeof next.endedAt === "number") { + timing.elapsedMs = Math.max(0, next.endedAt - next.startedAt); + } + const waitError = typeof params.wait?.error === "string" ? params.wait.error : undefined; + if (params.wait?.status === "timeout") { + next.outcome = { status: "timeout", ...timing }; + } else if (params.wait?.status === "error") { + next.outcome = { status: "error", error: waitError, ...timing }; + } else if (params.wait?.status === "ok") { + next.outcome = { status: "ok", ...timing }; + } return next; } diff --git a/src/agents/tools/sessions-spawn-tool.ts b/src/agents/tools/sessions-spawn-tool.ts index fa05597c721..4fcc0359cad 100644 --- a/src/agents/tools/sessions-spawn-tool.ts +++ b/src/agents/tools/sessions-spawn-tool.ts @@ -201,10 +201,13 @@ export function createSessionsSpawnTool( }>) : undefined; + const roleContext = requestedAgentId ? { role: requestedAgentId } : {}; + if (streamTo && runtime !== "acp") { return jsonResult({ status: "error", error: `streamTo is only supported for runtime=acp; got runtime=${runtime}`, + ...roleContext, }); } @@ -212,6 +215,7 @@ export function createSessionsSpawnTool( return jsonResult({ status: "error", error: `resumeSessionId is only supported for runtime=acp; got runtime=${runtime}`, + ...roleContext, }); } @@ -222,6 +226,7 @@ export function createSessionsSpawnTool( status: "error", error: "attachments are currently unsupported for runtime=acp; use runtime=subagent or remove attachments", + ...roleContext, }); } const result = await spawnAcpDirect( @@ -304,10 +309,15 @@ export function createSessionsSpawnTool( error: `Failed to register ACP run: ${summarizeError(err)}. Cleanup was attempted, but the already-started ACP run may still finish in the background.`, childSessionKey, runId: childRunId, + ...roleContext, }); } } - return jsonResult(result); + return jsonResult( + result.status === "error" && requestedAgentId + ? { ...result, role: requestedAgentId } + : result, + ); } const result = await spawnSubagentDirect( @@ -345,7 +355,11 @@ export function createSessionsSpawnTool( }, ); - return jsonResult(result); + return jsonResult( + result.status === "error" && requestedAgentId + ? { ...result, role: requestedAgentId } + : result, + ); }, }; }