Merge pull request #4032 from PeterPonyu/fix/3996-tool-pair-validator-background-sessions
fix(tool-pair-validator): skip placeholder repair for subagent sessions
This commit is contained in:
@@ -6,6 +6,7 @@ declare const expect: <T>(value: T) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
import { createToolPairValidatorHook } from "./hook"
|
import { createToolPairValidatorHook } from "./hook"
|
||||||
|
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)"
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ type TestPart = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TestMessage = {
|
type TestMessage = {
|
||||||
info: { role: "assistant" | "user" }
|
info: { role: "assistant" | "user"; sessionID?: string }
|
||||||
parts: TestPart[]
|
parts: TestPart[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,4 +154,46 @@ describe("createToolPairValidatorHook", () => {
|
|||||||
{ type: "text", text: "continue" },
|
{ type: "text", text: "continue" },
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("leaves tracked subagent sessions unchanged while normal sessions still repair", async () => {
|
||||||
|
//#given
|
||||||
|
_resetForTesting()
|
||||||
|
subagentSessions.add("ses_background_1")
|
||||||
|
const backgroundMessages = [
|
||||||
|
{
|
||||||
|
info: { role: "assistant", sessionID: "ses_background_1" },
|
||||||
|
parts: [{ type: "tool_use", id: "toolu_background_1" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
info: { role: "assistant", sessionID: "ses_background_1" },
|
||||||
|
parts: [{ type: "text", text: "background agent keeps reasoning" }],
|
||||||
|
},
|
||||||
|
] satisfies TestMessage[]
|
||||||
|
const originalBackgroundMessages = JSON.parse(JSON.stringify(backgroundMessages))
|
||||||
|
const mainMessages = [
|
||||||
|
{
|
||||||
|
info: { role: "assistant", sessionID: "ses_main_1" },
|
||||||
|
parts: [{ type: "tool_use", id: "toolu_main_1" }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
info: { role: "user", sessionID: "ses_main_1" },
|
||||||
|
parts: [{ type: "text", text: "continue main session" }],
|
||||||
|
},
|
||||||
|
] satisfies TestMessage[]
|
||||||
|
|
||||||
|
try {
|
||||||
|
//#when
|
||||||
|
await runTransform(backgroundMessages)
|
||||||
|
await runTransform(mainMessages)
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(backgroundMessages).toEqual(originalBackgroundMessages)
|
||||||
|
expect(mainMessages[1]?.parts).toEqual([
|
||||||
|
{ type: "tool_result", tool_use_id: "toolu_main_1", content: TOOL_RESULT_PLACEHOLDER },
|
||||||
|
{ type: "text", text: "continue main session" },
|
||||||
|
])
|
||||||
|
} finally {
|
||||||
|
_resetForTesting()
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { Message, Part } from "@opencode-ai/sdk"
|
import type { Message, Part } from "@opencode-ai/sdk"
|
||||||
|
|
||||||
|
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)"
|
||||||
@@ -134,6 +135,11 @@ function getMessageID(message: TransformMessageInfo): string | undefined {
|
|||||||
return typeof candidate.id === "string" ? candidate.id : undefined
|
return typeof candidate.id === "string" ? candidate.id : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessageSessionID(message: TransformMessageInfo): string | undefined {
|
||||||
|
const candidate = message as { sessionID?: unknown }
|
||||||
|
return typeof candidate.sessionID === "string" ? candidate.sessionID : undefined
|
||||||
|
}
|
||||||
|
|
||||||
function repairMissingToolResults(messages: MessageWithParts[], assistantIndex: number): void {
|
function repairMissingToolResults(messages: MessageWithParts[], assistantIndex: number): void {
|
||||||
const assistantMessage = messages[assistantIndex]
|
const assistantMessage = messages[assistantIndex]
|
||||||
const toolUseIDs = extractUniqueToolUseIDs(assistantMessage.parts)
|
const toolUseIDs = extractUniqueToolUseIDs(assistantMessage.parts)
|
||||||
@@ -173,7 +179,15 @@ export function createToolPairValidatorHook(): MessagesTransformHook {
|
|||||||
return {
|
return {
|
||||||
"experimental.chat.messages.transform": async (_input, output) => {
|
"experimental.chat.messages.transform": async (_input, output) => {
|
||||||
for (let i = 0; i < output.messages.length; i++) {
|
for (let i = 0; i < output.messages.length; i++) {
|
||||||
if (output.messages[i].info.role !== "assistant") {
|
const messageInfo = output.messages[i].info
|
||||||
|
|
||||||
|
if (messageInfo.role !== "assistant") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionID = getMessageSessionID(messageInfo)
|
||||||
|
if (sessionID && subagentSessions.has(sessionID)) {
|
||||||
|
log("[tool-pair-validator] Skipping repair for subagent session", { sessionID })
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user