mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 00:40:23 +00:00
fix: tighten mutating error retry matching
This commit is contained in:
@@ -169,8 +169,13 @@ function sameToolAction(
|
||||
meta?: string,
|
||||
actionFingerprint?: string,
|
||||
): boolean {
|
||||
if (existing.actionFingerprint && actionFingerprint) {
|
||||
return existing.actionFingerprint === actionFingerprint;
|
||||
if (existing.actionFingerprint != null || actionFingerprint != null) {
|
||||
// For mutating flows, fail closed: only clear when both fingerprints exist and match.
|
||||
return (
|
||||
existing.actionFingerprint != null &&
|
||||
actionFingerprint != null &&
|
||||
existing.actionFingerprint === actionFingerprint
|
||||
);
|
||||
}
|
||||
return existing.toolName === toolName && (existing.meta ?? "") === (meta ?? "");
|
||||
}
|
||||
|
||||
@@ -412,6 +412,98 @@ describe("subscribeEmbeddedPiSession", () => {
|
||||
expect(subscription.getLastToolError()).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps unresolved mutating failure when same tool succeeds on a different target", () => {
|
||||
let handler: ((evt: unknown) => void) | undefined;
|
||||
const session: StubSession = {
|
||||
subscribe: (fn) => {
|
||||
handler = fn;
|
||||
return () => {};
|
||||
},
|
||||
};
|
||||
|
||||
const subscription = subscribeEmbeddedPiSession({
|
||||
session: session as unknown as Parameters<typeof subscribeEmbeddedPiSession>[0]["session"],
|
||||
runId: "run-tools-3",
|
||||
sessionKey: "test-session",
|
||||
});
|
||||
|
||||
handler?.({
|
||||
type: "tool_execution_start",
|
||||
toolName: "write",
|
||||
toolCallId: "w1",
|
||||
args: { path: "/tmp/a.txt", content: "first" },
|
||||
});
|
||||
handler?.({
|
||||
type: "tool_execution_end",
|
||||
toolName: "write",
|
||||
toolCallId: "w1",
|
||||
isError: true,
|
||||
result: { error: "disk full" },
|
||||
});
|
||||
|
||||
handler?.({
|
||||
type: "tool_execution_start",
|
||||
toolName: "write",
|
||||
toolCallId: "w2",
|
||||
args: { path: "/tmp/b.txt", content: "second" },
|
||||
});
|
||||
handler?.({
|
||||
type: "tool_execution_end",
|
||||
toolName: "write",
|
||||
toolCallId: "w2",
|
||||
isError: false,
|
||||
result: { ok: true },
|
||||
});
|
||||
|
||||
expect(subscription.getLastToolError()?.toolName).toBe("write");
|
||||
});
|
||||
|
||||
it("keeps unresolved session_status model-mutation failure on later read-only status success", () => {
|
||||
let handler: ((evt: unknown) => void) | undefined;
|
||||
const session: StubSession = {
|
||||
subscribe: (fn) => {
|
||||
handler = fn;
|
||||
return () => {};
|
||||
},
|
||||
};
|
||||
|
||||
const subscription = subscribeEmbeddedPiSession({
|
||||
session: session as unknown as Parameters<typeof subscribeEmbeddedPiSession>[0]["session"],
|
||||
runId: "run-tools-4",
|
||||
sessionKey: "test-session",
|
||||
});
|
||||
|
||||
handler?.({
|
||||
type: "tool_execution_start",
|
||||
toolName: "session_status",
|
||||
toolCallId: "s1",
|
||||
args: { sessionKey: "agent:main:main", model: "openai/gpt-4o" },
|
||||
});
|
||||
handler?.({
|
||||
type: "tool_execution_end",
|
||||
toolName: "session_status",
|
||||
toolCallId: "s1",
|
||||
isError: true,
|
||||
result: { error: "Model not allowed." },
|
||||
});
|
||||
|
||||
handler?.({
|
||||
type: "tool_execution_start",
|
||||
toolName: "session_status",
|
||||
toolCallId: "s2",
|
||||
args: { sessionKey: "agent:main:main" },
|
||||
});
|
||||
handler?.({
|
||||
type: "tool_execution_end",
|
||||
toolName: "session_status",
|
||||
toolCallId: "s2",
|
||||
isError: false,
|
||||
result: { ok: true },
|
||||
});
|
||||
|
||||
expect(subscription.getLastToolError()?.toolName).toBe("session_status");
|
||||
});
|
||||
|
||||
it("emits lifecycle:error event on agent_end when last assistant message was an error", async () => {
|
||||
let handler: ((evt: unknown) => void) | undefined;
|
||||
const session: StubSession = {
|
||||
|
||||
Reference in New Issue
Block a user