

























@@ -12,6 +12,7 @@ import {
1212resetTaskRegistryForTests,
1313} from "../../tasks/task-registry.js";
1414import { withTempDir } from "../../test-helpers/temp-dir.js";
15+import { setGatewayDedupeEntry } from "./agent-wait-dedupe.js";
1516import { agentHandlers } from "./agent.js";
1617import { chatHandlers } from "./chat.js";
1718import { expectSubagentFollowupReactivation } from "./subagent-followup.test-helpers.js";
@@ -1451,14 +1452,15 @@ describe("gateway agent handler", () => {
14511452payloads: [],
14521453meta: { durationMs: 100, aborted: true },
14531454});
1455+const context = makeContext();
1454145614551457await invokeAgent(
14561458{
14571459message: "background cli task",
14581460sessionKey: "agent:main:main",
14591461idempotencyKey: "task-registry-agent-run-aborted",
14601462},
1461-{ reqId: "task-registry-agent-run-aborted" },
1463+{ context, reqId: "task-registry-agent-run-aborted" },
14621464);
1463146514641466await waitForAssertion(() => {
@@ -1468,6 +1470,11 @@ describe("gateway agent handler", () => {
14681470status: "timed_out",
14691471terminalSummary: "aborted",
14701472});
1473+expect(context.dedupe.get("agent:task-registry-agent-run-aborted")?.payload).toMatchObject({
1474+runId: "task-registry-agent-run-aborted",
1475+status: "timeout",
1476+summary: "aborted",
1477+});
14711478});
14721479});
14731480});
@@ -1480,14 +1487,15 @@ describe("gateway agent handler", () => {
14801487const abortError = new Error("This operation was aborted");
14811488abortError.name = "AbortError";
14821489mocks.agentCommand.mockRejectedValueOnce(abortError);
1490+const context = makeContext();
1483149114841492await invokeAgent(
14851493{
14861494message: "background cli task",
14871495sessionKey: "agent:main:main",
14881496idempotencyKey: "task-registry-agent-run-abort-error",
14891497},
1490-{ reqId: "task-registry-agent-run-abort-error" },
1498+{ context, reqId: "task-registry-agent-run-abort-error" },
14911499);
1492150014931501await waitForAssertion(() => {
@@ -1497,6 +1505,13 @@ describe("gateway agent handler", () => {
14971505status: "timed_out",
14981506error: "AbortError: This operation was aborted",
14991507});
1508+expect(
1509+context.dedupe.get("agent:task-registry-agent-run-abort-error")?.payload,
1510+).toMatchObject({
1511+runId: "task-registry-agent-run-abort-error",
1512+status: "timeout",
1513+summary: "aborted",
1514+});
15001515});
15011516});
15021517});
@@ -2896,6 +2911,69 @@ describe("gateway agent handler chat.abort integration", () => {
28962911expect(context.chatAbortControllers.has(runId)).toBe(false);
28972912});
289829132914+it("keeps the sessions.abort wait snapshot after late agent completion", async () => {
2915+prime();
2916+let capturedSignal: AbortSignal | undefined;
2917+let resolveRun:
2918+| ((value: { payloads: Array<{ text: string }>; meta: { durationMs: number } }) => void)
2919+| undefined;
2920+mocks.agentCommand.mockImplementationOnce((opts: { abortSignal?: AbortSignal }) => {
2921+capturedSignal = opts.abortSignal;
2922+return new Promise((resolve) => {
2923+resolveRun = resolve;
2924+});
2925+});
2926+2927+const context = makeContext();
2928+const runId = "idem-abort-snapshot-wins";
2929+await invokeAgent(
2930+{
2931+message: "hi",
2932+agentId: "main",
2933+sessionKey: "agent:main:main",
2934+idempotencyKey: runId,
2935+},
2936+{ context, reqId: runId },
2937+);
2938+2939+const abortRespond = vi.fn();
2940+await chatHandlers["chat.abort"]({
2941+params: { sessionKey: "agent:main:main", runId },
2942+respond: abortRespond as never,
2943+ context,
2944+req: { type: "req", id: "abort-req", method: "chat.abort" },
2945+client: null,
2946+isWebchatConnect: () => false,
2947+});
2948+expect(capturedSignal?.aborted).toBe(true);
2949+2950+setGatewayDedupeEntry({
2951+dedupe: context.dedupe,
2952+key: `agent:${runId}`,
2953+entry: {
2954+ts: 100,
2955+ok: true,
2956+payload: {
2957+ runId,
2958+status: "timeout",
2959+stopReason: "rpc",
2960+endedAt: 100,
2961+},
2962+},
2963+});
2964+2965+resolveRun?.({ payloads: [{ text: "late ok" }], meta: { durationMs: 1 } });
2966+2967+await waitForAssertion(() => {
2968+expect(context.dedupe.get(`agent:${runId}`)?.payload).toMatchObject({
2969+ runId,
2970+status: "timeout",
2971+stopReason: "rpc",
2972+endedAt: 100,
2973+});
2974+});
2975+});
2976+28992977it("chat.abort without runId aborts the active agent run for the sessionKey", async () => {
29002978prime();
29012979let capturedSignal: AbortSignal | undefined;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。