Merge pull request #3135 from jim80net/fix/agent-not-found-fallback
fix(background-agent): retry with fallback agent on Agent not found
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
import { isAgentNotFoundError, FALLBACK_AGENT, buildFallbackBody } from "./spawner"
|
||||||
import type {
|
import type {
|
||||||
BackgroundTask,
|
BackgroundTask,
|
||||||
LaunchInput,
|
LaunchInput,
|
||||||
@@ -546,32 +547,55 @@ export class BackgroundManager {
|
|||||||
applySessionPromptParams(sessionID, input.model)
|
applySessionPromptParams(sessionID, input.model)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const promptBody = {
|
||||||
|
agent: input.agent,
|
||||||
|
...(launchModel ? { model: launchModel } : {}),
|
||||||
|
...(launchVariant ? { variant: launchVariant } : {}),
|
||||||
|
system: input.skillContent,
|
||||||
|
tools: (() => {
|
||||||
|
const tools = {
|
||||||
|
task: false,
|
||||||
|
call_omo_agent: true,
|
||||||
|
question: false,
|
||||||
|
...getAgentToolRestrictions(input.agent),
|
||||||
|
}
|
||||||
|
setSessionTools(sessionID, tools)
|
||||||
|
return tools
|
||||||
|
})(),
|
||||||
|
parts: [createInternalAgentTextPart(input.prompt)],
|
||||||
|
}
|
||||||
|
|
||||||
promptWithModelSuggestionRetry(this.client, {
|
promptWithModelSuggestionRetry(this.client, {
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: promptBody,
|
||||||
agent: input.agent,
|
|
||||||
...(launchModel ? { model: launchModel } : {}),
|
|
||||||
...(launchVariant ? { variant: launchVariant } : {}),
|
|
||||||
system: input.skillContent,
|
|
||||||
tools: (() => {
|
|
||||||
const tools = {
|
|
||||||
task: false,
|
|
||||||
call_omo_agent: true,
|
|
||||||
question: false,
|
|
||||||
...getAgentToolRestrictions(input.agent),
|
|
||||||
}
|
|
||||||
setSessionTools(sessionID, tools)
|
|
||||||
return tools
|
|
||||||
})(),
|
|
||||||
parts: [createInternalAgentTextPart(input.prompt)],
|
|
||||||
},
|
|
||||||
}).catch(async (error) => {
|
}).catch(async (error) => {
|
||||||
|
// Retry with fallback agent if the original agent was unregistered (e.g., after a model switch)
|
||||||
|
if (isAgentNotFoundError(error) && input.agent !== FALLBACK_AGENT) {
|
||||||
|
log("[background-agent] Agent not found, retrying with fallback agent", {
|
||||||
|
original: input.agent,
|
||||||
|
fallback: FALLBACK_AGENT,
|
||||||
|
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: fallbackBody,
|
||||||
|
})
|
||||||
|
task.agent = FALLBACK_AGENT
|
||||||
|
return
|
||||||
|
} catch (retryError) {
|
||||||
|
log("[background-agent] Fallback agent also failed:", retryError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log("[background-agent] promptAsync error:", error)
|
log("[background-agent] promptAsync error:", error)
|
||||||
const existingTask = this.findBySession(sessionID)
|
const existingTask = this.findBySession(sessionID)
|
||||||
if (existingTask) {
|
if (existingTask) {
|
||||||
existingTask.status = "interrupt"
|
existingTask.status = "interrupt"
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||||
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
|
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error)) {
|
||||||
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`
|
existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`
|
||||||
} else {
|
} else {
|
||||||
existingTask.error = errorMessage
|
existingTask.error = errorMessage
|
||||||
@@ -1202,6 +1226,16 @@ export class BackgroundManager {
|
|||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
const { task, errorInfo, errorMessage, errorName } = args
|
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")) {
|
if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,321 @@ import {
|
|||||||
getSessionPromptParams,
|
getSessionPromptParams,
|
||||||
} from "../../shared/session-prompt-params-state"
|
} from "../../shared/session-prompt-params-state"
|
||||||
|
|
||||||
|
describe("background-agent spawner agent-not-found fallback", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
clearSessionPromptParams("session-fallback")
|
||||||
|
})
|
||||||
|
|
||||||
|
test("retries with 'general' agent when promptAsync fails with Agent not found", async () => {
|
||||||
|
//#given
|
||||||
|
const promptCalls: any[] = []
|
||||||
|
let callCount = 0
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/tmp/test" } }),
|
||||||
|
create: async () => ({ data: { id: "session-fallback" } }),
|
||||||
|
promptAsync: async (args: any) => {
|
||||||
|
callCount++
|
||||||
|
promptCalls.push({ body: { ...args.body }, path: { ...args.path } })
|
||||||
|
if (callCount === 1) {
|
||||||
|
throw new Error('Agent not found: "Sisyphus-Junior". Available agents: build, explore, general, plan')
|
||||||
|
}
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const onTaskError = mock(() => {})
|
||||||
|
|
||||||
|
const task = createTask({
|
||||||
|
description: "Implement feature",
|
||||||
|
prompt: "Please implement the break-even analysis",
|
||||||
|
agent: "Sisyphus-Junior",
|
||||||
|
parentSessionID: "ses_parent",
|
||||||
|
parentMessageID: "msg_parent",
|
||||||
|
})
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
task,
|
||||||
|
input: {
|
||||||
|
description: task.description,
|
||||||
|
prompt: task.prompt,
|
||||||
|
agent: task.agent,
|
||||||
|
parentSessionID: task.parentSessionID,
|
||||||
|
parentMessageID: task.parentMessageID,
|
||||||
|
parentModel: task.parentModel,
|
||||||
|
parentAgent: task.parentAgent,
|
||||||
|
model: task.model,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
client,
|
||||||
|
directory: "/tmp/test",
|
||||||
|
concurrencyManager: { release: () => {} },
|
||||||
|
tmuxEnabled: false,
|
||||||
|
onTaskError,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await startTask(item as any, ctx as any)
|
||||||
|
|
||||||
|
// Wait for the fire-and-forget prompt chain to settle
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
// Should have called promptAsync twice: once with original agent, once with fallback
|
||||||
|
expect(promptCalls).toHaveLength(2)
|
||||||
|
expect(promptCalls[0].body.agent).toBe("Sisyphus-Junior")
|
||||||
|
expect(promptCalls[1].body.agent).toBe("general")
|
||||||
|
// Original prompt content preserved in fallback
|
||||||
|
expect(promptCalls[1].body.parts).toEqual(promptCalls[0].body.parts)
|
||||||
|
// Tool restrictions recomputed for fallback agent (general has no restrictions)
|
||||||
|
expect(promptCalls[1].body.tools).toEqual({
|
||||||
|
task: false,
|
||||||
|
call_omo_agent: true,
|
||||||
|
question: false,
|
||||||
|
})
|
||||||
|
// Task agent identity updated to reflect fallback
|
||||||
|
expect(task.agent).toBe("general")
|
||||||
|
// Task should not have errored
|
||||||
|
expect(onTaskError).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("does not retry for non-agent-not-found errors", async () => {
|
||||||
|
//#given
|
||||||
|
const promptCalls: any[] = []
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/tmp/test" } }),
|
||||||
|
create: async () => ({ data: { id: "session-fallback" } }),
|
||||||
|
promptAsync: async (args: any) => {
|
||||||
|
promptCalls.push(args)
|
||||||
|
throw new Error("Connection timeout")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const onTaskError = mock(() => {})
|
||||||
|
|
||||||
|
const task = createTask({
|
||||||
|
description: "Implement feature",
|
||||||
|
prompt: "Do work",
|
||||||
|
agent: "Sisyphus-Junior",
|
||||||
|
parentSessionID: "ses_parent",
|
||||||
|
parentMessageID: "msg_parent",
|
||||||
|
})
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
task,
|
||||||
|
input: {
|
||||||
|
description: task.description,
|
||||||
|
prompt: task.prompt,
|
||||||
|
agent: task.agent,
|
||||||
|
parentSessionID: task.parentSessionID,
|
||||||
|
parentMessageID: task.parentMessageID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
client,
|
||||||
|
directory: "/tmp/test",
|
||||||
|
concurrencyManager: { release: () => {} },
|
||||||
|
tmuxEnabled: false,
|
||||||
|
onTaskError,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await startTask(item as any, ctx as any)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
// Only one attempt — no retry for non-agent errors
|
||||||
|
expect(promptCalls).toHaveLength(1)
|
||||||
|
expect(onTaskError).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("calls onTaskError if fallback agent also fails", async () => {
|
||||||
|
//#given
|
||||||
|
let callCount = 0
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/tmp/test" } }),
|
||||||
|
create: async () => ({ data: { id: "session-fallback" } }),
|
||||||
|
promptAsync: async () => {
|
||||||
|
callCount++
|
||||||
|
throw new Error('Agent not found: "Sisyphus-Junior". Available agents: build, explore, general, plan')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const onTaskError = mock(() => {})
|
||||||
|
|
||||||
|
const task = createTask({
|
||||||
|
description: "Implement feature",
|
||||||
|
prompt: "Do work",
|
||||||
|
agent: "Sisyphus-Junior",
|
||||||
|
parentSessionID: "ses_parent",
|
||||||
|
parentMessageID: "msg_parent",
|
||||||
|
})
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
task,
|
||||||
|
input: {
|
||||||
|
description: task.description,
|
||||||
|
prompt: task.prompt,
|
||||||
|
agent: task.agent,
|
||||||
|
parentSessionID: task.parentSessionID,
|
||||||
|
parentMessageID: task.parentMessageID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
client,
|
||||||
|
directory: "/tmp/test",
|
||||||
|
concurrencyManager: { release: () => {} },
|
||||||
|
tmuxEnabled: false,
|
||||||
|
onTaskError,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await startTask(item as any, ctx as any)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
// Verify retry was attempted (2 calls: original + fallback)
|
||||||
|
expect(callCount).toBe(2)
|
||||||
|
expect(onTaskError).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("retries on agent.name/undefined error variant", async () => {
|
||||||
|
//#given
|
||||||
|
const promptCalls: any[] = []
|
||||||
|
let callCount = 0
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/tmp/test" } }),
|
||||||
|
create: async () => ({ data: { id: "session-fallback" } }),
|
||||||
|
promptAsync: async (args: any) => {
|
||||||
|
callCount++
|
||||||
|
promptCalls.push({ body: { ...args.body } })
|
||||||
|
if (callCount === 1) {
|
||||||
|
throw new Error("Cannot read properties of undefined (reading 'agent.name')")
|
||||||
|
}
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const onTaskError = mock(() => {})
|
||||||
|
|
||||||
|
const task = createTask({
|
||||||
|
description: "Test task",
|
||||||
|
prompt: "Do work",
|
||||||
|
agent: "Sisyphus-Junior",
|
||||||
|
parentSessionID: "ses_parent",
|
||||||
|
parentMessageID: "msg_parent",
|
||||||
|
})
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
task,
|
||||||
|
input: {
|
||||||
|
description: task.description,
|
||||||
|
prompt: task.prompt,
|
||||||
|
agent: task.agent,
|
||||||
|
parentSessionID: task.parentSessionID,
|
||||||
|
parentMessageID: task.parentMessageID,
|
||||||
|
parentModel: task.parentModel,
|
||||||
|
parentAgent: task.parentAgent,
|
||||||
|
model: task.model,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
client,
|
||||||
|
directory: "/tmp/test",
|
||||||
|
concurrencyManager: { release: () => {} },
|
||||||
|
tmuxEnabled: false,
|
||||||
|
onTaskError,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await startTask(item as any, ctx as any)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(promptCalls).toHaveLength(2)
|
||||||
|
expect(promptCalls[0].body.agent).toBe("Sisyphus-Junior")
|
||||||
|
expect(promptCalls[1].body.agent).toBe("general")
|
||||||
|
expect(onTaskError).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("detects agent error from plain object with message field", async () => {
|
||||||
|
//#given
|
||||||
|
const promptCalls: any[] = []
|
||||||
|
let callCount = 0
|
||||||
|
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
get: async () => ({ data: { directory: "/tmp/test" } }),
|
||||||
|
create: async () => ({ data: { id: "session-fallback" } }),
|
||||||
|
promptAsync: async (args: any) => {
|
||||||
|
callCount++
|
||||||
|
promptCalls.push({ body: { ...args.body } })
|
||||||
|
if (callCount === 1) {
|
||||||
|
throw { message: 'Agent not found: "Custom-Agent"', name: "UnknownError" }
|
||||||
|
}
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
|
||||||
|
const onTaskError = mock(() => {})
|
||||||
|
|
||||||
|
const task = createTask({
|
||||||
|
description: "Test task",
|
||||||
|
prompt: "Do work",
|
||||||
|
agent: "Custom-Agent",
|
||||||
|
parentSessionID: "ses_parent",
|
||||||
|
parentMessageID: "msg_parent",
|
||||||
|
})
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
task,
|
||||||
|
input: {
|
||||||
|
description: task.description,
|
||||||
|
prompt: task.prompt,
|
||||||
|
agent: task.agent,
|
||||||
|
parentSessionID: task.parentSessionID,
|
||||||
|
parentMessageID: task.parentMessageID,
|
||||||
|
parentModel: task.parentModel,
|
||||||
|
parentAgent: task.parentAgent,
|
||||||
|
model: task.model,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = {
|
||||||
|
client,
|
||||||
|
directory: "/tmp/test",
|
||||||
|
concurrencyManager: { release: () => {} },
|
||||||
|
tmuxEnabled: false,
|
||||||
|
onTaskError,
|
||||||
|
}
|
||||||
|
|
||||||
|
//#when
|
||||||
|
await startTask(item as any, ctx as any)
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50))
|
||||||
|
|
||||||
|
//#then
|
||||||
|
expect(promptCalls).toHaveLength(2)
|
||||||
|
expect(promptCalls[1].body.agent).toBe("general")
|
||||||
|
expect(onTaskError).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("background-agent spawner fallback model promotion", () => {
|
describe("background-agent spawner fallback model promotion", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
clearSessionPromptParams("session-123")
|
clearSessionPromptParams("session-123")
|
||||||
|
|||||||
@@ -8,6 +8,39 @@ import { getTaskToastManager } from "../task-toast-manager"
|
|||||||
import { isInsideTmux } from "../../shared/tmux"
|
import { isInsideTmux } from "../../shared/tmux"
|
||||||
import type { ConcurrencyManager } from "./concurrency"
|
import type { ConcurrencyManager } from "./concurrency"
|
||||||
|
|
||||||
|
export const FALLBACK_AGENT = "general"
|
||||||
|
|
||||||
|
export function isAgentNotFoundError(error: unknown): boolean {
|
||||||
|
const message =
|
||||||
|
typeof error === "string"
|
||||||
|
? error
|
||||||
|
: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: typeof error === "object" && error !== null && typeof (error as { message?: unknown }).message === "string"
|
||||||
|
? (error as { message: string }).message
|
||||||
|
: String(error)
|
||||||
|
return (
|
||||||
|
message.includes("Agent not found") ||
|
||||||
|
message.includes("agent.name")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
export interface SpawnerContext {
|
||||||
client: OpencodeClient
|
client: OpencodeClient
|
||||||
directory: string
|
directory: string
|
||||||
@@ -138,22 +171,43 @@ export async function startTask(
|
|||||||
|
|
||||||
applySessionPromptParams(sessionID, input.model)
|
applySessionPromptParams(sessionID, input.model)
|
||||||
|
|
||||||
|
const promptBody = {
|
||||||
|
agent: input.agent,
|
||||||
|
...(launchModel ? { model: launchModel } : {}),
|
||||||
|
...(launchVariant ? { variant: launchVariant } : {}),
|
||||||
|
system: input.skillContent,
|
||||||
|
tools: {
|
||||||
|
task: false,
|
||||||
|
call_omo_agent: true,
|
||||||
|
question: false,
|
||||||
|
...getAgentToolRestrictions(input.agent),
|
||||||
|
},
|
||||||
|
parts: [createInternalAgentTextPart(input.prompt)],
|
||||||
|
}
|
||||||
|
|
||||||
promptWithModelSuggestionRetry(client, {
|
promptWithModelSuggestionRetry(client, {
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: promptBody,
|
||||||
agent: input.agent,
|
}).catch(async (error) => {
|
||||||
...(launchModel ? { model: launchModel } : {}),
|
if (isAgentNotFoundError(error) && input.agent !== FALLBACK_AGENT) {
|
||||||
...(launchVariant ? { variant: launchVariant } : {}),
|
log("[background-agent] Agent not found, retrying with fallback agent", {
|
||||||
system: input.skillContent,
|
original: input.agent,
|
||||||
tools: {
|
fallback: FALLBACK_AGENT,
|
||||||
task: false,
|
taskId: task.id,
|
||||||
call_omo_agent: true,
|
})
|
||||||
question: false,
|
try {
|
||||||
...getAgentToolRestrictions(input.agent),
|
await promptWithModelSuggestionRetry(client, {
|
||||||
},
|
path: { id: sessionID },
|
||||||
parts: [createInternalAgentTextPart(input.prompt)],
|
body: buildFallbackBody(promptBody, FALLBACK_AGENT),
|
||||||
},
|
})
|
||||||
}).catch((error) => {
|
task.agent = FALLBACK_AGENT
|
||||||
|
return
|
||||||
|
} catch (retryError) {
|
||||||
|
log("[background-agent] Fallback agent also failed:", retryError)
|
||||||
|
onTaskError(task, retryError instanceof Error ? retryError : new Error(String(retryError)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
log("[background-agent] promptAsync error:", error)
|
log("[background-agent] promptAsync error:", error)
|
||||||
onTaskError(task, error instanceof Error ? error : new Error(String(error)))
|
onTaskError(task, error instanceof Error ? error : new Error(String(error)))
|
||||||
})
|
})
|
||||||
@@ -228,21 +282,42 @@ export async function resumeTask(
|
|||||||
|
|
||||||
applySessionPromptParams(task.sessionID, task.model)
|
applySessionPromptParams(task.sessionID, task.model)
|
||||||
|
|
||||||
|
const resumeBody = {
|
||||||
|
agent: task.agent,
|
||||||
|
...(resumeModel ? { model: resumeModel } : {}),
|
||||||
|
...(resumeVariant ? { variant: resumeVariant } : {}),
|
||||||
|
tools: {
|
||||||
|
task: false,
|
||||||
|
call_omo_agent: true,
|
||||||
|
question: false,
|
||||||
|
...getAgentToolRestrictions(task.agent),
|
||||||
|
},
|
||||||
|
parts: [createInternalAgentTextPart(input.prompt)],
|
||||||
|
}
|
||||||
|
|
||||||
client.session.promptAsync({
|
client.session.promptAsync({
|
||||||
path: { id: task.sessionID },
|
path: { id: task.sessionID },
|
||||||
body: {
|
body: resumeBody,
|
||||||
agent: task.agent,
|
}).catch(async (error) => {
|
||||||
...(resumeModel ? { model: resumeModel } : {}),
|
if (isAgentNotFoundError(error) && task.agent !== FALLBACK_AGENT) {
|
||||||
...(resumeVariant ? { variant: resumeVariant } : {}),
|
log("[background-agent] Resume agent not found, retrying with fallback agent", {
|
||||||
tools: {
|
original: task.agent,
|
||||||
task: false,
|
fallback: FALLBACK_AGENT,
|
||||||
call_omo_agent: true,
|
taskId: task.id,
|
||||||
question: false,
|
})
|
||||||
...getAgentToolRestrictions(task.agent),
|
try {
|
||||||
},
|
await promptWithModelSuggestionRetry(client, {
|
||||||
parts: [createInternalAgentTextPart(input.prompt)],
|
path: { id: task.sessionID! },
|
||||||
},
|
body: buildFallbackBody(resumeBody, FALLBACK_AGENT),
|
||||||
}).catch((error) => {
|
})
|
||||||
|
task.agent = FALLBACK_AGENT
|
||||||
|
return
|
||||||
|
} catch (retryError) {
|
||||||
|
log("[background-agent] Resume fallback agent also failed:", retryError)
|
||||||
|
onTaskError(task, retryError instanceof Error ? retryError : new Error(String(retryError)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
log("[background-agent] resume prompt error:", error)
|
log("[background-agent] resume prompt error:", error)
|
||||||
onTaskError(task, error instanceof Error ? error : new Error(String(error)))
|
onTaskError(task, error instanceof Error ? error : new Error(String(error)))
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user