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
+22 -4
View File
@@ -8,13 +8,29 @@ import { getTaskToastManager } from "../task-toast-manager"
import { isInsideTmux } from "../../shared/tmux"
import type { ConcurrencyManager } from "./concurrency"
const FALLBACK_AGENT = "general"
export const FALLBACK_AGENT = "general"
export function isAgentNotFoundError(error: unknown): boolean {
const message = error instanceof Error ? error.message : String(error)
return message.includes("Agent not found")
}
export function buildFallbackBody(
originalBody: Record<string, unknown>,
fallbackAgent: string,
): Record<string, unknown> {
return {
...originalBody,
agent: fallbackAgent,
tools: {
task: false,
call_omo_agent: true,
question: false,
...getAgentToolRestrictions(fallbackAgent),
},
}
}
export interface SpawnerContext {
client: OpencodeClient
directory: string
@@ -172,8 +188,9 @@ export async function startTask(
try {
await promptWithModelSuggestionRetry(client, {
path: { id: sessionID },
body: { ...promptBody, agent: FALLBACK_AGENT },
body: buildFallbackBody(promptBody, FALLBACK_AGENT),
})
task.agent = FALLBACK_AGENT
return
} catch (retryError) {
log("[background-agent] Fallback agent also failed:", retryError)
@@ -279,10 +296,11 @@ export async function resumeTask(
taskId: task.id,
})
try {
await client.session.promptAsync({
await promptWithModelSuggestionRetry(client, {
path: { id: task.sessionID! },
body: { ...resumeBody, agent: FALLBACK_AGENT },
body: buildFallbackBody(resumeBody, FALLBACK_AGENT),
})
task.agent = FALLBACK_AGENT
return
} catch (retryError) {
log("[background-agent] Resume fallback agent also failed:", retryError)