Merge pull request #1870 from dankochetov/fix/background-notification-hook-gate

fix(background-agent): honor disabled background-notification for system reminders
This commit is contained in:
YeonGyu-Kim 2026-02-17 01:35:21 +09:00 committed by GitHub
commit 5aa9ecdd5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 134 additions and 73 deletions

View File

@ -22,8 +22,9 @@ export function createManagers(args: {
pluginConfig: OhMyOpenCodeConfig pluginConfig: OhMyOpenCodeConfig
tmuxConfig: TmuxConfig tmuxConfig: TmuxConfig
modelCacheState: ModelCacheState modelCacheState: ModelCacheState
backgroundNotificationHookEnabled: boolean
}): Managers { }): Managers {
const { ctx, pluginConfig, tmuxConfig, modelCacheState } = args const { ctx, pluginConfig, tmuxConfig, modelCacheState, backgroundNotificationHookEnabled } = args
const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig) const tmuxSessionManager = new TmuxSessionManager(ctx, tmuxConfig)
@ -57,6 +58,7 @@ export function createManagers(args: {
log("[index] tmux cleanup error during shutdown:", error) log("[index] tmux cleanup error during shutdown:", error)
}) })
}, },
enableParentSessionNotifications: backgroundNotificationHookEnabled,
}, },
) )

View File

@ -1003,6 +1003,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

View File

@ -92,6 +92,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(
@ -101,6 +102,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()
@ -113,6 +115,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()
} }
@ -1203,14 +1206,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")
@ -1301,6 +1307,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) {

View File

@ -44,6 +44,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
pluginConfig, pluginConfig,
tmuxConfig, tmuxConfig,
modelCacheState, modelCacheState,
backgroundNotificationHookEnabled: isHookEnabled("background-notification"),
}) })
const toolsResult = await createTools({ const toolsResult = await createTools({