Description
When a hook mapping sets deliver: false, the delivery announcement is correctly suppressed, but a system event is still injected into the main session via enqueueSystemEvent. This causes the main session to receive "Hook :
" messages for every hook completion, even when the user explicitly opted out of delivery.
Expected Behavior
When deliver: false is set on a hook mapping, no system event should be injected into the main session. The hook should complete silently.
Actual Behavior
The system event Hook <name>: <summary> is always injected into the main session when deliver: false, because the guard only checks whether delivery succeeded — not whether it was requested.
Root Cause
In src/gateway/server/hooks.ts (~line 83):
if (!result.delivered) {
enqueueSystemEvent(`${prefix}: ${summary}`.trim(), {
sessionKey: mainSessionKey,
});
}
When deliver: false:
resolveCronDeliveryPlan (in src/cron/delivery.ts) correctly resolves to { mode: "none", requested: false }
- Delivery is never attempted, so
result.delivered is always false
- The
!result.delivered guard passes → system event fires unconditionally
Suggested Fix
Check whether delivery was explicitly disabled before injecting the system event. For example:
// Also suppress system event when delivery was explicitly disabled
if (!result.delivered && value.deliver !== false) {
enqueueSystemEvent(`${prefix}: ${summary}`.trim(), {
sessionKey: mainSessionKey,
});
}
Alternatively, runCronIsolatedAgentTurn could return a deliveryRequested boolean in its result so the caller can make an informed decision.
Impact
This affects any hook with deliver: false. The main session receives a system event for every hook completion, which can cause the main session agent to announce hook results to unrelated channels (e.g., posting triage summaries to a project coordination channel instead of an audit trail channel).
Reproduction
- Configure a hook mapping with
deliver: false
- Trigger the hook
- Observe system event
Hook <name>: <summary> injected into main session
- Main session may then announce the summary to its active channel
Relevant Files
src/gateway/server/hooks.ts — dispatchAgentHook, the !result.delivered guard
src/cron/delivery.ts — resolveCronDeliveryPlan, legacy mode handling for deliver: false
src/cron/isolated-agent/run.ts — runCronIsolatedAgentTurn, delivery dispatch
Description
When a hook mapping sets
deliver: false, the delivery announcement is correctly suppressed, but a system event is still injected into the main session viaenqueueSystemEvent. This causes the main session to receive "Hook :Expected Behavior
When
deliver: falseis set on a hook mapping, no system event should be injected into the main session. The hook should complete silently.Actual Behavior
The system event
Hook <name>: <summary>is always injected into the main session whendeliver: false, because the guard only checks whether delivery succeeded — not whether it was requested.Root Cause
In
src/gateway/server/hooks.ts(~line 83):When
deliver: false:resolveCronDeliveryPlan(insrc/cron/delivery.ts) correctly resolves to{ mode: "none", requested: false }result.deliveredis alwaysfalse!result.deliveredguard passes → system event fires unconditionallySuggested Fix
Check whether delivery was explicitly disabled before injecting the system event. For example:
Alternatively,
runCronIsolatedAgentTurncould return adeliveryRequestedboolean in its result so the caller can make an informed decision.Impact
This affects any hook with
deliver: false. The main session receives a system event for every hook completion, which can cause the main session agent to announce hook results to unrelated channels (e.g., posting triage summaries to a project coordination channel instead of an audit trail channel).Reproduction
deliver: falseHook <name>: <summary>injected into main sessionRelevant Files
src/gateway/server/hooks.ts—dispatchAgentHook, the!result.deliveredguardsrc/cron/delivery.ts—resolveCronDeliveryPlan, legacy mode handling fordeliver: falsesrc/cron/isolated-agent/run.ts—runCronIsolatedAgentTurn, delivery dispatch