Notification CLI¶
This page names notification and gate CLI behavior that is not only a flag table. See
the generated sase notify page for flags and subcommands.
CLI¶
The sase notify command can create notifications and inspect the local notification
inbox.
Bare sase notify is a read-only shortcut for sase notify list. Use
sase notify create when writing a notification from JSON input:
echo '{"sender": "test", "icon": "š", "notes": ["Hello"], "tags": ["review"]}' | sase notify create
echo '{"sender": "audit", "notes": ["Background result"], "silent": true}' | sase notify create
sase notify create -s my_sender < notification.json
sase notify create -s my_sender --tag review --tag handoff < notification.json
sase notify +1 6f8a2 "same failure reproduced on macOS" -s ci_watch
sase notify +1 -k ci/sase-main "failure reproduced again" -s ci_watch
sase notify rules shows the configured
delivery rules in the order they are consulted, and
sase notify rules -e ID explains which rule decides the toast and the sound for one
stored notification. It never changes notification state.
Raw creation validates and preserves the optional single-glyph JSON icon, the optional
#RRGGBB JSON color (see Tab colors), and the JSON
silent field. It rejects registered privileged actions (PlanApproval,
EpicApproval, TaskTriage, BeadSnooze, FlagTriage, BeadStaleCleanup,
PluginsRequired, UserQuestion, LaunchApproval, SudoRequest, CustomGate, and
HITL) because a raw row has no trusted command bundle.
sase notify +1 [ID] NOTE accepts an exact notification ID or unique prefix. With
-k/--dedup-key, it instead selects the newest row matching the resolved sender and
key; pass -s/--sender explicitly when producer identity matters. Otherwise the sender
is the current SASE agent identity, falling back to the OS user. An ID miss exits
non-zero; a dedup-key miss emits {"action":"no_match"} and exits zero so producers can
decide to create a fresh row.
Producers can make that decision atomically with sase notify create -k KEY -p NOTE.
When no (sender, key) row exists, SASE creates the input notification with that key;
when one exists, it appends NOTE and does not create a duplicate.
-S/--supersedes OLD_KEY applies only on the create branch: it retires matching old-key
rows by appending a superseding +1 and dismissing them. It is ignored when the current
key already matches a row. The same three values can be supplied as JSON dedup_key,
plus_one_note, and supersedes fields.
The first-class gate API reads a versioned gate specification from stdin:
sase gate create < gate-request.json
For example, this custom gate offers a restart-and-verify group plus a separate rejection branch:
{
"schema_version": 3,
"kind": "custom",
"request_id": "restart-api",
"producer": { "agent": "maintenance" },
"continuation_mode": "resume_agent",
"gate_timeout_seconds": 900,
"presentation": {
"sender": "maintenance",
"icon": "š”ļø",
"notes": ["Restart the API after reviewing the health report?"],
"panel": "deployments",
"panel_icon": "š",
"origin_agent": "maintenance.agent",
"chip": { "glyph": "š", "label": "deploy", "color": "#5FD75F" },
"preview": "preview.md"
},
"query": "(restart AND verify) OR reject",
"primary_branch": ["restart", "verify"],
"options": [
{
"id": "restart",
"label": "Restart service",
"icon": "š",
"default_selected": true,
"feedback": "required",
"command": { "argv": ["commands/restart"] }
},
{
"id": "verify",
"label": "Verify service health",
"icon": "š©ŗ",
"default_selected": true,
"feedback": "disabled",
"command": { "argv": ["commands/verify"] }
},
{
"id": "reject",
"label": "Do not restart",
"icon": "ā",
"feedback": "optional",
"command": { "argv": ["commands/reject"] }
}
],
"groups": [
{
"options": ["restart", "verify"],
"label": "Restart service",
"icon": "š"
}
],
"resources": [
{
"path": "commands/restart",
"role": "command",
"content": "#!/bin/sh\nprintf '{\"status\":\"restarted\"}\\n'\n"
},
{
"path": "commands/verify",
"role": "command",
"content": "#!/bin/sh\nprintf '{\"status\":\"healthy\"}\\n'\n"
},
{
"path": "commands/reject",
"role": "command",
"content": "#!/bin/sh\nprintf '{\"status\":\"rejected\"}\\n'\n"
},
{
"path": "preview.md",
"role": "preview",
"content": "# API health report\n\nAll checks passed.\n"
}
],
"auto": false
}
presentation.panel selects the named notification panel tab described in
Tags, and requires presentation.panel_icon alongside
it ā a gate that names a tab is the thing introducing that tab to the user, so it is the
thing responsible for saying what the tab looks like. Omitting panel_icon while
declaring panel fails gate creation with a missing_presentation error.
presentation.origin_agent attributes the gate to the agent it was filed on behalf of;
it is stripped, limited to 128 characters, and stored without consulting the local agent
registry so remote agent names remain valid. All three fields are projected into
notification action_data as panel, panel_icon, and origin_agent; producers may
not write those protected keys directly through presentation.action_data.
presentation.chip is an optional subject chip: an object with glyph (required, one
grapheme), label (required, stripped, non-empty, single-line, free of control
characters, and at most 32 characters), and color (optional #RRGGBB). A declared
chip is projected into notification action_data as gate_chip_glyph,
gate_chip_label, and gate_chip_color; a colourless chip writes only the first two
keys. Those keys are protected: producers may not write them through
presentation.action_data. Every render surface ā sase's TUI toast, the notification
row, the gate detail pane, the review modal, and the mobile bridge row ā reads the
stored keys only and never resolves a task type. A stored colour that is not #RRGGBB
is ignored so the glyph and label still render; a missing glyph or label drops the chip.
Every gate whose subject is a typed task bead declares this chip from the presentation
frozen at gate creation (the type glyph, the slug as the label, and the type accent) and
adds the compact typed facts line as notes[1]. An untyped bead declares no chip.
presentation.title is the one-line decision headline shown in the notification panel's
gate detail pane and in the custom gate review
modal's header. It is stripped, limited to 120 characters, must be a single line, and
must not contain control characters; a missing title falls back to the gate kind's
display title (for example Custom Gate). It is required for kind: "custom" ā
along with a non-empty presentation.icon and at least one non-blank
presentation.notes entry ā because a custom gate has no other source for the headline,
icon, and context the panel renders. plan, epic_plan, question, launch, and
hitl gates keep their existing contracts and do not require a title.
presentation.title is projected into notification action_data as gate_title;
producers may not write that protected key directly through presentation.action_data.
presentation.color suggests the #RRGGBB accent for the tab the gate's notification
lands in, as described in Tab colors; a malformed
value fails gate creation with an invalid_color error.
presentation.panel_icon suggests one emoji or display glyph for the tab the gate's
notification lands in, as described in Tab icons; a
malformed value fails gate creation with an invalid_presentation error. It is a
separate field from presentation.icon rather than a reuse of it, because
presentation.icon is the row's icon and rows sharing one panel legitimately differ
ā donating the row icon to the tab would make the tab's glyph flip depending on which
row arrived most recently. panel_icon is a property of the tab, and gates sharing a
panel are expected to agree on it.
presentation.icon, option.icon, and group.icon each accept one emoji or display
glyph. Each OR branch is a mutually exclusive resolution path. A singleton branch
renders as one button; an AND branch renders selectable option toggles plus a submit
button. The selected ids must be a non-empty subset of exactly one branch.
default_selected defaults to true, and a matching groups entry configures an AND
branch's submit label and icon. feedback is disabled, optional, or required;
custom options default to optional, and a group selection uses the strongest mode
among its selected members. Automatic resolution runs the creator's autonomy record
through one core evaluator over explicit policy option IDs ā never primary_branch,
default_selected, or option order ā so reordering the branch or switching the defaults
off leaves the automatic outcome unchanged. Only the plan, epic-plan, and question kinds
can auto-resolve; every other kind, including custom gates, always waits for a human.
Each evaluation writes a policy block (profile, selection, rule, outcome,
value, option_ids, source, revision, digest) into the request envelope's
auto block and the creation result's auto_resolution, and, for automatic outcomes,
into the gate response as well. primary_branch must name one complete branch in
canonical query order. sase's TUI submits it with Enter while Space toggles the focused
AND member; submitting a primary group preserves the reviewer's current toggles. sase's
TUI also numbers top-level branches in canonical order: the fixed keys 1ā9 submit
their matching branches directly. AND-member toggles remain unnumbered.
Every option references a bundle-owned command resource and is executed in query order
as an argv array without a shell after its hash is reverified. A selected-command
failure is recorded in the bundle error log and leaves the gate answerable. The
write-once response records selected_option_ids, option_results, and normalized
top-level feedback consistently for every transport.
On success, creation prints a stable JSON descriptor containing schema_version,
notification_id, request_id, kind, bundle/request/response/preview paths,
continuation_mode, auto_resolution, and hashes. The descriptor's request_id and
kind are the exact values accepted by sase gate wait. Typed front doors such as
sase plan propose, sase questions, and agent-initiated launch requests call the same
in-process gate service directly; they do not spawn this CLI. Agents should normally use
the generated /sase_gate skill to author this JSON.
Wait mechanically for a gate without reading or polling bundle files directly:
sase gate wait --id <request_id> --kind <kind>
sase gate wait --id <request_id> --kind <kind> --json
sase gate wait --id <request_id> --kind <kind> --timeout 60
Human output is colored and summarizes the selected options, feedback, response path,
and any failed execution recovery commands. -j/--json emits the stable shape status,
selected_option_ids, feedback, and response_path, plus input, option_inputs,
and option_results off the write-once response (populated only once the gate is
answered), operations from the execution journal, and for failed execution failure
plus failure_recovery (error_report_path and exact resume, restart, or cancel
commands when they are safe to run). Status is answered, cancelled, timeout, or
failed, with exit codes 0, 3, 4, and 5 respectively. A CLI timeout can shorten but
never extend the request's own gate timeout.
sase gate answer, sase gate act, and sase gate show are the headless counterparts
to sase's TUI modals: answer selects a branch and supplies each selected option's
declared input (--set field=value typed by its declaration,
--option-input <opt>=@file.json for a whole per-option value, or --input @file.json
for the shared value) and resumes or restarts a partially executed AND branch with
--resume / --restart. On an already-answered turn-backed tale whose selected branch
requested a coder, --resume retries only that unfinished handoff: it reuses the stored
answer, skips option commands and plan archival, and launches nothing when the successor
is already recorded. Conflicting --option / input values are refused. --restart
still means "run the whole option branch again" and does not recover a coder handoff.
act runs one declared action headlessly, including opening $EDITOR for an
edit_file action, without answering the gate; show prints a gate's declared
branches, each option's input fields, and its declared actions, so an author can check
that the gate they wrote asks for what they intended. When a decision has been accepted
but its execution has not written response.json yet, show adds an Acceptance
block (an acceptance object with --json) with the accepted options, the submitting
surface, and the latest recorded execution error, or flags an invalid decision receipt.
See --help on each for the full flag reference.
For read-only inspection, list recent notifications as either a compact table or stable JSON:
sase notify
sase notify list
sase notify list -j -l 20
sase notify list -j --sender axe
sase notify list -j --unread
sase notify list -j --tag done
sase notify list -j --tag memory
sase notify list -j -q digest
sase notify list -j --all
Use the explicit list subcommand when passing list flags; for example, use
sase notify list -j, not sase notify -j.
sase notify list -j prints notifications newest first with id, timestamp, age,
sender, icon, priority, notes, files, tags, action, action_data, read,
dismissed, silent, muted, snooze_until, and resurfaced_at. The -q/--query
filter matches tags as well as ids, senders, notes, files, actions, and action data.
Dismissed notifications are hidden unless --all is provided.
list and show are current-state reads: they atomically expire any due snooze before
projecting, and they order and limit by the activity key, so a resurfaced old
notification appears on the first -l 1 page while still reporting its original
timestamp and age.
Inspect one notification by id:
sase notify show --id <notification_id>
sase notify show --id <notification_id> -f json
sase notify show --id <notification_id> -f markdown
The default show format is markdown. It includes the notification tags, notes,
attached file paths, action data, and state flags. Axe error digest notifications
usually point to the actionable report through files or
action_data.error_report_path; read that attached file for the detailed errors. Gate
execution failures use action GateExecutionFailed, deterministic ids scoped to the
gate and accepted receipt, tags gate, execution, and error, and action data with
error_report_path plus the exact safe resume, restart, and cancel commands.
To create a local test notification with a persistent PNG attachment for sase's TUI
modal image-preview checks, run tools/test_image_notification from the repository
root.
See sase notify for the full CLI reference.
Related¶
sase notify¶
With no subcommand, sase notify defaults to the compact sase notify list view. Use
sase notify list for JSON, limit, query, unread, dismissed, or the clearest sender/tag
filtering form. Use sase notify create to write a raw, non-privileged notification
from stdin JSON. Use sase gate create and sase gate wait for command-backed gates.
Moved to sase notify.
Moved to Notification CLI.
Command behavior¶
Raw creation accepts JSON icon, tags, and silent fields plus repeatable
-t/--tag; icons must be one emoji or display glyph, and CLI tags are appended to JSON
tags, then normalized and deduplicated. Raw creation cannot create a registered
privileged gate action. The query form, sase notify list -q, also matches tags, and
sase notify list --tag <tag> filters to notifications with that exact normalized tag.
notify +1 never changes read, dismissed, muted, snoozed, ordering, or delivery state.
When notify create uses a dedup key, --plus-one-note is required: it is appended on
a match, while a miss creates the row. --supersedes identifies an old sender-scoped
key to annotate and dismiss only when that miss creates the replacement; it has no
effect when the current dedup key already matches a row. See
Notification +1 Evidence.
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.