Skip to content

Notification Presets

SQLite-backed notification fanout presets, cursor replay, filters, and bridge suppression.

For people running agent work9 pages in this section

Notification presets are daemon-owned fanout policies stored in notification_presets. They are managed through HTTP, UDS, CLI, native tools, and the web settings surface; config.toml does not define mutable preset rows.

Built-in preset definitions are shared. Enablement is resolved separately for each profile and defaults to enabled when no exception row exists:

PresetEvents
task_terminaltask.run_*
session_unhealthysession.unhealthy, session.hung, session.recovered
provider_failureprovider.auth_required, provider.rate_limited, provider.permission_denied, provider.unavailable

Each built-in carries default_version, default_hash, user_modified, and default_update_available. Reconciliation updates untouched defaults and reports default drift without overwriting operator-modified rows.

Dispatch Contract

Preset dispatch consumes the canonical event registry. A preset matches when:

  1. the event is notification-eligible,
  2. the event type matches one of the preset patterns, and
  3. the filter expression evaluates true.

Filter fields are severity, workspace, agent, event_type, provider, task, and run. Operators are =, !=, >=, >, <=, and <; expressions support AND, OR, and parentheses.

Suffix wildcards stay inside one public event family. The built-in task_terminal preset matches notification-eligible task run events such as task.run_completed, task.run_failed, and task.run_operator_retry; it does not match session or provider events.

Each concrete target occurrence uses a notification cursor. The cursor preserves the source event's structured scope (global, or workspace plus workspace_id) and uses the event's exact ID as its subject. All scope, bridge, route, peer, thread, group, and delivery identities are opaque valid UTF-8 values: clients preserve them byte-for-byte and never trim, split, case-fold, or reinterpret them.

scope       = event.scope
consumer_id = opaque identity for this target occurrence
stream_name = event.type
subject_id  = event.id

Preset dispatch processes targets sequentially and applies the configured bridge delivery timeout to each target. The cursor advances only after confirmed delivery, deterministic skip, or bridge suppression. A delivery error records cursor diagnostics and leaves the cursor unchanged so replay can retry the same event.

The v1 dispatcher intentionally does not fan out one preset's targets in parallel. This keeps cursor ordering deterministic and makes each target's retry point explicit.

Duplicate targets are not collapsed. If a preset contains [T, T], it produces two ordered delivery attempts and two independent cursors. A confirmed delivery or skip for the first occurrence never advances the second occurrence.

For its --target input only, the CLI uses bridge_id:canonical_route: it separates the first colon as syntax and preserves both resulting components verbatim. API and native-tool payloads carry the two fields separately. Everything after the first CLI colon is the canonical route, so routes such as channel:ops are valid.

Bridge Suppression

bridge_instances.notification_suppress is a live bridge flag. When enabled, preset dispatch treats that bridge target as a deterministic skip, advances the target cursor, and performs no outbound delivery. This keeps replay monotonic while letting operators suppress a noisy or degraded bridge without deleting presets.

Bridge create/update exposes the flag as --notification-suppress, and API payloads project the same notification_suppress field.

Manage Presets

The CLI group is agent-operable and supports -o json|jsonl|toon:

compozy notifications presets list -o json
compozy --profile marketing notifications presets list -o json
compozy notifications preset show task_terminal -o json
compozy --profile marketing notifications preset disable task_terminal -o json
compozy notifications preset create provider_failures --event provider.* --filter "severity >= warning" -o json
compozy --profile marketing notifications preset enable task_terminal -o json
compozy notifications preset delete provider_failures -o json

HTTP and UDS expose the same CRUD contract:

MethodPath
GET/api/notifications/presets
POST/api/notifications/presets
GET/api/notifications/presets/{name}
PUT/api/notifications/presets/{name}
DELETE/api/notifications/presets/{name}
PUT/api/notifications/presets/{name}/enablement

GET /api/notifications/presets?profile=marketing returns each shared definition with that profile's effective enabled value. The enablement mutation accepts {"profile":"marketing","enabled":false}. Disabling a preset in one profile does not change delivery in another profile.

See also Notification Cursors for replay and cursor diagnostics.

Acknowledge notifications in the app

The header bell and Home's Needs you area offer Mark as read on each notification and Clear all for the current notification set. Acknowledgement persists across reloads and removes that occurrence from the list and count. A new attention occurrence can notify again.

The bell spans all workspaces and source profiles; read status belongs to the selected destination profile. Home uses its selected workspace (or Global) and profile view. Clear all includes rows beyond the visible slice, using the set captured when the list was loaded. Events that arrive later remain unread. If saving fails, the app retains/reloads the rows and displays an error.

Acknowledgement leaves the underlying work unchanged. Pending approvals, questions and terminal input remain available in Sessions, Tasks, Loops or Terminal. It does not approve, reject, retry, cancel, delete work, or change notification preset delivery rules.

Operator HTTP/UDS clients can read /api/notifications/attention?profile=default and submit { "snapshot": "<returned snapshot>", "id": "<occurrence id>" } to /api/notifications/attention/acknowledge?profile=default. Omit id to acknowledge the full snapshot. For Home, use its overview attention.snapshot and surface=home with the same workspace, profile/all_profiles and receipt_profile parameters. A 409 requires refreshing the list; snapshots expire after 24 hours. Acknowledgements are atomic and repeatable.

On this page