diff --git a/src/agents/command/attempt-execution.helpers.ts b/src/agents/command/attempt-execution.helpers.ts index 784603f60cc..7c55f727f5b 100644 --- a/src/agents/command/attempt-execution.helpers.ts +++ b/src/agents/command/attempt-execution.helpers.ts @@ -185,13 +185,13 @@ async function jsonlFileHasOrphanedTrailingToolUse(filePath: string): Promise | undefined; const role = message?.role; - const blocks = toToolContentBlocks(message?.content); - if (!blocks) { - continue; - } if (role === "assistant") { lastAssistantToolUseIds = new Set(); answeredToolResultIds = new Set(); + const blocks = toToolContentBlocks(message?.content); + if (!blocks) { + continue; + } for (const block of blocks) { if (isClaudeTranscriptToolUseBlock(block)) { const id = resolveToolUseId(block); @@ -206,6 +206,10 @@ async function jsonlFileHasOrphanedTrailingToolUse(filePath: string): Promise { ).toBe(false); }); + it("returns false when a later string assistant message supersedes an old orphan", async () => { + await writeJsonlSession("buried-string", [ + { + type: "assistant", + message: { + role: "assistant", + content: [{ type: "tool_use", id: "toolu_old_string", name: "Bash", input: {} }], + }, + }, + { + type: "assistant", + message: { role: "assistant", content: "moving on" }, + }, + ]); + expect( + await claudeCliSessionTranscriptHasOrphanedToolUse({ + sessionId: "buried-string", + workspaceDir, + homeDir: tmpDir, + }), + ).toBe(false); + }); + it("rejects path-like session ids instead of escaping the Claude projects tree", async () => { await writeJsonlSession("safe", []); expect(