fix(background-agent): pass model on resume to preserve category config

The resume method was not passing the stored model from the task,
causing Sisyphus-Junior to revert to the default model when resumed.

This fix adds the model to the prompt body in resume(), matching
the existing behavior in launch().

Fixes #826
This commit is contained in:
ewjin
2026-01-16 18:21:31 +09:00
parent e23ce11df9
commit 8402b550df
2 changed files with 90 additions and 1 deletions

View File

@@ -1036,6 +1036,93 @@ describe("BackgroundManager.resume concurrency key", () => {
})
})
describe("BackgroundManager.resume model persistence", () => {
let manager: BackgroundManager
let promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }>
beforeEach(() => {
// #given
promptCalls = []
const client = {
session: {
prompt: async (args: { path: { id: string }; body: Record<string, unknown> }) => {
promptCalls.push(args)
return {}
},
},
}
manager = new BackgroundManager({ client, directory: tmpdir() } as unknown as PluginInput)
stubNotifyParentSession(manager)
})
afterEach(() => {
manager.shutdown()
})
test("should pass model when task has a configured model", async () => {
// #given - task with model from category config
const taskWithModel: BackgroundTask = {
id: "task-with-model",
sessionID: "session-1",
parentSessionID: "parent-session",
parentMessageID: "msg-1",
description: "task with model override",
prompt: "original prompt",
agent: "explore",
status: "completed",
startedAt: new Date(),
completedAt: new Date(),
model: { providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
concurrencyGroup: "explore",
}
getTaskMap(manager).set(taskWithModel.id, taskWithModel)
// #when
await manager.resume({
sessionId: "session-1",
prompt: "continue the work",
parentSessionID: "parent-session-2",
parentMessageID: "msg-2",
})
// #then - model should be passed in prompt body
expect(promptCalls).toHaveLength(1)
expect(promptCalls[0].body.model).toEqual({ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" })
expect(promptCalls[0].body.agent).toBe("explore")
})
test("should NOT pass model when task has no model (backward compatibility)", async () => {
// #given - task without model (default behavior)
const taskWithoutModel: BackgroundTask = {
id: "task-no-model",
sessionID: "session-2",
parentSessionID: "parent-session",
parentMessageID: "msg-1",
description: "task without model",
prompt: "original prompt",
agent: "explore",
status: "completed",
startedAt: new Date(),
completedAt: new Date(),
concurrencyGroup: "explore",
}
getTaskMap(manager).set(taskWithoutModel.id, taskWithoutModel)
// #when
await manager.resume({
sessionId: "session-2",
prompt: "continue the work",
parentSessionID: "parent-session-2",
parentMessageID: "msg-2",
})
// #then - model should NOT be in prompt body
expect(promptCalls).toHaveLength(1)
expect("model" in promptCalls[0].body).toBe(false)
expect(promptCalls[0].body.agent).toBe("explore")
})
})
describe("BackgroundManager process cleanup", () => {
test("should remove listeners after last shutdown", () => {
// #given

View File

@@ -397,15 +397,17 @@ export class BackgroundManager {
log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
sessionID: existingTask.sessionID,
agent: existingTask.agent,
model: existingTask.model,
promptLength: input.prompt.length,
})
// Note: Don't pass model in body - use agent's configured model instead
// Use prompt() instead of promptAsync() to properly initialize agent loop
// Include model if task has one (preserved from original launch with category config)
this.client.session.prompt({
path: { id: existingTask.sessionID },
body: {
agent: existingTask.agent,
...(existingTask.model ? { model: existingTask.model } : {}),
tools: {
...getAgentToolRestrictions(existingTask.agent),
task: false,