fix(background-agent): allow disabling parent session reminders

This commit is contained in:
Dan Kochetov
2026-02-16 00:58:33 +02:00
parent da3f9f9b67
commit ff10e6b02b
2 changed files with 128 additions and 70 deletions
@@ -981,6 +981,52 @@ describe("BackgroundManager.notifyParentSession - aborted parent", () => {
}) })
}) })
describe("BackgroundManager.notifyParentSession - notifications toggle", () => {
test("should skip parent prompt injection when notifications are disabled", async () => {
//#given
let promptCalled = false
const promptMock = async () => {
promptCalled = true
return {}
}
const client = {
session: {
prompt: promptMock,
promptAsync: promptMock,
abort: async () => ({}),
messages: async () => ({ data: [] }),
},
}
const manager = new BackgroundManager(
{ client, directory: tmpdir() } as unknown as PluginInput,
undefined,
{ enableParentSessionNotifications: false },
)
const task: BackgroundTask = {
id: "task-no-parent-notification",
sessionID: "session-child",
parentSessionID: "session-parent",
parentMessageID: "msg-parent",
description: "task notifications disabled",
prompt: "test",
agent: "explore",
status: "completed",
startedAt: new Date(),
completedAt: new Date(),
}
getPendingByParent(manager).set("session-parent", new Set([task.id]))
//#when
await (manager as unknown as { notifyParentSession: (task: BackgroundTask) => Promise<void> })
.notifyParentSession(task)
//#then
expect(promptCalled).toBe(false)
manager.shutdown()
})
})
function buildNotificationPromptBody( function buildNotificationPromptBody(
task: BackgroundTask, task: BackgroundTask,
currentMessage: CurrentMessage | null currentMessage: CurrentMessage | null
+15 -3
View File
@@ -93,6 +93,7 @@ export class BackgroundManager {
private completionTimers: Map<string, ReturnType<typeof setTimeout>> = new Map() private completionTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
private idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>> = new Map() private idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
private notificationQueueByParent: Map<string, Promise<void>> = new Map() private notificationQueueByParent: Map<string, Promise<void>> = new Map()
private enableParentSessionNotifications: boolean
readonly taskHistory = new TaskHistory() readonly taskHistory = new TaskHistory()
constructor( constructor(
@@ -102,6 +103,7 @@ export class BackgroundManager {
tmuxConfig?: TmuxConfig tmuxConfig?: TmuxConfig
onSubagentSessionCreated?: OnSubagentSessionCreated onSubagentSessionCreated?: OnSubagentSessionCreated
onShutdown?: () => void onShutdown?: () => void
enableParentSessionNotifications?: boolean
} }
) { ) {
this.tasks = new Map() this.tasks = new Map()
@@ -114,6 +116,7 @@ export class BackgroundManager {
this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false
this.onSubagentSessionCreated = options?.onSubagentSessionCreated this.onSubagentSessionCreated = options?.onSubagentSessionCreated
this.onShutdown = options?.onShutdown this.onShutdown = options?.onShutdown
this.enableParentSessionNotifications = options?.enableParentSessionNotifications ?? true
this.registerProcessCleanup() this.registerProcessCleanup()
} }
@@ -1186,14 +1189,17 @@ export class BackgroundManager {
allComplete = true allComplete = true
} }
const completedTasks = allComplete
? Array.from(this.tasks.values())
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
: []
if (this.enableParentSessionNotifications) {
const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED" const statusText = task.status === "completed" ? "COMPLETED" : task.status === "interrupt" ? "INTERRUPTED" : "CANCELLED"
const errorInfo = task.error ? `\n**Error:** ${task.error}` : "" const errorInfo = task.error ? `\n**Error:** ${task.error}` : ""
let notification: string let notification: string
let completedTasks: BackgroundTask[] = []
if (allComplete) { if (allComplete) {
completedTasks = Array.from(this.tasks.values())
.filter(t => t.parentSessionID === task.parentSessionID && t.status !== "running" && t.status !== "pending")
const completedTasksText = completedTasks const completedTasksText = completedTasks
.map(t => `- \`${t.id}\`: ${t.description}`) .map(t => `- \`${t.id}\`: ${t.description}`)
.join("\n") .join("\n")
@@ -1285,6 +1291,12 @@ Use \`background_output(task_id="${task.id}")\` to retrieve this result when rea
} }
log("[background-agent] Failed to send notification:", error) log("[background-agent] Failed to send notification:", error)
} }
} else {
log("[background-agent] Parent session notifications disabled, skipping prompt injection:", {
taskId: task.id,
parentSessionID: task.parentSessionID,
})
}
if (allComplete) { if (allComplete) {
for (const completedTask of completedTasks) { for (const completedTask of completedTasks) {