fix: propagate variant field in all promptAsync continuation paths (#3081)

All 5 continuation paths now send variant as top-level body field:
- boulder-continuation-injector.ts
- ralph-loop/continuation-prompt-injector.ts
- todo-continuation-enforcer/continuation-injection.ts
- unstable-agent-babysitter-hook.ts
- session-recovery/resume.ts

Plus type/helper updates in atlas, todo-continuation-enforcer,
unstable-agent-babysitter, and session-recovery.

TDD: 18 regression tests added, all pass. tsc clean.
This commit is contained in:
YeonGyu-Kim
2026-04-07 15:10:38 +09:00
parent 3e8fd5ff18
commit aa528e42c0
15 changed files with 310 additions and 23 deletions
@@ -121,4 +121,64 @@ describe("injectBoulderContinuation", () => {
expect(result).toBe("skipped_agent_unavailable")
expect(promptAsyncMock).not.toHaveBeenCalled()
})
test("#given recent prompt context includes variant #when injecting boulder continuation #then promptAsync receives variant as a top-level field", async () => {
// given
registerAgentName("atlas")
const capturedRequests: Array<{
body?: {
model?: { providerID: string; modelID: string }
variant?: string
}
}> = []
const promptAsyncMock = mock(async (request: unknown) => {
capturedRequests.push(request as typeof capturedRequests[number])
return undefined
})
const recentModel = {
providerID: "anthropic",
modelID: "claude-sonnet-4-20250514",
variant: "max",
}
const messagesMock = mock(async () => ({
data: [{
id: "msg_1",
info: {
agent: "atlas",
model: recentModel,
time: { created: Date.now() },
},
}],
}))
const ctx = {
directory: "/tmp",
client: {
session: {
messages: messagesMock,
promptAsync: promptAsyncMock,
},
},
} as unknown as PluginInput
// when
const result = await injectBoulderContinuation({
ctx,
sessionID: "ses_test_variant",
planName: "test-plan",
remaining: 1,
total: 2,
agent: "atlas",
sessionState: { promptFailureCount: 0 },
})
// then
expect(result).toBe("injected")
expect(capturedRequests).toHaveLength(1)
expect(capturedRequests[0]?.body?.model).toEqual({
providerID: "anthropic",
modelID: "claude-sonnet-4-20250514",
})
expect(capturedRequests[0]?.body?.variant).toBe("max")
})
})
@@ -71,12 +71,18 @@ export async function injectBoulderContinuation(input: {
const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID)
const inheritedTools = resolveInheritedPromptTools(sessionID, promptContext.tools)
await ctx.client.session.promptAsync({
path: { id: sessionID },
body: {
agent: continuationAgent,
...(promptContext.model !== undefined ? { model: promptContext.model } : {}),
...(inheritedTools ? { tools: inheritedTools } : {}),
const launchModel = promptContext.model
? { providerID: promptContext.model.providerID, modelID: promptContext.model.modelID }
: undefined
const launchVariant = promptContext.model?.variant
await ctx.client.session.promptAsync({
path: { id: sessionID },
body: {
agent: continuationAgent,
...(launchModel ? { model: launchModel } : {}),
...(launchVariant ? { variant: launchVariant } : {}),
...(inheritedTools ? { tools: inheritedTools } : {}),
parts: [createInternalAgentTextPart(prompt)],
},
query: { directory: ctx.directory },
+16 -2
View File
@@ -40,7 +40,14 @@ export async function resolveRecentPromptContextForSession(
const model = info?.model
const tools = normalizePromptTools(info?.tools)
if (model?.providerID && model?.modelID) {
return { model: { providerID: model.providerID, modelID: model.modelID }, tools }
return {
model: {
providerID: model.providerID,
modelID: model.modelID,
...(model.variant ? { variant: model.variant } : {}),
},
tools,
}
}
if (info?.providerID && info?.modelID) {
@@ -63,7 +70,14 @@ export async function resolveRecentPromptContextForSession(
if (!model?.providerID || !model?.modelID) {
return { tools }
}
return { model: { providerID: model.providerID, modelID: model.modelID }, tools }
return {
model: {
providerID: model.providerID,
modelID: model.modelID,
...(model.variant ? { variant: model.variant } : {}),
},
tools,
}
}
export async function resolveRecentModelForSession(
+1 -1
View File
@@ -2,7 +2,7 @@ import type { AgentOverrides } from "../../config"
import type { BackgroundManager } from "../../features/background-agent"
import type { TopLevelTaskRef } from "../../features/boulder-state"
export type ModelInfo = { providerID: string; modelID: string }
export type ModelInfo = { providerID: string; modelID: string; variant?: string }
export interface AtlasHookOptions {
directory: string