mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-25 20:21:12 +00:00
fix(gateway): stop reporting user cancellations as gateway draining (#110220)
* fix(gateway): preserve agent cancel attribution * chore: keep release note in PR body
This commit is contained in:
committed by
GitHub
parent
9dedacf577
commit
abc7dffe4c
@@ -64,6 +64,16 @@ function resolveGatewayAgentAbortStopReason(signal: AbortSignal): "restart" | "r
|
||||
return readErrorName(signal.reason) === "TimeoutError" ? "timeout" : "rpc";
|
||||
}
|
||||
|
||||
function resolveAbortedAgentTaskStatus(stopReason: string | undefined): "cancelled" | "timed_out" {
|
||||
return stopReason === "timeout" ? "timed_out" : "cancelled";
|
||||
}
|
||||
|
||||
function resolveGatewayAgentAbortTimeoutPhase(
|
||||
stopReason: "restart" | "rpc" | "timeout",
|
||||
): "gateway_draining" | undefined {
|
||||
return stopReason === "restart" ? "gateway_draining" : undefined;
|
||||
}
|
||||
|
||||
export function resolveAbortedAgentStopReason(entry?: ChatAbortControllerEntry): string {
|
||||
return entry?.abortStopReason?.trim() || "rpc";
|
||||
}
|
||||
@@ -146,11 +156,12 @@ export function dispatchAgentRunFromGateway(params: {
|
||||
})
|
||||
.then(async (result) => {
|
||||
const aborted = result?.meta?.aborted === true;
|
||||
const stopReason = aborted ? (result?.meta?.stopReason ?? "rpc") : undefined;
|
||||
const timeoutAttribution = readAgentRunTimeoutAttribution(result?.meta);
|
||||
if (taskTracked) {
|
||||
tryFinalizeTrackedAgentTask({
|
||||
runId: params.runId,
|
||||
status: aborted ? "timed_out" : "succeeded",
|
||||
status: aborted ? resolveAbortedAgentTaskStatus(stopReason) : "succeeded",
|
||||
terminalSummary: aborted ? "aborted" : "completed",
|
||||
log: params.context.logGateway,
|
||||
});
|
||||
@@ -159,7 +170,7 @@ export function dispatchAgentRunFromGateway(params: {
|
||||
runId: params.runId,
|
||||
status: aborted ? ("timeout" as const) : ("ok" as const),
|
||||
summary: aborted ? "aborted" : "completed",
|
||||
...(aborted ? { stopReason: result?.meta?.stopReason ?? "rpc" } : {}),
|
||||
...(aborted ? { stopReason } : {}),
|
||||
...(aborted && timeoutAttribution.timeoutPhase
|
||||
? { timeoutPhase: timeoutAttribution.timeoutPhase }
|
||||
: {}),
|
||||
@@ -213,28 +224,31 @@ export function dispatchAgentRunFromGateway(params: {
|
||||
.catch(async (err: unknown) => {
|
||||
const aborted = isGatewayAgentAbortRejection(err, params.abortController.signal);
|
||||
const renderedErr = formatForLog(err);
|
||||
const stopReason = resolveGatewayAgentAbortStopReason(params.abortController.signal);
|
||||
const timeoutPhase = aborted ? resolveGatewayAgentAbortTimeoutPhase(stopReason) : undefined;
|
||||
if (taskTracked) {
|
||||
tryFinalizeTrackedAgentTask({
|
||||
runId: params.runId,
|
||||
status: aborted ? "timed_out" : resolveFailedTrackedAgentTaskStatus(err),
|
||||
status: aborted
|
||||
? resolveAbortedAgentTaskStatus(stopReason)
|
||||
: resolveFailedTrackedAgentTaskStatus(err),
|
||||
error: renderedErr,
|
||||
terminalSummary: renderedErr,
|
||||
log: params.context.logGateway,
|
||||
});
|
||||
}
|
||||
const error = errorShape(ErrorCodes.UNAVAILABLE, renderedErr);
|
||||
const stopReason = resolveGatewayAgentAbortStopReason(params.abortController.signal);
|
||||
const terminalOutcome = buildAgentRunTerminalOutcome({
|
||||
status: aborted ? "timeout" : "error",
|
||||
error: renderedErr,
|
||||
stopReason,
|
||||
timeoutPhase: aborted ? "gateway_draining" : undefined,
|
||||
timeoutPhase,
|
||||
});
|
||||
const payload = {
|
||||
runId: params.runId,
|
||||
status: aborted ? ("timeout" as const) : ("error" as const),
|
||||
summary: aborted ? "aborted" : renderedErr,
|
||||
...(aborted ? { stopReason, timeoutPhase: "gateway_draining" as const } : {}),
|
||||
...(aborted ? { stopReason, ...(timeoutPhase ? { timeoutPhase } : {}) } : {}),
|
||||
};
|
||||
const persistTerminalDedupe = (settlementPersisted: boolean) => {
|
||||
setGatewayDedupeEntries({
|
||||
|
||||
@@ -430,7 +430,7 @@ describe("gateway agent handler", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves aborted async gateway agent runs as timed out", async () => {
|
||||
it("preserves aborted async gateway agent runs as cancelled", async () => {
|
||||
await withTempDir({ prefix: "openclaw-gateway-agent-task-aborted-" }, async (root) => {
|
||||
useTestStateDir(root);
|
||||
resetTaskRegistryForTests();
|
||||
@@ -454,7 +454,7 @@ describe("gateway agent handler", () => {
|
||||
expectRecordFields(findTaskByRunId("task-registry-agent-run-aborted"), {
|
||||
runtime: "cli",
|
||||
childSessionKey: "agent:main:main",
|
||||
status: "timed_out",
|
||||
status: "cancelled",
|
||||
terminalSummary: "aborted",
|
||||
});
|
||||
expectRecordFields(context.dedupe.get("agent:task-registry-agent-run-aborted")?.payload, {
|
||||
@@ -466,7 +466,7 @@ describe("gateway agent handler", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("classifies aborted async gateway agent rejections as timed out", async () => {
|
||||
it("classifies RPC-aborted async gateway agent rejections as cancelled", async () => {
|
||||
await withTempDir({ prefix: "openclaw-gateway-agent-task-abort-error-" }, async (root) => {
|
||||
useTestStateDir(root);
|
||||
resetTaskRegistryForTests();
|
||||
@@ -493,7 +493,7 @@ describe("gateway agent handler", () => {
|
||||
expectRecordFields(findTaskByRunId("task-registry-agent-run-abort-error"), {
|
||||
runtime: "cli",
|
||||
childSessionKey: "agent:main:main",
|
||||
status: "timed_out",
|
||||
status: "cancelled",
|
||||
error: "AbortError: This operation was aborted",
|
||||
});
|
||||
expectRecordFields(
|
||||
@@ -505,6 +505,9 @@ describe("gateway agent handler", () => {
|
||||
stopReason: "rpc",
|
||||
},
|
||||
);
|
||||
expect(
|
||||
context.dedupe.get("agent:task-registry-agent-run-abort-error")?.payload,
|
||||
).not.toHaveProperty("timeoutPhase");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -536,11 +539,15 @@ describe("gateway agent handler", () => {
|
||||
);
|
||||
|
||||
await waitForAssertion(() => {
|
||||
expectRecordFields(findTaskByRunId(runId), {
|
||||
status: "cancelled",
|
||||
});
|
||||
expectRecordFields(context.dedupe.get(`agent:${runId}`)?.payload, {
|
||||
runId,
|
||||
status: "timeout",
|
||||
summary: "aborted",
|
||||
stopReason: "restart",
|
||||
timeoutPhase: "gateway_draining",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -585,6 +592,9 @@ describe("gateway agent handler", () => {
|
||||
stopReason: "timeout",
|
||||
},
|
||||
);
|
||||
expect(
|
||||
context.dedupe.get("agent:task-registry-agent-run-timeout-error")?.payload,
|
||||
).not.toHaveProperty("timeoutPhase");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user