Merge pull request #2030 from acamq/feature/agent-input-notifications
feat(notification): alert when agent asks questions or needs permission
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
const { describe, expect, test, spyOn } = require("bun:test")
|
||||
|
||||
const sessionState = require("../features/claude-code-session-state")
|
||||
const { createToolExecuteBeforeHandler } = require("./tool-execute-before")
|
||||
|
||||
describe("createToolExecuteBeforeHandler session notification sessionID", () => {
|
||||
test("uses main session fallback when input sessionID is empty", async () => {
|
||||
const mainSessionID = "ses_main"
|
||||
const getMainSessionIDSpy = spyOn(sessionState, "getMainSessionID").mockReturnValue(mainSessionID)
|
||||
|
||||
let capturedSessionID: string | undefined
|
||||
const hooks = {
|
||||
sessionNotification: async (input) => {
|
||||
capturedSessionID = input.event.properties?.sessionID
|
||||
},
|
||||
}
|
||||
|
||||
const handler = createToolExecuteBeforeHandler({
|
||||
ctx: { client: { session: { messages: async () => ({ data: [] }) } } },
|
||||
hooks,
|
||||
})
|
||||
|
||||
await handler(
|
||||
{ tool: "question", sessionID: "", callID: "call_q" },
|
||||
{ args: { questions: [{ question: "Continue?", options: [{ label: "Yes" }] }] } },
|
||||
)
|
||||
|
||||
expect(getMainSessionIDSpy).toHaveBeenCalled()
|
||||
expect(capturedSessionID).toBe(mainSessionID)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
@@ -31,6 +31,60 @@ describe("createToolExecuteBeforeHandler", () => {
|
||||
await expect(run).resolves.toBeUndefined()
|
||||
})
|
||||
|
||||
test("triggers session notification hook for question tools", async () => {
|
||||
let called = false
|
||||
const ctx = {
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({ data: [] }),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const hooks = {
|
||||
sessionNotification: async (input: { event: { type: string; properties?: Record<string, unknown> } }) => {
|
||||
called = true
|
||||
expect(input.event.type).toBe("tool.execute.before")
|
||||
expect(input.event.properties?.sessionID).toBe("ses_q")
|
||||
expect(input.event.properties?.tool).toBe("question")
|
||||
},
|
||||
}
|
||||
|
||||
const handler = createToolExecuteBeforeHandler({ ctx, hooks })
|
||||
const input = { tool: "question", sessionID: "ses_q", callID: "call_q" }
|
||||
const output = { args: { questions: [{ question: "Proceed?", options: [{ label: "Yes" }] }] } as Record<string, unknown> }
|
||||
|
||||
await handler(input, output)
|
||||
|
||||
expect(called).toBe(true)
|
||||
})
|
||||
|
||||
test("does not trigger session notification hook for non-question tools", async () => {
|
||||
let called = false
|
||||
const ctx = {
|
||||
client: {
|
||||
session: {
|
||||
messages: async () => ({ data: [] }),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const hooks = {
|
||||
sessionNotification: async () => {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
|
||||
const handler = createToolExecuteBeforeHandler({ ctx, hooks })
|
||||
|
||||
await handler(
|
||||
{ tool: "bash", sessionID: "ses_b", callID: "call_b" },
|
||||
{ args: { command: "pwd" } as Record<string, unknown> },
|
||||
)
|
||||
|
||||
expect(called).toBe(false)
|
||||
})
|
||||
|
||||
describe("task tool subagent_type normalization", () => {
|
||||
const emptyHooks = {}
|
||||
|
||||
|
||||
@@ -30,6 +30,26 @@ export function createToolExecuteBeforeHandler(args: {
|
||||
await hooks.prometheusMdOnly?.["tool.execute.before"]?.(input, output)
|
||||
await hooks.sisyphusJuniorNotepad?.["tool.execute.before"]?.(input, output)
|
||||
await hooks.atlasHook?.["tool.execute.before"]?.(input, output)
|
||||
|
||||
const normalizedToolName = input.tool.toLowerCase()
|
||||
if (
|
||||
normalizedToolName === "question"
|
||||
|| normalizedToolName === "ask_user_question"
|
||||
|| normalizedToolName === "askuserquestion"
|
||||
) {
|
||||
const sessionID = input.sessionID || getMainSessionID()
|
||||
await hooks.sessionNotification?.({
|
||||
event: {
|
||||
type: "tool.execute.before",
|
||||
properties: {
|
||||
sessionID,
|
||||
tool: input.tool,
|
||||
args: output.args,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (input.tool === "task") {
|
||||
const argsObject = output.args
|
||||
const category = typeof argsObject.category === "string" ? argsObject.category : undefined
|
||||
|
||||
Reference in New Issue
Block a user