mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:00:42 +00:00
fix: trust embedded anthropic tool results for signed replay
This commit is contained in:
@@ -555,6 +555,34 @@ describe("validateAnthropicTurns strips dangling tool_use blocks", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("preserves signed-thinking turns when the matching tool result is embedded in user content", () => {
|
||||
const msgs = asMessages([
|
||||
{ role: "user", content: [{ type: "text", text: "Use tool" }] },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "thinking", thinking: "internal", thinkingSignature: "sig_1" },
|
||||
{ type: "toolUse", id: "tool-1", name: "gateway", arguments: {} },
|
||||
],
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "toolResult", toolUseId: "tool-1", content: [{ type: "text", text: "ok" }] },
|
||||
{ type: "text", text: "Continue" },
|
||||
],
|
||||
},
|
||||
]);
|
||||
|
||||
const result = validateAnthropicTurns(msgs);
|
||||
|
||||
expect(result).toHaveLength(3);
|
||||
expect((result[1] as { content?: unknown[] }).content).toEqual([
|
||||
{ type: "thinking", thinking: "internal", thinkingSignature: "sig_1" },
|
||||
{ type: "toolUse", id: "tool-1", name: "gateway", arguments: {} },
|
||||
]);
|
||||
});
|
||||
|
||||
it("drops signed-thinking turns whose sibling tool calls are dangling", () => {
|
||||
const msgs = asMessages([
|
||||
{ role: "user", content: [{ type: "text", text: "Use tool" }] },
|
||||
|
||||
@@ -88,11 +88,13 @@ function collectTrustedToolResultMatches(message: AgentMessage): Map<string, Set
|
||||
const matches = new Map<string, Set<string>>();
|
||||
const role = (message as { role?: unknown }).role;
|
||||
const addMatch = (id: string | null, toolName: string | null) => {
|
||||
if (!id || !toolName) {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
const bucket = matches.get(id) ?? new Set<string>();
|
||||
bucket.add(toolName);
|
||||
if (toolName) {
|
||||
bucket.add(toolName);
|
||||
}
|
||||
matches.set(id, bucket);
|
||||
};
|
||||
|
||||
@@ -107,6 +109,22 @@ function collectTrustedToolResultMatches(message: AgentMessage): Map<string, Set
|
||||
addMatch(extractToolResultMatchId(record), extractToolResultMatchName(record));
|
||||
}
|
||||
|
||||
const content = (message as { content?: unknown }).content;
|
||||
if (!Array.isArray(content)) {
|
||||
return matches;
|
||||
}
|
||||
|
||||
for (const block of content) {
|
||||
if (!block || typeof block !== "object") {
|
||||
continue;
|
||||
}
|
||||
const record = block as Record<string, unknown>;
|
||||
if (record.type !== "toolResult" && record.type !== "tool") {
|
||||
continue;
|
||||
}
|
||||
addMatch(extractToolResultMatchId(record), extractToolResultMatchName(record));
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
@@ -198,9 +216,14 @@ function stripDanglingAnthropicToolUses(messages: AgentMessage[]): AgentMessage[
|
||||
}
|
||||
const blockId = normalizeOptionalString(block.id);
|
||||
const blockName = normalizeOptionalString(block.name);
|
||||
return blockId && blockName
|
||||
? validToolResultMatches.get(blockId)?.has(blockName) === true
|
||||
: false;
|
||||
if (!blockId || !blockName) {
|
||||
return false;
|
||||
}
|
||||
const matchingToolNames = validToolResultMatches.get(blockId);
|
||||
if (!matchingToolNames) {
|
||||
return false;
|
||||
}
|
||||
return matchingToolNames.size === 0 || matchingToolNames.has(blockName);
|
||||
});
|
||||
if (allToolCallsResolvable) {
|
||||
result.push(msg);
|
||||
|
||||
Reference in New Issue
Block a user