

























@@ -648,6 +648,76 @@ describe("stuck session diagnostics threshold", () => {
648648);
649649});
650650651+it("aborts stale embedded runs when queued work refreshes session activity", () => {
652+const recoverStuckSession = vi.fn();
653+const stuckSessionWarnMs = 120_000;
654+const stuckSessionAbortMs = 360_000;
655+656+logSessionStateChange({ sessionId: "s1", sessionKey: "main", state: "processing" });
657+markDiagnosticEmbeddedRunStarted({ sessionId: "s1", sessionKey: "main" });
658+vi.advanceTimersByTime(507_000);
659+logMessageQueued({ sessionId: "s1", sessionKey: "main", source: "test" });
660+vi.advanceTimersByTime(122_000);
661+662+startDiagnosticHeartbeat(
663+{
664+diagnostics: {
665+enabled: true,
666+ stuckSessionWarnMs,
667+ stuckSessionAbortMs,
668+},
669+},
670+{ recoverStuckSession },
671+);
672+673+vi.advanceTimersByTime(30_000);
674+675+expectRecoveryCall(
676+recoverStuckSession,
677+{ sessionId: "s1", sessionKey: "main", queueDepth: 1, allowActiveAbort: true },
678+["ageMs", "stateGeneration"],
679+);
680+});
681+682+it("does not abort embedded runs with recent progress just because session activity is old", () => {
683+const recoverStuckSession = vi.fn();
684+const stuckSessionWarnMs = 30_000;
685+const stuckSessionAbortMs = 60_000;
686+687+logSessionStateChange({ sessionId: "s1", sessionKey: "main", state: "processing" });
688+markDiagnosticEmbeddedRunStarted({ sessionId: "s1", sessionKey: "main" });
689+vi.advanceTimersByTime(120_000);
690+markDiagnosticRunProgressForTest({
691+sessionId: "s1",
692+sessionKey: "main",
693+reason: "embedded_run:progress",
694+});
695+696+startDiagnosticHeartbeat(
697+{
698+diagnostics: {
699+enabled: true,
700+ stuckSessionWarnMs,
701+ stuckSessionAbortMs,
702+},
703+},
704+{ recoverStuckSession },
705+);
706+707+vi.advanceTimersByTime(30_000);
708+709+expect(recoverStuckSession).not.toHaveBeenCalled();
710+expectRecordFields(
711+getDiagnosticSessionActivitySnapshot({ sessionId: "s1", sessionKey: "main" }),
712+{
713+activeWorkKind: "embedded_run",
714+hasActiveEmbeddedRun: true,
715+lastProgressAgeMs: 30_000,
716+lastProgressReason: "embedded_run:progress",
717+},
718+);
719+});
720+651721it("recovers stale native tool calls through the active-run abort path", async () => {
652722const events: DiagnosticEventPayload[] = [];
653723const recoverStuckSession = vi.fn();
@@ -1819,14 +1889,14 @@ describe("stuck session diagnostics threshold", () => {
18191889logMessageQueued({ sessionId: "s1", sessionKey: "main", source: "test" });
18201890logSessionStateChange({ sessionId: "s1", sessionKey: "main", state: "processing" });
18211891markDiagnosticEmbeddedRunStarted({ sessionId: "s1", sessionKey: "main" });
1822-1823-vi.advanceTimersByTime(stuckSessionAbortMs - 1);
18241892markDiagnosticRunProgressForTest({
18251893sessionId: "s1",
18261894sessionKey: "main",
18271895reason: terminalReason,
18281896});
1829-vi.advanceTimersByTime(1);
1897+vi.advanceTimersByTime(stuckSessionAbortMs - stuckSessionWarnMs - 1);
1898+logMessageQueued({ sessionId: "s1", sessionKey: "main", source: "test" });
1899+vi.advanceTimersByTime(stuckSessionWarnMs + 1);
18301900} finally {
18311901unsubscribe();
18321902}
@@ -1842,13 +1912,13 @@ describe("stuck session diagnostics threshold", () => {
18421912classification: "stalled_agent_run",
18431913reason: "queued_behind_terminal_active_work",
18441914activeWorkKind: "embedded_run",
1845-queueDepth: 1,
1915+queueDepth: 2,
18461916terminalProgressStale: true,
18471917lastProgressReason: terminalReason,
18481918});
18491919expectRecoveryCall(
18501920recoverStuckSession,
1851-{ sessionId: "s1", sessionKey: "main", queueDepth: 1, allowActiveAbort: true },
1921+{ sessionId: "s1", sessionKey: "main", queueDepth: 2, allowActiveAbort: true },
18521922["ageMs", "stateGeneration"],
18531923);
18541924});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。