fix(agents): raise dynamic retry cap budget

This commit is contained in:
Peter Steinberger
2026-02-21 15:41:03 +01:00
parent 1bd3f01c17
commit c8466e516f
3 changed files with 19 additions and 6 deletions

View File

@@ -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.

View File

@@ -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);

View File

@@ -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<typeof normalizeUsage>,
@@ -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: [