fix: track agent in boulder state to fix session continuation (fixes #927)
Add 'agent' field to BoulderState to track which agent (atlas) should resume on session continuation. Previously, when user typed 'continue' after interruption, Prometheus (planner) resumed instead of Sisyphus (executor), causing all delegate_task calls to get READ-ONLY mode. Changes: - Add optional 'agent' field to BoulderState interface - Update createBoulderState() to accept agent parameter - Set agent='atlas' when /start-work creates boulder.json - Use stored agent on boulder continuation (defaults to 'atlas') - Add tests for new agent field functionality
This commit is contained in:
@@ -246,5 +246,33 @@ describe("boulder-state", () => {
|
||||
expect(state.plan_name).toBe("auth-refactor")
|
||||
expect(state.started_at).toBeDefined()
|
||||
})
|
||||
|
||||
test("should include agent field when provided", () => {
|
||||
//#given - plan path, session id, and agent type
|
||||
const planPath = "/path/to/feature.md"
|
||||
const sessionId = "ses-xyz789"
|
||||
const agent = "atlas"
|
||||
|
||||
//#when - createBoulderState is called with agent
|
||||
const state = createBoulderState(planPath, sessionId, agent)
|
||||
|
||||
//#then - state should include the agent field
|
||||
expect(state.agent).toBe("atlas")
|
||||
expect(state.active_plan).toBe(planPath)
|
||||
expect(state.session_ids).toEqual([sessionId])
|
||||
expect(state.plan_name).toBe("feature")
|
||||
})
|
||||
|
||||
test("should allow agent to be undefined", () => {
|
||||
//#given - plan path and session id without agent
|
||||
const planPath = "/path/to/legacy.md"
|
||||
const sessionId = "ses-legacy"
|
||||
|
||||
//#when - createBoulderState is called without agent
|
||||
const state = createBoulderState(planPath, sessionId)
|
||||
|
||||
//#then - state should not have agent field (backward compatible)
|
||||
expect(state.agent).toBeUndefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -139,12 +139,14 @@ export function getPlanName(planPath: string): string {
|
||||
*/
|
||||
export function createBoulderState(
|
||||
planPath: string,
|
||||
sessionId: string
|
||||
sessionId: string,
|
||||
agent?: string
|
||||
): BoulderState {
|
||||
return {
|
||||
active_plan: planPath,
|
||||
started_at: new Date().toISOString(),
|
||||
session_ids: [sessionId],
|
||||
plan_name: getPlanName(planPath),
|
||||
...(agent !== undefined ? { agent } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface BoulderState {
|
||||
session_ids: string[]
|
||||
/** Plan name derived from filename */
|
||||
plan_name: string
|
||||
/** Agent type to use when resuming (e.g., 'atlas') */
|
||||
agent?: string
|
||||
}
|
||||
|
||||
export interface PlanProgress {
|
||||
|
||||
@@ -431,7 +431,7 @@ export function createAtlasHook(
|
||||
return state
|
||||
}
|
||||
|
||||
async function injectContinuation(sessionID: string, planName: string, remaining: number, total: number): Promise<void> {
|
||||
async function injectContinuation(sessionID: string, planName: string, remaining: number, total: number, agent?: string): Promise<void> {
|
||||
const hasRunningBgTasks = backgroundManager
|
||||
? backgroundManager.getTasksByParentSession(sessionID).some(t => t.status === "running")
|
||||
: false
|
||||
@@ -477,7 +477,7 @@ export function createAtlasHook(
|
||||
await ctx.client.session.prompt({
|
||||
path: { id: sessionID },
|
||||
body: {
|
||||
agent: "atlas",
|
||||
agent: agent ?? "atlas",
|
||||
...(model !== undefined ? { model } : {}),
|
||||
parts: [{ type: "text", text: prompt }],
|
||||
},
|
||||
@@ -568,7 +568,7 @@ export function createAtlasHook(
|
||||
|
||||
state.lastContinuationInjectedAt = now
|
||||
const remaining = progress.total - progress.completed
|
||||
injectContinuation(sessionID, boulderState.plan_name, remaining, progress.total)
|
||||
injectContinuation(sessionID, boulderState.plan_name, remaining, progress.total, boulderState.agent)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ All ${progress.total} tasks are done. Create a new plan with: /plan "your task"`
|
||||
if (existingState) {
|
||||
clearBoulderState(ctx.directory)
|
||||
}
|
||||
const newState = createBoulderState(matchedPlan, sessionId)
|
||||
const newState = createBoulderState(matchedPlan, sessionId, "atlas")
|
||||
writeBoulderState(ctx.directory, newState)
|
||||
|
||||
contextInfo = `
|
||||
@@ -187,7 +187,7 @@ All ${plans.length} plan(s) are complete. Create a new plan with: /plan "your ta
|
||||
} else if (incompletePlans.length === 1) {
|
||||
const planPath = incompletePlans[0]
|
||||
const progress = getPlanProgress(planPath)
|
||||
const newState = createBoulderState(planPath, sessionId)
|
||||
const newState = createBoulderState(planPath, sessionId, "atlas")
|
||||
writeBoulderState(ctx.directory, newState)
|
||||
|
||||
contextInfo += `
|
||||
|
||||
Reference in New Issue
Block a user