mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-31 11:51:22 +00:00
fix: repair replay tool result pairing
Regeneration-Prompt: | Address the PR review finding that replay sanitization can drop an assistant tool-call turn and leave downstream toolResult messages orphaned in the outbound provider context. Keep the replay-only sanitizer from the previous review fix, but when it changes the message list, immediately run sanitizeToolUseResultPairing before handing the context to the provider. Add a regression test that starts with a dropped malformed assistant tool-call turn followed by a toolResult and verifies the orphaned result is removed.
This commit is contained in:
@@ -878,6 +878,47 @@ describe("wrapStreamFnSanitizeMalformedToolCalls", () => {
|
||||
expect(toolCall.name).toBe("SESSIONS_SPAWN");
|
||||
expect(toolCall.input?.attachments?.[0]?.content).toBe(attachmentContent);
|
||||
});
|
||||
|
||||
it("drops orphaned tool results after replay sanitization removes a tool-call turn", async () => {
|
||||
const messages = [
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", name: "read", arguments: {} }],
|
||||
stopReason: "error",
|
||||
},
|
||||
{
|
||||
role: "toolResult",
|
||||
toolCallId: "call_missing",
|
||||
toolName: "read",
|
||||
content: [{ type: "text", text: "stale result" }],
|
||||
isError: false,
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "retry" }],
|
||||
},
|
||||
];
|
||||
const baseFn = vi.fn((_model, _context) =>
|
||||
createFakeStream({ events: [], resultMessage: { role: "assistant", content: [] } }),
|
||||
);
|
||||
|
||||
const wrapped = wrapStreamFnSanitizeMalformedToolCalls(baseFn as never, new Set(["read"]));
|
||||
const stream = wrapped({} as never, { messages } as never, {} as never) as
|
||||
| FakeWrappedStream
|
||||
| Promise<FakeWrappedStream>;
|
||||
await Promise.resolve(stream);
|
||||
|
||||
expect(baseFn).toHaveBeenCalledTimes(1);
|
||||
const seenContext = baseFn.mock.calls[0]?.[1] as {
|
||||
messages: Array<{ role?: string }>;
|
||||
};
|
||||
expect(seenContext.messages).toEqual([
|
||||
{
|
||||
role: "user",
|
||||
content: [{ type: "text", text: "retry" }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("wrapStreamFnRepairMalformedToolCallArguments", () => {
|
||||
|
||||
@@ -916,9 +916,10 @@ export function wrapStreamFnSanitizeMalformedToolCalls(
|
||||
if (sanitized === messages) {
|
||||
return baseFn(model, context, options);
|
||||
}
|
||||
const paired = sanitizeToolUseResultPairing(sanitized);
|
||||
const nextContext = {
|
||||
...(context as unknown as Record<string, unknown>),
|
||||
messages: sanitized,
|
||||
messages: paired,
|
||||
} as unknown;
|
||||
return baseFn(model, nextContext as typeof context, options);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user