diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index 8b330d555d0f..bb9210e82634 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -31,7 +31,7 @@ import { runToolCall, type ThinkingLevel, } from "@earendil-works/pi-agent-core"; -import { contentText, getCurrentSystemMessage, retryDelayMs } from "@earendil-works/pi-ai"; +import { contentText, getCurrentSystemMessage, jitteredRetryDelayMs } from "@earendil-works/pi-ai"; import type { AssistantMessage, AuthResult, @@ -3803,7 +3803,7 @@ export class AgentSession { return false; } - const delayMs = retryDelayMs(settings, this._retryAttempt); + const delayMs = jitteredRetryDelayMs(settings, this._retryAttempt); this._emit({ type: "auto_retry_start", @@ -3816,7 +3816,7 @@ export class AgentSession { // Keep the failed attempt in raw history while durably omitting it from model projection. this._omitRecoveryAttempt(message); - // Wait with exponential backoff (abortable) + // Wait with jittered exponential backoff (abortable) this._retryAbortController = new AbortController(); try { await sleep(delayMs, this._retryAbortController.signal); diff --git a/packages/coding-agent/test/agent-session-retry.test.ts b/packages/coding-agent/test/agent-session-retry.test.ts index 206b25b6048a..89e67e02344f 100644 --- a/packages/coding-agent/test/agent-session-retry.test.ts +++ b/packages/coding-agent/test/agent-session-retry.test.ts @@ -4,7 +4,7 @@ import { join } from "node:path"; import { Agent, type AgentEvent, type AgentTool } from "@earendil-works/pi-agent-core"; import { type AssistantMessage, createAssistantMessageEventStream, getModel } from "@earendil-works/pi-ai/compat"; import { Type } from "typebox"; -import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { AgentSession } from "../src/core/agent-session.ts"; import { AuthStorage } from "../src/core/auth-storage.ts"; import { SessionManager } from "../src/core/session-manager.ts"; @@ -47,6 +47,7 @@ describe("AgentSession retry", () => { }); afterEach(() => { + vi.restoreAllMocks(); if (session) { session.dispose(); } @@ -155,7 +156,8 @@ describe("AgentSession retry", () => { }); it("caps agent retry delay", async () => { - // Regression for #8826. + // Regression for #8826. The top of the jitter range is the unjittered delay. + vi.spyOn(Math, "random").mockReturnValue(1); const created = await createSession({ failCount: 4, maxRetries: 5, maxAgentDelayMs: 5 }); const delays: number[] = []; created.session.subscribe((event) => {