Merge pull request #1620 from potb/acp-json-error
fix: switch session.prompt() to promptAsync() — delegate broken in ACP
This commit is contained in:
commit
18c161a9cd
@ -171,6 +171,7 @@ function createBackgroundManager(): BackgroundManager {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -880,12 +881,14 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
|
|||||||
test("should skip notification when parent session is aborted", async () => {
|
test("should skip notification when parent session is aborted", async () => {
|
||||||
//#given
|
//#given
|
||||||
let promptCalled = false
|
let promptCalled = false
|
||||||
const client = {
|
const promptMock = async () => {
|
||||||
session: {
|
|
||||||
prompt: async () => {
|
|
||||||
promptCalled = true
|
promptCalled = true
|
||||||
return {}
|
return {}
|
||||||
},
|
}
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: promptMock,
|
||||||
|
promptAsync: promptMock,
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
messages: async () => {
|
messages: async () => {
|
||||||
const error = new Error("User aborted")
|
const error = new Error("User aborted")
|
||||||
@ -922,14 +925,16 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
|
|||||||
test("should swallow aborted error from prompt", async () => {
|
test("should swallow aborted error from prompt", async () => {
|
||||||
//#given
|
//#given
|
||||||
let promptCalled = false
|
let promptCalled = false
|
||||||
const client = {
|
const promptMock = async () => {
|
||||||
session: {
|
|
||||||
prompt: async () => {
|
|
||||||
promptCalled = true
|
promptCalled = true
|
||||||
const error = new Error("User aborted")
|
const error = new Error("User aborted")
|
||||||
error.name = "MessageAbortedError"
|
error.name = "MessageAbortedError"
|
||||||
throw error
|
throw error
|
||||||
},
|
}
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: promptMock,
|
||||||
|
promptAsync: promptMock,
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
@ -1060,6 +1065,7 @@ describe("BackgroundManager.tryCompleteTask", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async (args: { path: { id: string } }) => {
|
abort: async (args: { path: { id: string } }) => {
|
||||||
abortedSessionIDs.push(args.path.id)
|
abortedSessionIDs.push(args.path.id)
|
||||||
return {}
|
return {}
|
||||||
@ -1202,12 +1208,14 @@ describe("BackgroundManager.resume model persistence", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// given
|
// given
|
||||||
promptCalls = []
|
promptCalls = []
|
||||||
const client = {
|
const promptMock = async (args: { path: { id: string }; body: Record<string, unknown> }) => {
|
||||||
session: {
|
|
||||||
prompt: async (args: { path: { id: string }; body: Record<string, unknown> }) => {
|
|
||||||
promptCalls.push(args)
|
promptCalls.push(args)
|
||||||
return {}
|
return {}
|
||||||
},
|
}
|
||||||
|
const client = {
|
||||||
|
session: {
|
||||||
|
prompt: promptMock,
|
||||||
|
promptAsync: promptMock,
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1317,6 +1325,7 @@ describe("BackgroundManager - Non-blocking Queue Integration", () => {
|
|||||||
create: async () => ({ data: { id: `ses_${crypto.randomUUID()}` } }),
|
create: async () => ({ data: { id: `ses_${crypto.randomUUID()}` } }),
|
||||||
get: async () => ({ data: { directory: "/test/dir" } }),
|
get: async () => ({ data: { directory: "/test/dir" } }),
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
todo: async () => ({ data: [] }),
|
todo: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
@ -1875,6 +1884,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1907,6 +1917,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1939,6 +1950,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1975,6 +1987,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2009,6 +2022,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2044,6 +2058,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2095,6 +2110,7 @@ describe("BackgroundManager.checkAndInterruptStaleTasks", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2132,6 +2148,7 @@ describe("BackgroundManager.shutdown session abort", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async (args: { path: { id: string } }) => {
|
abort: async (args: { path: { id: string } }) => {
|
||||||
abortedSessionIDs.push(args.path.id)
|
abortedSessionIDs.push(args.path.id)
|
||||||
return {}
|
return {}
|
||||||
@ -2181,6 +2198,7 @@ describe("BackgroundManager.shutdown session abort", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async (args: { path: { id: string } }) => {
|
abort: async (args: { path: { id: string } }) => {
|
||||||
abortedSessionIDs.push(args.path.id)
|
abortedSessionIDs.push(args.path.id)
|
||||||
return {}
|
return {}
|
||||||
@ -2241,6 +2259,7 @@ describe("BackgroundManager.shutdown session abort", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2266,6 +2285,7 @@ describe("BackgroundManager.shutdown session abort", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2512,6 +2532,7 @@ describe("BackgroundManager.handleEvent - early session.idle deferral", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
messages: async (args: { path: { id: string } }) => {
|
messages: async (args: { path: { id: string } }) => {
|
||||||
messagesCalls.push(args.path.id)
|
messagesCalls.push(args.path.id)
|
||||||
@ -2571,6 +2592,7 @@ describe("BackgroundManager.handleEvent - early session.idle deferral", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
@ -2621,6 +2643,7 @@ describe("BackgroundManager.handleEvent - early session.idle deferral", () => {
|
|||||||
const client = {
|
const client = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({}),
|
prompt: async () => ({}),
|
||||||
|
promptAsync: async () => ({}),
|
||||||
abort: async () => ({}),
|
abort: async () => ({}),
|
||||||
messages: async () => {
|
messages: async () => {
|
||||||
messagesCallCount += 1
|
messagesCallCount += 1
|
||||||
|
|||||||
@ -310,7 +310,7 @@ export class BackgroundManager {
|
|||||||
promptLength: input.prompt.length,
|
promptLength: input.prompt.length,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use prompt() instead of promptAsync() to properly initialize agent loop (fire-and-forget)
|
// Fire-and-forget prompt via promptAsync (no response body needed)
|
||||||
// Include model if caller provided one (e.g., from Sisyphus category configs)
|
// Include model if caller provided one (e.g., from Sisyphus category configs)
|
||||||
// IMPORTANT: variant must be a top-level field in the body, NOT nested inside model
|
// IMPORTANT: variant must be a top-level field in the body, NOT nested inside model
|
||||||
// OpenCode's PromptInput schema expects: { model: { providerID, modelID }, variant: "max" }
|
// OpenCode's PromptInput schema expects: { model: { providerID, modelID }, variant: "max" }
|
||||||
@ -571,7 +571,7 @@ export class BackgroundManager {
|
|||||||
promptLength: input.prompt.length,
|
promptLength: input.prompt.length,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Use prompt() instead of promptAsync() to properly initialize agent loop
|
// Fire-and-forget prompt via promptAsync (no response body needed)
|
||||||
// Include model if task has one (preserved from original launch with category config)
|
// Include model if task has one (preserved from original launch with category config)
|
||||||
// variant must be top-level in body, not nested inside model (OpenCode PromptInput schema)
|
// variant must be top-level in body, not nested inside model (OpenCode PromptInput schema)
|
||||||
const resumeModel = existingTask.model
|
const resumeModel = existingTask.model
|
||||||
@ -579,7 +579,7 @@ export class BackgroundManager {
|
|||||||
: undefined
|
: undefined
|
||||||
const resumeVariant = existingTask.model?.variant
|
const resumeVariant = existingTask.model?.variant
|
||||||
|
|
||||||
this.client.session.prompt({
|
this.client.session.promptAsync({
|
||||||
path: { id: existingTask.sessionID },
|
path: { id: existingTask.sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: existingTask.agent,
|
agent: existingTask.agent,
|
||||||
@ -1198,7 +1198,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.client.session.prompt({
|
await this.client.session.promptAsync({
|
||||||
path: { id: task.parentSessionID },
|
path: { id: task.parentSessionID },
|
||||||
body: {
|
body: {
|
||||||
noReply: !allComplete,
|
noReply: !allComplete,
|
||||||
|
|||||||
@ -240,7 +240,7 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
|
|||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.session.prompt({
|
await client.session.promptAsync({
|
||||||
path: { id: task.parentSessionID },
|
path: { id: task.parentSessionID },
|
||||||
body: {
|
body: {
|
||||||
noReply: !allComplete,
|
noReply: !allComplete,
|
||||||
|
|||||||
@ -221,7 +221,7 @@ export async function resumeTask(
|
|||||||
: undefined
|
: undefined
|
||||||
const resumeVariant = task.model?.variant
|
const resumeVariant = task.model?.variant
|
||||||
|
|
||||||
client.session.prompt({
|
client.session.promptAsync({
|
||||||
path: { id: task.sessionID },
|
path: { id: task.sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: task.agent,
|
agent: task.agent,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ describe("atlas hook", () => {
|
|||||||
client: {
|
client: {
|
||||||
session: {
|
session: {
|
||||||
prompt: promptMock,
|
prompt: promptMock,
|
||||||
|
promptAsync: promptMock,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
_promptMock: promptMock,
|
_promptMock: promptMock,
|
||||||
|
|||||||
@ -484,7 +484,7 @@ export function createAtlasHook(
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.client.session.prompt({
|
await ctx.client.session.promptAsync({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: agent ?? "atlas",
|
agent: agent ?? "atlas",
|
||||||
|
|||||||
@ -364,7 +364,7 @@ export function createRalphLoopHook(
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
await ctx.client.session.prompt({
|
await ctx.client.session.promptAsync({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: {
|
||||||
...(agent !== undefined ? { agent } : {}),
|
...(agent !== undefined ? { agent } : {}),
|
||||||
|
|||||||
@ -75,7 +75,7 @@ function extractResumeConfig(userMessage: MessageData | undefined, sessionID: st
|
|||||||
|
|
||||||
async function resumeSession(client: Client, config: ResumeConfig): Promise<boolean> {
|
async function resumeSession(client: Client, config: ResumeConfig): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await client.session.prompt({
|
await client.session.promptAsync({
|
||||||
path: { id: config.sessionID },
|
path: { id: config.sessionID },
|
||||||
body: {
|
body: {
|
||||||
parts: [{ type: "text", text: RECOVERY_RESUME_TEXT }],
|
parts: [{ type: "text", text: RECOVERY_RESUME_TEXT }],
|
||||||
@ -185,7 +185,7 @@ async function recoverToolResultMissing(
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.session.prompt({
|
await client.session.promptAsync({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
// @ts-expect-error - SDK types may not include tool_result parts
|
// @ts-expect-error - SDK types may not include tool_result parts
|
||||||
body: { parts: toolResultParts },
|
body: { parts: toolResultParts },
|
||||||
|
|||||||
@ -152,6 +152,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: {
|
tui: {
|
||||||
showToast: async (opts: any) => {
|
showToast: async (opts: any) => {
|
||||||
@ -986,6 +995,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: { showToast: async () => ({}) },
|
tui: { showToast: async () => ({}) },
|
||||||
},
|
},
|
||||||
@ -1038,6 +1056,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: { showToast: async () => ({}) },
|
tui: { showToast: async () => ({}) },
|
||||||
},
|
},
|
||||||
@ -1082,6 +1109,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: { showToast: async () => ({}) },
|
tui: { showToast: async () => ({}) },
|
||||||
},
|
},
|
||||||
@ -1128,6 +1164,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: { showToast: async () => ({}) },
|
tui: { showToast: async () => ({}) },
|
||||||
},
|
},
|
||||||
@ -1174,6 +1219,15 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
})
|
})
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
|
promptAsync: async (opts: any) => {
|
||||||
|
promptCalls.push({
|
||||||
|
sessionID: opts.path.id,
|
||||||
|
agent: opts.body.agent,
|
||||||
|
model: opts.body.model,
|
||||||
|
text: opts.body.parts[0].text,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tui: { showToast: async () => ({}) },
|
tui: { showToast: async () => ({}) },
|
||||||
},
|
},
|
||||||
|
|||||||
@ -245,7 +245,7 @@ ${todoList}`
|
|||||||
try {
|
try {
|
||||||
log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: agentName, model, incompleteCount: freshIncompleteCount })
|
log(`[${HOOK_NAME}] Injecting continuation`, { sessionID, agent: agentName, model, incompleteCount: freshIncompleteCount })
|
||||||
|
|
||||||
await ctx.client.session.prompt({
|
await ctx.client.session.promptAsync({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: agentName,
|
agent: agentName,
|
||||||
|
|||||||
@ -25,6 +25,15 @@ type BabysitterContext = {
|
|||||||
}
|
}
|
||||||
query?: { directory?: string }
|
query?: { directory?: string }
|
||||||
}) => Promise<unknown>
|
}) => Promise<unknown>
|
||||||
|
promptAsync: (args: {
|
||||||
|
path: { id: string }
|
||||||
|
body: {
|
||||||
|
parts: Array<{ type: "text"; text: string }>
|
||||||
|
agent?: string
|
||||||
|
model?: { providerID: string; modelID: string }
|
||||||
|
}
|
||||||
|
query?: { directory?: string }
|
||||||
|
}) => Promise<unknown>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +227,7 @@ export function createUnstableAgentBabysitterHook(ctx: BabysitterContext, option
|
|||||||
const { agent, model } = await resolveMainSessionTarget(ctx, mainSessionID)
|
const { agent, model } = await resolveMainSessionTarget(ctx, mainSessionID)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.client.session.prompt({
|
await ctx.client.session.promptAsync({
|
||||||
path: { id: mainSessionID },
|
path: { id: mainSessionID },
|
||||||
body: {
|
body: {
|
||||||
...(agent ? { agent } : {}),
|
...(agent ? { agent } : {}),
|
||||||
|
|||||||
@ -388,7 +388,10 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
prompt: async (args) => {
|
prompt: async (args) => {
|
||||||
await ctx.client.session.prompt(args);
|
await ctx.client.session.promptAsync(args);
|
||||||
|
},
|
||||||
|
promptAsync: async (args) => {
|
||||||
|
await ctx.client.session.promptAsync(args);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@ -212,9 +212,9 @@ describe("parseModelSuggestion", () => {
|
|||||||
|
|
||||||
describe("promptWithModelSuggestionRetry", () => {
|
describe("promptWithModelSuggestionRetry", () => {
|
||||||
it("should succeed on first try without retry", async () => {
|
it("should succeed on first try without retry", async () => {
|
||||||
// given a client where prompt succeeds
|
// given a client where promptAsync succeeds
|
||||||
const promptMock = mock(() => Promise.resolve())
|
const promptMock = mock(() => Promise.resolve())
|
||||||
const client = { session: { prompt: promptMock } }
|
const client = { session: { promptAsync: promptMock } }
|
||||||
|
|
||||||
// when calling promptWithModelSuggestionRetry
|
// when calling promptWithModelSuggestionRetry
|
||||||
await promptWithModelSuggestionRetry(client as any, {
|
await promptWithModelSuggestionRetry(client as any, {
|
||||||
@ -225,14 +225,13 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// then should call prompt exactly once
|
// then should call promptAsync exactly once
|
||||||
expect(promptMock).toHaveBeenCalledTimes(1)
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should retry with suggestion on model-not-found error", async () => {
|
it("should throw error from promptAsync directly on model-not-found error", async () => {
|
||||||
// given a client that fails first with model-not-found, then succeeds
|
// given a client that fails with model-not-found error
|
||||||
const promptMock = mock()
|
const promptMock = mock().mockRejectedValueOnce({
|
||||||
.mockRejectedValueOnce({
|
|
||||||
name: "ProviderModelNotFoundError",
|
name: "ProviderModelNotFoundError",
|
||||||
data: {
|
data: {
|
||||||
providerID: "anthropic",
|
providerID: "anthropic",
|
||||||
@ -240,11 +239,12 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
suggestions: ["claude-sonnet-4"],
|
suggestions: ["claude-sonnet-4"],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.mockResolvedValueOnce(undefined)
|
const client = { session: { promptAsync: promptMock } }
|
||||||
const client = { session: { prompt: promptMock } }
|
|
||||||
|
|
||||||
// when calling promptWithModelSuggestionRetry
|
// when calling promptWithModelSuggestionRetry
|
||||||
await promptWithModelSuggestionRetry(client as any, {
|
// then should throw the error without retrying
|
||||||
|
await expect(
|
||||||
|
promptWithModelSuggestionRetry(client as any, {
|
||||||
path: { id: "session-1" },
|
path: { id: "session-1" },
|
||||||
body: {
|
body: {
|
||||||
agent: "explore",
|
agent: "explore",
|
||||||
@ -252,21 +252,17 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
model: { providerID: "anthropic", modelID: "claude-sonet-4" },
|
model: { providerID: "anthropic", modelID: "claude-sonet-4" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
).rejects.toThrow()
|
||||||
|
|
||||||
// then should call prompt twice - first with original, then with suggestion
|
// and should call promptAsync only once
|
||||||
expect(promptMock).toHaveBeenCalledTimes(2)
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
const retryCall = promptMock.mock.calls[1][0]
|
|
||||||
expect(retryCall.body.model).toEqual({
|
|
||||||
providerID: "anthropic",
|
|
||||||
modelID: "claude-sonnet-4",
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw original error when no suggestion available", async () => {
|
it("should throw original error when no suggestion available", async () => {
|
||||||
// given a client that fails with a non-model-not-found error
|
// given a client that fails with a non-model-not-found error
|
||||||
const originalError = new Error("Connection refused")
|
const originalError = new Error("Connection refused")
|
||||||
const promptMock = mock().mockRejectedValueOnce(originalError)
|
const promptMock = mock().mockRejectedValueOnce(originalError)
|
||||||
const client = { session: { prompt: promptMock } }
|
const client = { session: { promptAsync: promptMock } }
|
||||||
|
|
||||||
// when calling promptWithModelSuggestionRetry
|
// when calling promptWithModelSuggestionRetry
|
||||||
// then should throw the original error
|
// then should throw the original error
|
||||||
@ -283,50 +279,32 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
expect(promptMock).toHaveBeenCalledTimes(1)
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw original error when retry also fails", async () => {
|
it("should throw error from promptAsync directly", async () => {
|
||||||
// given a client that fails with model-not-found, retry also fails
|
// given a client that fails with an error
|
||||||
const modelNotFoundError = {
|
const error = new Error("Still not found")
|
||||||
name: "ProviderModelNotFoundError",
|
const promptMock = mock().mockRejectedValueOnce(error)
|
||||||
data: {
|
const client = { session: { promptAsync: promptMock } }
|
||||||
providerID: "anthropic",
|
|
||||||
modelID: "claude-sonet-4",
|
|
||||||
suggestions: ["claude-sonnet-4"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const retryError = new Error("Still not found")
|
|
||||||
const promptMock = mock()
|
|
||||||
.mockRejectedValueOnce(modelNotFoundError)
|
|
||||||
.mockRejectedValueOnce(retryError)
|
|
||||||
const client = { session: { prompt: promptMock } }
|
|
||||||
|
|
||||||
// when calling promptWithModelSuggestionRetry
|
// when calling promptWithModelSuggestionRetry
|
||||||
// then should throw the retry error (not the original)
|
// then should throw the error
|
||||||
await expect(
|
await expect(
|
||||||
promptWithModelSuggestionRetry(client as any, {
|
promptWithModelSuggestionRetry(client as any, {
|
||||||
path: { id: "session-1" },
|
path: { id: "session-1" },
|
||||||
body: {
|
body: {
|
||||||
parts: [{ type: "text", text: "hello" }],
|
parts: [{ type: "text", text: "hello" }],
|
||||||
model: { providerID: "anthropic", modelID: "claude-sonet-4" },
|
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
).rejects.toThrow("Still not found")
|
).rejects.toThrow("Still not found")
|
||||||
|
|
||||||
expect(promptMock).toHaveBeenCalledTimes(2)
|
// and should call promptAsync only once
|
||||||
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should preserve other body fields during retry", async () => {
|
it("should pass all body fields through to promptAsync", async () => {
|
||||||
// given a client that fails first with model-not-found
|
// given a client where promptAsync succeeds
|
||||||
const promptMock = mock()
|
const promptMock = mock().mockResolvedValueOnce(undefined)
|
||||||
.mockRejectedValueOnce({
|
const client = { session: { promptAsync: promptMock } }
|
||||||
name: "ProviderModelNotFoundError",
|
|
||||||
data: {
|
|
||||||
providerID: "anthropic",
|
|
||||||
modelID: "claude-sonet-4",
|
|
||||||
suggestions: ["claude-sonnet-4"],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.mockResolvedValueOnce(undefined)
|
|
||||||
const client = { session: { prompt: promptMock } }
|
|
||||||
|
|
||||||
// when calling with additional body fields
|
// when calling with additional body fields
|
||||||
await promptWithModelSuggestionRetry(client as any, {
|
await promptWithModelSuggestionRetry(client as any, {
|
||||||
@ -336,57 +314,56 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
system: "You are a helpful agent",
|
system: "You are a helpful agent",
|
||||||
tools: { task: false },
|
tools: { task: false },
|
||||||
parts: [{ type: "text", text: "hello" }],
|
parts: [{ type: "text", text: "hello" }],
|
||||||
model: { providerID: "anthropic", modelID: "claude-sonet-4" },
|
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
|
||||||
variant: "max",
|
variant: "max",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// then retry call should preserve all fields except corrected model
|
// then call should pass all fields through unchanged
|
||||||
const retryCall = promptMock.mock.calls[1][0]
|
const call = promptMock.mock.calls[0][0]
|
||||||
expect(retryCall.body.agent).toBe("explore")
|
expect(call.body.agent).toBe("explore")
|
||||||
expect(retryCall.body.system).toBe("You are a helpful agent")
|
expect(call.body.system).toBe("You are a helpful agent")
|
||||||
expect(retryCall.body.tools).toEqual({ task: false })
|
expect(call.body.tools).toEqual({ task: false })
|
||||||
expect(retryCall.body.variant).toBe("max")
|
expect(call.body.variant).toBe("max")
|
||||||
expect(retryCall.body.model).toEqual({
|
expect(call.body.model).toEqual({
|
||||||
providerID: "anthropic",
|
providerID: "anthropic",
|
||||||
modelID: "claude-sonnet-4",
|
modelID: "claude-sonnet-4",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should handle string error message with suggestion", async () => {
|
it("should throw string error message from promptAsync", async () => {
|
||||||
// given a client that fails with a string error containing suggestion
|
// given a client that fails with a string error
|
||||||
const promptMock = mock()
|
const promptMock = mock().mockRejectedValueOnce(
|
||||||
.mockRejectedValueOnce(
|
|
||||||
new Error("Model not found: anthropic/claude-sonet-4. Did you mean: claude-sonnet-4?")
|
new Error("Model not found: anthropic/claude-sonet-4. Did you mean: claude-sonnet-4?")
|
||||||
)
|
)
|
||||||
.mockResolvedValueOnce(undefined)
|
const client = { session: { promptAsync: promptMock } }
|
||||||
const client = { session: { prompt: promptMock } }
|
|
||||||
|
|
||||||
// when calling promptWithModelSuggestionRetry
|
// when calling promptWithModelSuggestionRetry
|
||||||
await promptWithModelSuggestionRetry(client as any, {
|
// then should throw the error
|
||||||
|
await expect(
|
||||||
|
promptWithModelSuggestionRetry(client as any, {
|
||||||
path: { id: "session-1" },
|
path: { id: "session-1" },
|
||||||
body: {
|
body: {
|
||||||
parts: [{ type: "text", text: "hello" }],
|
parts: [{ type: "text", text: "hello" }],
|
||||||
model: { providerID: "anthropic", modelID: "claude-sonet-4" },
|
model: { providerID: "anthropic", modelID: "claude-sonnet-4" },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
).rejects.toThrow()
|
||||||
|
|
||||||
// then should retry with suggested model
|
// and should call promptAsync only once
|
||||||
expect(promptMock).toHaveBeenCalledTimes(2)
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
const retryCall = promptMock.mock.calls[1][0]
|
|
||||||
expect(retryCall.body.model.modelID).toBe("claude-sonnet-4")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should not retry when no model in original request", async () => {
|
it("should throw error when no model in original request", async () => {
|
||||||
// given a client that fails with model-not-found but original has no model param
|
// given a client that fails with an error
|
||||||
const modelNotFoundError = new Error(
|
const modelNotFoundError = new Error(
|
||||||
"Model not found: anthropic/claude-sonet-4. Did you mean: claude-sonnet-4?"
|
"Model not found: anthropic/claude-sonet-4. Did you mean: claude-sonnet-4?"
|
||||||
)
|
)
|
||||||
const promptMock = mock().mockRejectedValueOnce(modelNotFoundError)
|
const promptMock = mock().mockRejectedValueOnce(modelNotFoundError)
|
||||||
const client = { session: { prompt: promptMock } }
|
const client = { session: { promptAsync: promptMock } }
|
||||||
|
|
||||||
// when calling without model in body
|
// when calling without model in body
|
||||||
// then should throw without retrying
|
// then should throw the error
|
||||||
await expect(
|
await expect(
|
||||||
promptWithModelSuggestionRetry(client as any, {
|
promptWithModelSuggestionRetry(client as any, {
|
||||||
path: { id: "session-1" },
|
path: { id: "session-1" },
|
||||||
@ -396,6 +373,7 @@ describe("promptWithModelSuggestionRetry", () => {
|
|||||||
})
|
})
|
||||||
).rejects.toThrow()
|
).rejects.toThrow()
|
||||||
|
|
||||||
|
// and should call promptAsync only once
|
||||||
expect(promptMock).toHaveBeenCalledTimes(1)
|
expect(promptMock).toHaveBeenCalledTimes(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -84,28 +84,7 @@ export async function promptWithModelSuggestionRetry(
|
|||||||
client: Client,
|
client: Client,
|
||||||
args: PromptArgs,
|
args: PromptArgs,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
// NOTE: Model suggestion retry removed — promptAsync returns 204 immediately,
|
||||||
await client.session.prompt(args as Parameters<typeof client.session.prompt>[0])
|
// model errors happen asynchronously server-side and cannot be caught here
|
||||||
} catch (error) {
|
await client.session.promptAsync(args as Parameters<typeof client.session.promptAsync>[0])
|
||||||
const suggestion = parseModelSuggestion(error)
|
|
||||||
if (!suggestion || !args.body.model) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
log("[model-suggestion-retry] Model not found, retrying with suggestion", {
|
|
||||||
original: `${suggestion.providerID}/${suggestion.modelID}`,
|
|
||||||
suggested: suggestion.suggestion,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.session.prompt({
|
|
||||||
...args,
|
|
||||||
body: {
|
|
||||||
...args.body,
|
|
||||||
model: {
|
|
||||||
providerID: suggestion.providerID,
|
|
||||||
modelID: suggestion.suggestion,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as Parameters<typeof client.session.prompt>[0])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -220,7 +220,7 @@ Original error: ${createResult.error}`
|
|||||||
log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100))
|
log(`[call_omo_agent] Prompt text:`, args.prompt.substring(0, 100))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ctx.client.session.prompt({
|
await (ctx.client.session as any).promptAsync({
|
||||||
path: { id: sessionID },
|
path: { id: sessionID },
|
||||||
body: {
|
body: {
|
||||||
agent: args.subagent_type,
|
agent: args.subagent_type,
|
||||||
|
|||||||
@ -211,7 +211,7 @@ export async function executeSyncContinuation(
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.session.prompt({
|
await (client.session as any).promptAsync({
|
||||||
path: { id: args.session_id! },
|
path: { id: args.session_id! },
|
||||||
body: {
|
body: {
|
||||||
...(resumeAgent !== undefined ? { agent: resumeAgent } : {}),
|
...(resumeAgent !== undefined ? { agent: resumeAgent } : {}),
|
||||||
|
|||||||
@ -229,6 +229,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -281,6 +282,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -326,6 +328,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -391,6 +394,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -679,6 +683,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -743,6 +748,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -787,6 +793,11 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
@ -794,10 +805,8 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_sync_default_variant" } }),
|
create: async () => ({ data: { id: "ses_sync_default_variant" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -851,6 +860,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -891,6 +901,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -927,16 +938,20 @@ describe("sisyphus-task", () => {
|
|||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -995,6 +1010,7 @@ describe("sisyphus-task", () => {
|
|||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
@ -1058,6 +1074,7 @@ describe("sisyphus-task", () => {
|
|||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [],
|
data: [],
|
||||||
}),
|
}),
|
||||||
@ -1104,13 +1121,16 @@ describe("sisyphus-task", () => {
|
|||||||
launch: async () => ({}),
|
launch: async () => ({}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const promptMock = async () => {
|
||||||
|
throw new Error("JSON Parse error: Unexpected EOF")
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_sync_error_test" } }),
|
create: async () => ({ data: { id: "ses_sync_error_test" } }),
|
||||||
prompt: async () => {
|
prompt: promptMock,
|
||||||
throw new Error("JSON Parse error: Unexpected EOF")
|
promptAsync: promptMock,
|
||||||
},
|
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -1164,6 +1184,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_sync_success" } }),
|
create: async () => ({ data: { id: "ses_sync_success" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
@ -1217,13 +1238,16 @@ describe("sisyphus-task", () => {
|
|||||||
launch: async () => ({}),
|
launch: async () => ({}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const promptMock = async () => {
|
||||||
|
throw new Error("Cannot read property 'name' of undefined agent.name")
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_agent_notfound" } }),
|
create: async () => ({ data: { id: "ses_agent_notfound" } }),
|
||||||
prompt: async () => {
|
prompt: promptMock,
|
||||||
throw new Error("Cannot read property 'name' of undefined agent.name")
|
promptAsync: promptMock,
|
||||||
},
|
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -1268,14 +1292,18 @@ describe("sisyphus-task", () => {
|
|||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_sync_model" } }),
|
create: async () => ({ data: { id: "ses_sync_model" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -1344,6 +1372,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_unstable_gemini" } }),
|
create: async () => ({ data: { id: "ses_unstable_gemini" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Gemini task completed successfully" }] }
|
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Gemini task completed successfully" }] }
|
||||||
@ -1407,6 +1436,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1466,6 +1496,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_unstable_minimax" } }),
|
create: async () => ({ data: { id: "ses_unstable_minimax" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Minimax task completed successfully" }] }
|
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Minimax task completed successfully" }] }
|
||||||
@ -1523,16 +1554,19 @@ describe("sisyphus-task", () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const promptMock = async () => {
|
||||||
|
promptCalled = true
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_sync_non_gemini" } }),
|
create: async () => ({ data: { id: "ses_sync_non_gemini" } }),
|
||||||
prompt: async () => {
|
prompt: promptMock,
|
||||||
promptCalled = true
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done sync" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done sync" }] }]
|
||||||
}),
|
}),
|
||||||
@ -1597,6 +1631,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_artistry_gemini" } }),
|
create: async () => ({ data: { id: "ses_artistry_gemini" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Artistry result here" }] }
|
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Artistry result here" }] }
|
||||||
@ -1662,6 +1697,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_writing_gemini" } }),
|
create: async () => ({ data: { id: "ses_writing_gemini" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Writing result here" }] }
|
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Writing result here" }] }
|
||||||
@ -1726,6 +1762,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_custom_unstable" } }),
|
create: async () => ({ data: { id: "ses_custom_unstable" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Custom unstable result" }] }
|
{ info: { role: "assistant", time: { created: Date.now() } }, parts: [{ type: "text", text: "Custom unstable result" }] }
|
||||||
@ -1858,11 +1895,12 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: "opencode/kimi-k2.5-free" } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
model: { list: async () => [] },
|
model: { list: async () => [] },
|
||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -1870,6 +1908,9 @@ describe("sisyphus-task", () => {
|
|||||||
const tool = createDelegateTask({
|
const tool = createDelegateTask({
|
||||||
manager: mockManager,
|
manager: mockManager,
|
||||||
client: mockClient,
|
client: mockClient,
|
||||||
|
userCategories: {
|
||||||
|
"fallback-test": { model: "anthropic/claude-opus-4-6" },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const toolContext = {
|
const toolContext = {
|
||||||
@ -1980,6 +2021,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2025,16 +2067,20 @@ describe("sisyphus-task", () => {
|
|||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_browser_provider" } }),
|
create: async () => ({ data: { id: "ses_browser_provider" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -2086,6 +2132,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_no_browser_provider" } }),
|
create: async () => ({ data: { id: "ses_no_browser_provider" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -2531,6 +2578,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -2577,6 +2625,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_prometheus_allowed" } }),
|
create: async () => ({ data: { id: "ses_prometheus_allowed" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created successfully" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created successfully" }] }]
|
||||||
}),
|
}),
|
||||||
@ -2625,6 +2674,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "test-session" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
status: async () => ({ data: {} }),
|
status: async () => ({ data: {} }),
|
||||||
},
|
},
|
||||||
@ -2691,6 +2741,7 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "ses_explore_model" } }),
|
create: async () => ({ data: { id: "ses_explore_model" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -2733,6 +2784,11 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: {
|
app: {
|
||||||
agents: async () => ({
|
agents: async () => ({
|
||||||
@ -2745,10 +2801,8 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_oracle_model" } }),
|
create: async () => ({ data: { id: "ses_oracle_model" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Consultation done" }] }],
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Consultation done" }] }],
|
||||||
}),
|
}),
|
||||||
@ -2794,6 +2848,11 @@ describe("sisyphus-task", () => {
|
|||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: {
|
app: {
|
||||||
agents: async () => ({
|
agents: async () => ({
|
||||||
@ -2806,10 +2865,8 @@ describe("sisyphus-task", () => {
|
|||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_no_model_agent" } }),
|
create: async () => ({ data: { id: "ses_no_model_agent" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }],
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Done" }] }],
|
||||||
}),
|
}),
|
||||||
@ -2853,16 +2910,20 @@ describe("sisyphus-task", () => {
|
|||||||
let promptBody: any
|
let promptBody: any
|
||||||
|
|
||||||
const mockManager = { launch: async () => ({}) }
|
const mockManager = { launch: async () => ({}) }
|
||||||
|
|
||||||
|
const promptMock = async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
}
|
||||||
|
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
app: { agents: async () => ({ data: [{ name: "prometheus", mode: "subagent" }] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_prometheus_delegate" } }),
|
create: async () => ({ data: { id: "ses_prometheus_delegate" } }),
|
||||||
prompt: async (input: any) => {
|
prompt: promptMock,
|
||||||
promptBody = input.body
|
promptAsync: promptMock,
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Plan created" }] }]
|
||||||
}),
|
}),
|
||||||
@ -2914,6 +2975,10 @@ describe("sisyphus-task", () => {
|
|||||||
promptBody = input.body
|
promptBody = input.body
|
||||||
return { data: {} }
|
return { data: {} }
|
||||||
},
|
},
|
||||||
|
promptAsync: async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Consultation done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Consultation done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -2968,6 +3033,7 @@ describe("sisyphus-task", () => {
|
|||||||
return { data: { id: "ses_title_test" } }
|
return { data: { id: "ses_title_test" } }
|
||||||
},
|
},
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "done" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "done" }] }]
|
||||||
}),
|
}),
|
||||||
@ -3016,6 +3082,7 @@ describe("sisyphus-task", () => {
|
|||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_metadata_test" } }),
|
create: async () => ({ data: { id: "ses_metadata_test" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Task completed" }] }]
|
data: [{ info: { role: "assistant" }, parts: [{ type: "text", text: "Task completed" }] }]
|
||||||
}),
|
}),
|
||||||
@ -3069,10 +3136,11 @@ describe("sisyphus-task", () => {
|
|||||||
const mockClient = {
|
const mockClient = {
|
||||||
app: { agents: async () => ({ data: [] }) },
|
app: { agents: async () => ({ data: [] }) },
|
||||||
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
|
||||||
model: { list: async () => [{ id: SYSTEM_DEFAULT_MODEL }] },
|
model: { list: async () => [] },
|
||||||
session: {
|
session: {
|
||||||
create: async () => ({ data: { id: "ses_bg_metadata" } }),
|
create: async () => ({ data: { id: "test-session" } }),
|
||||||
prompt: async () => ({ data: {} }),
|
prompt: async () => ({ data: {} }),
|
||||||
|
promptAsync: async () => ({ data: {} }),
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -3080,6 +3148,9 @@ describe("sisyphus-task", () => {
|
|||||||
const tool = createDelegateTask({
|
const tool = createDelegateTask({
|
||||||
manager: mockManager,
|
manager: mockManager,
|
||||||
client: mockClient,
|
client: mockClient,
|
||||||
|
userCategories: {
|
||||||
|
"sisyphus-junior": { model: "anthropic/claude-sonnet-4-5" },
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const toolContext = {
|
const toolContext = {
|
||||||
|
|||||||
@ -111,17 +111,19 @@ describe("look-at tool", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe("createLookAt error handling", () => {
|
describe("createLookAt error handling", () => {
|
||||||
// given JSON parse error occurs in session.prompt
|
// given JSON parse error occurs in session.promptAsync
|
||||||
// when LookAt tool executed
|
// when LookAt tool executed
|
||||||
// then return user-friendly error message
|
// then error propagates (band-aid removed since root cause fixed by promptAsync migration)
|
||||||
test("handles JSON parse error from session.prompt gracefully", async () => {
|
test("propagates JSON parse error from session.promptAsync", async () => {
|
||||||
|
const throwingMock = async () => {
|
||||||
|
throw new Error("JSON Parse error: Unexpected EOF")
|
||||||
|
}
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_test_json_error" } }),
|
create: async () => ({ data: { id: "ses_test_json_error" } }),
|
||||||
prompt: async () => {
|
prompt: throwingMock,
|
||||||
throw new Error("JSON Parse error: Unexpected EOF")
|
promptAsync: throwingMock,
|
||||||
},
|
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -142,28 +144,24 @@ describe("look-at tool", () => {
|
|||||||
ask: async () => {},
|
ask: async () => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await tool.execute(
|
await expect(
|
||||||
{ file_path: "/test/file.png", goal: "analyze image" },
|
tool.execute({ file_path: "/test/file.png", goal: "analyze image" }, toolContext)
|
||||||
toolContext
|
).rejects.toThrow("JSON Parse error: Unexpected EOF")
|
||||||
)
|
|
||||||
|
|
||||||
expect(result).toContain("Error: Failed to analyze")
|
|
||||||
expect(result).toContain("malformed response")
|
|
||||||
expect(result).toContain("multimodal-looker")
|
|
||||||
expect(result).toContain("image/png")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// given generic error occurs in session.prompt
|
// given generic error occurs in session.promptAsync
|
||||||
// when LookAt tool executed
|
// when LookAt tool executed
|
||||||
// then return error including original error message
|
// then error propagates
|
||||||
test("handles generic prompt error gracefully", async () => {
|
test("propagates generic prompt error", async () => {
|
||||||
|
const throwingMock = async () => {
|
||||||
|
throw new Error("Network connection failed")
|
||||||
|
}
|
||||||
const mockClient = {
|
const mockClient = {
|
||||||
session: {
|
session: {
|
||||||
get: async () => ({ data: { directory: "/project" } }),
|
get: async () => ({ data: { directory: "/project" } }),
|
||||||
create: async () => ({ data: { id: "ses_test_generic_error" } }),
|
create: async () => ({ data: { id: "ses_test_generic_error" } }),
|
||||||
prompt: async () => {
|
prompt: throwingMock,
|
||||||
throw new Error("Network connection failed")
|
promptAsync: throwingMock,
|
||||||
},
|
|
||||||
messages: async () => ({ data: [] }),
|
messages: async () => ({ data: [] }),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -184,13 +182,9 @@ describe("look-at tool", () => {
|
|||||||
ask: async () => {},
|
ask: async () => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await tool.execute(
|
await expect(
|
||||||
{ file_path: "/test/file.pdf", goal: "extract text" },
|
tool.execute({ file_path: "/test/file.pdf", goal: "extract text" }, toolContext)
|
||||||
toolContext
|
).rejects.toThrow("Network connection failed")
|
||||||
)
|
|
||||||
|
|
||||||
expect(result).toContain("Error: Failed to send prompt")
|
|
||||||
expect(result).toContain("Network connection failed")
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -220,6 +214,10 @@ describe("look-at tool", () => {
|
|||||||
promptBody = input.body
|
promptBody = input.body
|
||||||
return { data: {} }
|
return { data: {} }
|
||||||
},
|
},
|
||||||
|
promptAsync: async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "done" }] },
|
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "done" }] },
|
||||||
@ -274,6 +272,10 @@ describe("look-at tool", () => {
|
|||||||
promptBody = input.body
|
promptBody = input.body
|
||||||
return { data: {} }
|
return { data: {} }
|
||||||
},
|
},
|
||||||
|
promptAsync: async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "analyzed" }] },
|
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "analyzed" }] },
|
||||||
@ -327,6 +329,10 @@ describe("look-at tool", () => {
|
|||||||
promptBody = input.body
|
promptBody = input.body
|
||||||
return { data: {} }
|
return { data: {} }
|
||||||
},
|
},
|
||||||
|
promptAsync: async (input: any) => {
|
||||||
|
promptBody = input.body
|
||||||
|
return { data: {} }
|
||||||
|
},
|
||||||
messages: async () => ({
|
messages: async () => ({
|
||||||
data: [
|
data: [
|
||||||
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "analyzed" }] },
|
{ info: { role: "assistant", time: { created: 1 } }, parts: [{ type: "text", text: "analyzed" }] },
|
||||||
|
|||||||
@ -245,27 +245,7 @@ Original error: ${createResult.error}`
|
|||||||
const errorMessage = promptError instanceof Error ? promptError.message : String(promptError)
|
const errorMessage = promptError instanceof Error ? promptError.message : String(promptError)
|
||||||
log(`[look_at] Prompt error:`, promptError)
|
log(`[look_at] Prompt error:`, promptError)
|
||||||
|
|
||||||
const isJsonParseError = errorMessage.includes("JSON") && (errorMessage.includes("EOF") || errorMessage.includes("parse"))
|
throw promptError
|
||||||
if (isJsonParseError) {
|
|
||||||
return `Error: Failed to analyze ${isBase64Input ? "image" : "file"} - received malformed response from multimodal-looker agent.
|
|
||||||
|
|
||||||
This typically occurs when:
|
|
||||||
1. The multimodal-looker model is not available or not connected
|
|
||||||
2. The model does not support this ${isBase64Input ? "image format" : `file type (${mimeType})`}
|
|
||||||
3. The API returned an empty or truncated response
|
|
||||||
|
|
||||||
${isBase64Input ? "Source: clipboard/pasted image" : `File: ${args.file_path}`}
|
|
||||||
MIME type: ${mimeType}
|
|
||||||
|
|
||||||
Try:
|
|
||||||
- Ensure a vision-capable model (e.g., gemini-3-flash, gpt-5.2) is available
|
|
||||||
- Check provider connections in opencode settings
|
|
||||||
${!isBase64Input ? "- For text files like .md, .txt, use the Read tool instead" : ""}
|
|
||||||
|
|
||||||
Original error: ${errorMessage}`
|
|
||||||
}
|
|
||||||
|
|
||||||
return `Error: Failed to send prompt to multimodal-looker agent: ${errorMessage}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`[look_at] Prompt sent, fetching messages...`)
|
log(`[look_at] Prompt sent, fetching messages...`)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user