Skip to content

Hook deliver: false still injects system event into main session #36325

Description

@cioclawcode

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:

  1. resolveCronDeliveryPlan (in src/cron/delivery.ts) correctly resolves to { mode: "none", requested: false }
  2. Delivery is never attempted, so result.delivered is always false
  3. 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

  1. Configure a hook mapping with deliver: false
  2. Trigger the hook
  3. Observe system event Hook <name>: <summary> injected into main session
  4. Main session may then announce the summary to its active channel

Relevant Files

  • src/gateway/server/hooks.tsdispatchAgentHook, the !result.delivered guard
  • src/cron/delivery.tsresolveCronDeliveryPlan, legacy mode handling for deliver: false
  • src/cron/isolated-agent/run.tsrunCronIsolatedAgentTurn, delivery dispatch

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions