fix: address systems review findings for agent-not-found fallback

1. [HIGH] Tool restrictions recomputed for fallback agent via
   buildFallbackBody() — no longer inherits original agent's restrictions.

2. [HIGH] Double-retry race prevented — handleSessionErrorEvent now
   returns early for agent-not-found errors, since the prompt catch
   block already handles them with agent fallback. This prevents
   tryFallbackRetry from racing with a model-level retry on the same
   error (the "not found" pattern in RETRYABLE_MESSAGE_PATTERNS).

3. [MEDIUM] task.agent updated to FALLBACK_AGENT after successful
   fallback — notifications, toast, and logging reflect actual agent.

4. [MEDIUM] FALLBACK_AGENT exported from spawner.ts and imported into
   manager.ts — single source of truth.

5. [LOW] resumeTask fallback now uses promptWithModelSuggestionRetry
   (consistent with startTask), getting timeout protection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jim Park
2026-04-04 22:13:05 -07:00
parent 050502f8d0
commit 9470cbe090
3 changed files with 45 additions and 8 deletions
+15 -4
View File
@@ -1,6 +1,6 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { isAgentNotFoundError } from "./spawner"
import { isAgentNotFoundError, FALLBACK_AGENT, buildFallbackBody } from "./spawner"
import type {
BackgroundTask,
LaunchInput,
@@ -547,8 +547,6 @@ export class BackgroundManager {
applySessionPromptParams(sessionID, input.model)
}
const FALLBACK_AGENT = "general"
const promptBody = {
agent: input.agent,
...(launchModel ? { model: launchModel } : {}),
@@ -579,10 +577,13 @@ export class BackgroundManager {
taskId: task.id,
})
try {
const fallbackBody = buildFallbackBody(promptBody, FALLBACK_AGENT)
setSessionTools(sessionID, fallbackBody.tools as Record<string, boolean>)
await promptWithModelSuggestionRetry(this.client, {
path: { id: sessionID },
body: { ...promptBody, agent: FALLBACK_AGENT },
body: fallbackBody,
})
task.agent = FALLBACK_AGENT
return
} catch (retryError) {
log("[background-agent] Fallback agent also failed:", retryError)
@@ -1225,6 +1226,16 @@ export class BackgroundManager {
}): Promise<void> {
const { task, errorInfo, errorMessage, errorName } = args
// Agent-not-found errors are handled by the prompt catch block with agent fallback.
// Do not also trigger model fallback retry — that would race with the agent retry.
if (isAgentNotFoundError({ message: errorInfo.message } as Error)) {
log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
taskId: task.id,
errorMessage: errorInfo.message?.slice(0, 100),
})
return
}
if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
return
}