fix(tool-pair-validator): continue after synthetic repairs

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
SpencerJung
2026-05-22 16:36:12 +09:00
parent cb205e1479
commit 28569307eb
3 changed files with 31 additions and 2 deletions
@@ -9,6 +9,7 @@ import { createToolPairValidatorHook } from "./hook"
import { _resetForTesting, subagentSessions } from "../../features/claude-code-session-state/state" import { _resetForTesting, subagentSessions } from "../../features/claude-code-session-state/state"
const TOOL_RESULT_PLACEHOLDER = "Tool output unavailable (context compacted)" const TOOL_RESULT_PLACEHOLDER = "Tool output unavailable (context compacted)"
const TOOL_RESULT_RECOVERY_CONTINUATION = "Recovered missing tool results. Continue from the repaired tool output."
type TestPart = { type TestPart = {
type: string type: string
@@ -19,6 +20,7 @@ type TestPart = {
isError?: boolean isError?: boolean
content?: string | Array<{ type: "text"; text: string }> content?: string | Array<{ type: "text"; text: string }>
text?: string text?: string
synthetic?: boolean
} }
type TestMessage = { type TestMessage = {
@@ -121,6 +123,11 @@ describe("createToolPairValidatorHook", () => {
isError: true, isError: true,
content: [{ type: "text", text: TOOL_RESULT_PLACEHOLDER }], content: [{ type: "text", text: TOOL_RESULT_PLACEHOLDER }],
}, },
{
type: "text",
text: TOOL_RESULT_RECOVERY_CONTINUATION,
synthetic: true,
},
], ],
}, },
]) ])
@@ -148,6 +155,10 @@ describe("createToolPairValidatorHook", () => {
tool_use_id: "toolu_1", tool_use_id: "toolu_1",
isError: true, isError: true,
content: [{ type: "text", text: TOOL_RESULT_PLACEHOLDER }], content: [{ type: "text", text: TOOL_RESULT_PLACEHOLDER }],
}, {
type: "text",
text: TOOL_RESULT_RECOVERY_CONTINUATION,
synthetic: true,
}], }],
}, },
{ info: { role: "assistant" }, parts: [{ type: "text", text: "follow-up" }] }, { info: { role: "assistant" }, parts: [{ type: "text", text: "follow-up" }] },
+16 -2
View File
@@ -4,6 +4,7 @@ import { subagentSessions } from "../../features/claude-code-session-state"
import { log } from "../../shared/logger" import { log } from "../../shared/logger"
const TOOL_RESULT_PLACEHOLDER = "Tool output unavailable (context compacted)" const TOOL_RESULT_PLACEHOLDER = "Tool output unavailable (context compacted)"
const TOOL_RESULT_RECOVERY_CONTINUATION = "Recovered missing tool results. Continue from the repaired tool output."
type ToolUsePart = { type ToolUsePart = {
type: "tool_use" type: "tool_use"
@@ -20,7 +21,13 @@ type ToolResultPart = {
[key: string]: unknown [key: string]: unknown
} }
type TransformPart = Part | ToolUsePart | ToolResultPart type TextPart = {
type: "text"
text: string
synthetic: true
}
type TransformPart = Part | ToolUsePart | ToolResultPart | TextPart
type TransformMessageInfo = Message | { type TransformMessageInfo = Message | {
role: "user" role: "user"
@@ -138,7 +145,14 @@ function createSyntheticUserMessage(assistantMessage: MessageWithParts, missingT
role: "user", role: "user",
...(sessionID ? { sessionID } : {}), ...(sessionID ? { sessionID } : {}),
}, },
parts: missingToolUseIDs.map((toolUseID) => createToolResultPart(toolUseID)), parts: [
...missingToolUseIDs.map((toolUseID) => createToolResultPart(toolUseID)),
{
type: "text",
text: TOOL_RESULT_RECOVERY_CONTINUATION,
synthetic: true,
},
],
} }
} }
+4
View File
@@ -157,6 +157,10 @@ describe("createMessagesTransformHandler", () => {
tool_use_id: "toolu_01SRMQs3DUtVKWoSxC8bxxVA", tool_use_id: "toolu_01SRMQs3DUtVKWoSxC8bxxVA",
isError: true, isError: true,
content: [{ type: "text", text: "Tool output unavailable (context compacted)" }], content: [{ type: "text", text: "Tool output unavailable (context compacted)" }],
}, {
type: "text",
text: "Recovered missing tool results. Continue from the repaired tool output.",
synthetic: true,
}], }],
}) })
expect(messages[4]?.parts[0]).toEqual({ expect(messages[4]?.parts[0]).toEqual({