fix(babysitter): avoid double prompt gate
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test"
|
||||
import { createUnstableAgentBabysitter } from "./unstable-agent-babysitter"
|
||||
import type { BackgroundTask } from "../features/background-agent"
|
||||
import { _resetForTesting, setMainSession } from "../features/claude-code-session-state"
|
||||
import { releaseAllPromptAsyncReservationsForTesting } from "../hooks/shared/prompt-async-gate"
|
||||
import { unsafeTestValue } from "../../test-support/unsafe-test-value"
|
||||
|
||||
function createTask(): BackgroundTask {
|
||||
return {
|
||||
id: "task-1",
|
||||
sessionId: "bg-1",
|
||||
parentSessionId: "main-1",
|
||||
parentMessageId: "msg-1",
|
||||
description: "unstable task",
|
||||
prompt: "run work",
|
||||
agent: "test-agent",
|
||||
status: "running",
|
||||
progress: {
|
||||
toolCalls: 1,
|
||||
lastUpdate: new Date(Date.now() - 121000),
|
||||
lastMessage: "still working",
|
||||
lastMessageAt: new Date(Date.now() - 121000),
|
||||
},
|
||||
model: { providerID: "google", modelID: "gemini-1.5" },
|
||||
}
|
||||
}
|
||||
|
||||
describe("createUnstableAgentBabysitter", () => {
|
||||
afterEach(() => {
|
||||
_resetForTesting()
|
||||
releaseAllPromptAsyncReservationsForTesting()
|
||||
})
|
||||
|
||||
test("#given wrapper-created babysitter hook #when injecting a stale-task reminder #then the real SDK promptAsync is called once", async () => {
|
||||
// given
|
||||
setMainSession("main-1")
|
||||
const promptAsyncCalls: unknown[] = []
|
||||
const babysitter = createUnstableAgentBabysitter({
|
||||
ctx: unsafeTestValue({
|
||||
directory: process.cwd(),
|
||||
client: {
|
||||
session: {
|
||||
messages: async ({ path }: { path: { id: string } }) => ({
|
||||
data: path.id === "main-1"
|
||||
? [
|
||||
{
|
||||
info: {
|
||||
role: "assistant",
|
||||
agent: "sisyphus",
|
||||
model: { providerID: "openai", modelID: "gpt-4" },
|
||||
},
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
info: { role: "assistant" },
|
||||
parts: [{ type: "thinking", thinking: "deep thought" }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
status: async () => ({ data: { "main-1": { type: "idle" } } }),
|
||||
promptAsync: async (input: unknown) => {
|
||||
promptAsyncCalls.push(input)
|
||||
return {}
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
backgroundManager: unsafeTestValue({
|
||||
getTasksByParentSession: () => [createTask()],
|
||||
}),
|
||||
pluginConfig: unsafeTestValue({ babysitting: { timeout_ms: 120000 } }),
|
||||
})
|
||||
|
||||
// when
|
||||
await babysitter.event({
|
||||
event: {
|
||||
type: "session.idle",
|
||||
properties: { sessionID: "main-1" },
|
||||
},
|
||||
})
|
||||
|
||||
// then
|
||||
expect(promptAsyncCalls).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
@@ -3,7 +3,6 @@ import type { PluginContext } from "./types"
|
||||
|
||||
import { createUnstableAgentBabysitterHook } from "../hooks"
|
||||
import type { BackgroundManager } from "../features/background-agent"
|
||||
import { dispatchInternalPrompt } from "../hooks/shared/prompt-async-gate"
|
||||
|
||||
export function createUnstableAgentBabysitter(args: {
|
||||
ctx: PluginContext
|
||||
@@ -26,30 +25,8 @@ export function createUnstableAgentBabysitter(args: {
|
||||
return []
|
||||
},
|
||||
status: async () => ctx.client.session.status(),
|
||||
prompt: async (promptArgs) => {
|
||||
const promptResult = await dispatchInternalPrompt({
|
||||
mode: "async",
|
||||
client: ctx.client,
|
||||
sessionID: promptArgs.path.id,
|
||||
source: "unstable-agent-babysitter",
|
||||
input: promptArgs,
|
||||
})
|
||||
if (promptResult.status === "failed") {
|
||||
throw promptResult.error
|
||||
}
|
||||
},
|
||||
promptAsync: async (promptArgs) => {
|
||||
const promptResult = await dispatchInternalPrompt({
|
||||
mode: "async",
|
||||
client: ctx.client,
|
||||
sessionID: promptArgs.path.id,
|
||||
source: "unstable-agent-babysitter",
|
||||
input: promptArgs,
|
||||
})
|
||||
if (promptResult.status === "failed") {
|
||||
throw promptResult.error
|
||||
}
|
||||
},
|
||||
prompt: async (promptArgs) => ctx.client.session.prompt(promptArgs),
|
||||
promptAsync: async (promptArgs) => ctx.client.session.promptAsync(promptArgs),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,6 +14,10 @@ const RAW_PROMPT_ALLOWLIST = new Map<string, string>([
|
||||
path.join(SOURCE_ROOT, "plugin", "build-team-idle-wake-hint-client.ts"),
|
||||
"binds SDK Session.promptAsync/.status into a narrow facade consumed only by gate-routed team-idle-wake-hint dispatch; performs no direct dispatch itself",
|
||||
],
|
||||
[
|
||||
path.join(SOURCE_ROOT, "plugin", "unstable-agent-babysitter.ts"),
|
||||
"binds SDK Session.prompt/.promptAsync into a narrow facade consumed only by gate-routed unstable-agent-babysitter dispatch; performs no direct dispatch itself",
|
||||
],
|
||||
[
|
||||
path.join(SOURCE_ROOT, "hooks", "session-recovery", "recover-unavailable-tool.ts"),
|
||||
"runtime type guard checks promptAsync presence before gate-routed dispatchInternalPrompt",
|
||||
|
||||
Reference in New Issue
Block a user