From 70b67b0c68de30677c86c3fc4ef9fe1ce75ff37f Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 14 Apr 2026 16:47:01 -0400 Subject: [PATCH] fix(agents): preserve original prompt on model fallback retry (#65760) (#66029) Merged via squash. Prepared head SHA: ba919d19348f392cec5da8f0bf73c0733061a13b Co-authored-by: WuKongAI-CMU <210765158+WuKongAI-CMU@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf --- CHANGELOG.md | 1 + src/agents/command/attempt-execution.helpers.ts | 8 +++++++- src/agents/command/attempt-execution.test.ts | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e70946065c8..de7d17b109f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Docs: https://docs.openclaw.ai - Agents/workspace files: route `agents.files.get`, `agents.files.set`, and workspace listing through the shared `fs-safe` helpers (`openFileWithinRoot`/`readFileWithinRoot`/`writeFileWithinRoot`), reject symlink aliases for allowlisted agent files, and have `fs-safe` resolve opened-file real paths from the file descriptor before falling back to path-based `realpath` so a symlink swap between `open` and `realpath` can no longer redirect the validated path off the intended inode. (#66636) Thanks @eleqtrizit. - Gateway/MCP loopback: switch the `/mcp` bearer comparison from plain `!==` to constant-time `safeEqualSecret` (matching the convention every other auth surface in the codebase uses), and reject non-loopback browser-origin requests via `checkBrowserOrigin` before the auth gate runs. Loopback origins (`127.0.0.1:*`, `localhost:*`, same-origin) still go through, including the `localhost`↔`127.0.0.1` host mismatch that browsers flag as `Sec-Fetch-Site: cross-site`. (#66665) Thanks @eleqtrizit. - Auto-reply/billing: classify pure billing cooldown fallback summaries from structured fallback reasons so users see billing guidance instead of the generic failure reply. (#66363) Thanks @Rohan5commit. +- Agents/fallback: preserve the original prompt body on model fallback retries with session history so the retrying model keeps the active task instead of only seeing a generic continue message. (#66029) Thanks @WuKongAI-CMU. ## 2026.4.14 diff --git a/src/agents/command/attempt-execution.helpers.ts b/src/agents/command/attempt-execution.helpers.ts index f9abc3401c1..2315346ecf5 100644 --- a/src/agents/command/attempt-execution.helpers.ts +++ b/src/agents/command/attempt-execution.helpers.ts @@ -73,7 +73,13 @@ export function resolveFallbackRetryPrompt(params: { if (!params.sessionHasHistory) { return params.body; } - return "Continue where you left off. The previous model attempt failed or timed out."; + // Even with persisted session history, fully replacing the body with a + // generic "continue where you left off" message strips the original task + // from the fallback model's view. Agents then have to reconstruct the + // instruction from history alone, which is fragile and sometimes + // impossible. Prepend the retry context to the original body instead so + // the fallback model has both the recovery signal AND the task. (#65760) + return `[Retry after the previous model attempt failed or timed out]\n\n${params.body}`; } export function createAcpVisibleTextAccumulator() { diff --git a/src/agents/command/attempt-execution.test.ts b/src/agents/command/attempt-execution.test.ts index b1b5ab72700..6ab929778bd 100644 --- a/src/agents/command/attempt-execution.test.ts +++ b/src/agents/command/attempt-execution.test.ts @@ -20,14 +20,14 @@ describe("resolveFallbackRetryPrompt", () => { ).toBe(originalBody); }); - it("returns recovery prompt for fallback retry with existing session history", () => { + it("prepends recovery prefix to original body on fallback retry with existing session history", () => { expect( resolveFallbackRetryPrompt({ body: originalBody, isFallbackRetry: true, sessionHasHistory: true, }), - ).toBe("Continue where you left off. The previous model attempt failed or timed out."); + ).toBe(`[Retry after the previous model attempt failed or timed out]\n\n${originalBody}`); }); it("preserves original body for fallback retry when session has no history (subagent spawn)", () => {