




















@@ -298,6 +298,71 @@ describe("resolveRunFailoverDecision", () => {
298298});
299299});
300300301+it("does not rotate harness-owned assistant timeouts", () => {
302+expect(
303+resolveRunFailoverDecision({
304+stage: "assistant",
305+aborted: true,
306+externalAbort: false,
307+fallbackConfigured: true,
308+failoverFailure: false,
309+failoverReason: null,
310+timedOut: true,
311+idleTimedOut: false,
312+timedOutDuringCompaction: false,
313+timedOutDuringToolExecution: false,
314+harnessOwnsTransport: true,
315+profileRotated: false,
316+}),
317+).toEqual({
318+action: "continue_normal",
319+});
320+});
321+322+it("rotates concrete assistant failover failures that accompany harness-owned timeouts", () => {
323+expect(
324+resolveRunFailoverDecision({
325+stage: "assistant",
326+aborted: false,
327+externalAbort: false,
328+fallbackConfigured: true,
329+failoverFailure: true,
330+failoverReason: "rate_limit",
331+timedOut: true,
332+idleTimedOut: false,
333+timedOutDuringCompaction: false,
334+timedOutDuringToolExecution: false,
335+harnessOwnsTransport: true,
336+profileRotated: false,
337+}),
338+).toEqual({
339+action: "rotate_profile",
340+reason: "rate_limit",
341+});
342+});
343+344+it("falls back with the concrete assistant failover reason after harness-owned timeout rotation is exhausted", () => {
345+expect(
346+resolveRunFailoverDecision({
347+stage: "assistant",
348+aborted: false,
349+externalAbort: false,
350+fallbackConfigured: true,
351+failoverFailure: true,
352+failoverReason: "billing",
353+timedOut: true,
354+idleTimedOut: false,
355+timedOutDuringCompaction: false,
356+timedOutDuringToolExecution: false,
357+harnessOwnsTransport: true,
358+profileRotated: true,
359+}),
360+).toEqual({
361+action: "fallback_model",
362+reason: "billing",
363+});
364+});
365+301366it("treats idle watchdog timeouts during tool execution as model silence", () => {
302367expect(
303368resolveRunFailoverDecision({
@@ -403,6 +468,45 @@ describe("resolveRunFailoverDecision", () => {
403468});
404469});
405470471+it("does not fallback harness-owned LLM idle timeouts after profile rotation is exhausted", () => {
472+expect(
473+resolveRunFailoverDecision({
474+stage: "assistant",
475+aborted: false,
476+externalAbort: false,
477+fallbackConfigured: true,
478+failoverFailure: false,
479+failoverReason: null,
480+timedOut: false,
481+idleTimedOut: true,
482+timedOutDuringCompaction: false,
483+timedOutDuringToolExecution: false,
484+harnessOwnsTransport: true,
485+profileRotated: true,
486+}),
487+).toEqual({
488+action: "continue_normal",
489+});
490+});
491+492+it("surfaces harness-owned prompt timeouts instead of falling back", () => {
493+expect(
494+resolveRunFailoverDecision({
495+stage: "prompt",
496+aborted: false,
497+externalAbort: false,
498+fallbackConfigured: true,
499+failoverFailure: true,
500+failoverReason: "timeout",
501+harnessOwnsTransport: true,
502+profileRotated: true,
503+}),
504+).toEqual({
505+action: "surface_error",
506+reason: "timeout",
507+});
508+});
509+406510it("surfaces error on LLM idle timeout when no fallback is configured and rotation is exhausted", () => {
407511expect(
408512resolveRunFailoverDecision({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。