diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b1a7891d5..2b8fe0e278b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai - Agents/Replies: emit a default completion acknowledgement (`✅ Done.`) when runs execute tools successfully but return no final assistant text, preventing silent no-reply turns after tool-only completions. (#22834) Thanks @Oldshue. - Agents/Subagents: honor `tools.subagents.tools.alsoAllow` and explicit subagent `allow` entries when resolving built-in subagent deny defaults, so explicitly granted tools (for example `sessions_send`) are no longer blocked unless re-denied in `tools.subagents.tools.deny`. (#23359) Thanks @goren-beehero. - Agents/Diagnostics: include resolved lifecycle error text in `embedded run agent end` warnings so UI/TUI “Connection error” runs expose actionable provider failure reasons in gateway logs. (#23054) Thanks @Raize. +- Agents/Auth profiles: skip auth-profile cooldown writes for timeout failures in embedded runner rotation so model/network timeouts do not poison same-provider fallback model selection while still allowing in-turn account rotation. (#22622) Thanks @vageeshkumar. - Plugins/Hooks: run legacy `before_agent_start` once per agent turn and reuse that result across model-resolve and prompt-build compatibility paths, preventing duplicate hook side effects (for example duplicate external API calls). (#23289) Thanks @ksato8710. - Models/Config: default missing Anthropic provider/model `api` fields to `anthropic-messages` during config validation so custom relay model entries are preserved instead of being dropped by runtime model registry validation. (#23332) Thanks @bigbigmonkey123. - Gateway/Pairing: treat operator.admin pairing tokens as satisfying operator.write requests so legacy devices stop looping through scope-upgrade prompts introduced in 2026.2.19. (#23125, #23006) Thanks @vignesh07. diff --git a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts index 09694ffb623..de67dd10039 100644 --- a/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts +++ b/src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts @@ -331,6 +331,25 @@ describe("runEmbeddedPiAgent auth profile rotation", () => { } }); + it("rotates on timeout without cooling down the timed-out profile", async () => { + await withAgentWorkspace(async ({ agentDir, workspaceDir }) => { + await writeAuthStore(agentDir); + mockFailedThenSuccessfulAttempt("request ended without sending any chunks"); + + await runAutoPinnedOpenAiTurn({ + agentDir, + workspaceDir, + sessionKey: "agent:test:timeout-no-cooldown", + runId: "run:timeout-no-cooldown", + }); + + expect(runEmbeddedAttemptMock).toHaveBeenCalledTimes(2); + const usageStats = await readUsageStats(agentDir); + expect(typeof usageStats["openai:p2"]?.lastUsed).toBe("number"); + expect(usageStats["openai:p1"]?.cooldownUntil).toBeUndefined(); + }); + }); + it("does not rotate for compaction timeouts", async () => { await withAgentWorkspace(async ({ agentDir, workspaceDir }) => { await writeAuthStore(agentDir); diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index dc36421fbf6..02e31b8febe 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -949,8 +949,8 @@ export async function runEmbeddedPiAgent( ); } - // Treat timeout as potential rate limit (Antigravity hangs on rate limit) - // But exclude post-prompt compaction timeouts (model succeeded; no profile issue) + // Rotate on timeout to try another account/model path in this turn, + // but exclude post-prompt compaction timeouts (model succeeded; no profile issue). const shouldRotate = (!aborted && failoverFailure) || (timedOut && !timedOutDuringCompaction); @@ -973,9 +973,7 @@ export async function runEmbeddedPiAgent( }); } if (timedOut && !isProbeSession) { - log.warn( - `Profile ${lastProfileId} timed out (possible rate limit). Trying next account...`, - ); + log.warn(`Profile ${lastProfileId} timed out. Trying next account...`); } if (cloudCodeAssistFormatError) { log.warn(