From e130c1acbf8dd399e7490162ce304b290ff3d73d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 29 May 2026 09:40:23 +0100 Subject: [PATCH] fix(agents): log cli subprocess turn digests --- src/agents/cli-runner.spawn.test.ts | 63 +++++++++++++++++------------ src/agents/cli-runner/execute.ts | 5 +++ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/agents/cli-runner.spawn.test.ts b/src/agents/cli-runner.spawn.test.ts index 8b10d44c4af..9ad0ef372f6 100644 --- a/src/agents/cli-runner.spawn.test.ts +++ b/src/agents/cli-runner.spawn.test.ts @@ -686,6 +686,7 @@ describe("runCliAgent spawn path", () => { }); it("runs CLI through supervisor and returns payload", async () => { + const logInfoSpy = vi.spyOn(cliBackendLog, "info").mockImplementation(() => undefined); supervisorSpawnMock.mockResolvedValueOnce( createManagedRun({ reason: "exit", @@ -706,32 +707,44 @@ describe("runCliAgent spawn path", () => { }); context.reusableCliSession = { sessionId: "thread-123" }; - const result = await executePreparedCliRun(context, "thread-123"); + try { + const result = await executePreparedCliRun(context, "thread-123"); - expect(result.text).toBe("ok"); - const input = mockCallArg(supervisorSpawnMock) as { - argv?: string[]; - mode?: string; - timeoutMs?: number; - noOutputTimeoutMs?: number; - replaceExistingScope?: boolean; - scopeKey?: string; - }; - expect(input.mode).toBe("child"); - expect(input.argv).toEqual([ - "codex", - "exec", - "resume", - "thread-123", - "--skip-git-repo-check", - "--model", - "gpt-5.4", - "hi", - ]); - expect(input.timeoutMs).toBe(1_000); - expect(input.noOutputTimeoutMs).toBeGreaterThanOrEqual(1_000); - expect(input.replaceExistingScope).toBe(true); - expect(input.scopeKey).toContain("thread-123"); + expect(result.text).toBe("ok"); + const input = mockCallArg(supervisorSpawnMock) as { + argv?: string[]; + mode?: string; + timeoutMs?: number; + noOutputTimeoutMs?: number; + replaceExistingScope?: boolean; + scopeKey?: string; + }; + expect(input.mode).toBe("child"); + expect(input.argv).toEqual([ + "codex", + "exec", + "resume", + "thread-123", + "--skip-git-repo-check", + "--model", + "gpt-5.4", + "hi", + ]); + expect(input.timeoutMs).toBe(1_000); + expect(input.noOutputTimeoutMs).toBeGreaterThanOrEqual(1_000); + expect(input.replaceExistingScope).toBe(true); + expect(input.scopeKey).toContain("thread-123"); + + const turnLog = logInfoSpy.mock.calls + .map(([message]) => message) + .find((message) => message.startsWith("cli turn:")); + expect(turnLog).toContain("provider=codex-cli"); + expect(turnLog).toContain("model=gpt-5.4"); + expect(turnLog).toContain("outBytes=2 outHash=2689367b205c"); + expect(turnLog).not.toContain("ok"); + } finally { + logInfoSpy.mockRestore(); + } }); it("passes Codex system prompts through model_instructions_file", async () => { diff --git a/src/agents/cli-runner/execute.ts b/src/agents/cli-runner/execute.ts index 47449ce5156..97afc0897ac 100644 --- a/src/agents/cli-runner/execute.ts +++ b/src/agents/cli-runner/execute.ts @@ -40,6 +40,7 @@ import { import { cliBackendLog, CLI_BACKEND_LOG_OUTPUT_ENV, + formatCliBackendOutputDigest, LEGACY_CLAUDE_CLI_LOG_OUTPUT_ENV, } from "./log.js"; import type { PreparedCliRunContext } from "./types.js"; @@ -368,6 +369,7 @@ export async function executePreparedCliRun( try { return await enqueueCliRun(queueKey, async () => { + const cliTurnStartedAt = Date.now(); const restoreSkillEnv = params.skillsSnapshot ? applySkillEnvOverridesFromSnapshot({ snapshot: params.skillsSnapshot, @@ -781,6 +783,9 @@ export async function executePreparedCliRun( fallbackSessionId: resolvedSessionId, }); const rawText = parsed.text; + cliBackendLog.info( + `cli turn: provider=${params.provider} model=${context.modelId} durationMs=${Date.now() - cliTurnStartedAt} ${formatCliBackendOutputDigest(rawText)}`, + ); return { ...parsed, rawText,