diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3884782..523bb65d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ Docs: https://docs.openclaw.ai ### Fixes -- Security/Agents: cap embedded Pi runner outer retry loop to 24 attempts and return an explicit `retry_limit` error payload when retries never converge, preventing unbounded internal retry cycles (`GHSA-76m6-pj3w-v7mf`). +- Security/Agents: cap embedded Pi runner outer retry loop with a higher profile-aware dynamic limit (32-160 attempts) and return an explicit `retry_limit` error payload when retries never converge, preventing unbounded internal retry cycles (`GHSA-76m6-pj3w-v7mf`). - Telegram: detect duplicate bot-token ownership across Telegram accounts at startup/status time, mark secondary accounts as not configured with an explicit fix message, and block duplicate account startup before polling to avoid endless `getUpdates` conflict loops. - Agents/Tool images: include source filenames in `agents/tool-images` resize logs so compression events can be traced back to specific files. - Providers/OAuth: harden Qwen and Chutes refresh handling by validating refresh response expiry values and preserving prior refresh tokens when providers return empty refresh token fields, with regression coverage for empty-token responses. diff --git a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts index 29531fb07..c80ef3430 100644 --- a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts +++ b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts @@ -128,7 +128,7 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => { runId: "run-1", }); - expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(24); + expect(mockedRunEmbeddedAttempt).toHaveBeenCalledTimes(32); expect(mockedCompactDirect).not.toHaveBeenCalled(); expect(result.meta.error?.kind).toBe("retry_limit"); expect(result.payloads?.[0]?.isError).toBe(true); diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index be61bb601..83ae3e214 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -103,7 +103,17 @@ function createCompactionDiagId(): string { } // Defensive guard for the outer run loop across all retry branches. -const MAX_RUN_RETRY_ITERATIONS = 24; +const BASE_RUN_RETRY_ITERATIONS = 24; +const RUN_RETRY_ITERATIONS_PER_PROFILE = 8; +const MIN_RUN_RETRY_ITERATIONS = 32; +const MAX_RUN_RETRY_ITERATIONS = 160; + +function resolveMaxRunRetryIterations(profileCandidateCount: number): number { + const scaled = + BASE_RUN_RETRY_ITERATIONS + + Math.max(1, profileCandidateCount) * RUN_RETRY_ITERATIONS_PER_PROFILE; + return Math.min(MAX_RUN_RETRY_ITERATIONS, Math.max(MIN_RUN_RETRY_ITERATIONS, scaled)); +} const hasUsageValues = ( usage: ReturnType, @@ -478,7 +488,7 @@ export async function runEmbeddedPiAgent( } const MAX_OVERFLOW_COMPACTION_ATTEMPTS = 3; - const MAX_RUN_LOOP_ITERATIONS = MAX_RUN_RETRY_ITERATIONS; + const MAX_RUN_LOOP_ITERATIONS = resolveMaxRunRetryIterations(profileCandidates.length); let overflowCompactionAttempts = 0; let toolResultTruncationAttempted = false; const usageAccumulator = createUsageAccumulator(); @@ -488,10 +498,13 @@ export async function runEmbeddedPiAgent( try { while (true) { if (runLoopIterations >= MAX_RUN_LOOP_ITERATIONS) { - const message = `Exceeded retry limit after ${runLoopIterations} attempts.`; + const message = + `Exceeded retry limit after ${runLoopIterations} attempts ` + + `(max=${MAX_RUN_LOOP_ITERATIONS}).`; log.error( `[run-retry-limit] sessionKey=${params.sessionKey ?? params.sessionId} ` + - `provider=${provider}/${modelId} attempts=${runLoopIterations}`, + `provider=${provider}/${modelId} attempts=${runLoopIterations} ` + + `maxAttempts=${MAX_RUN_LOOP_ITERATIONS}`, ); return { payloads: [