Monitors¶
A monitor shell is a real agent family member whose work is one
supervised OS command instead of an LLM turn. sase monitor start hands a slow command
off to a detached supervisor process and returns immediately, so an agent can run
just check-full, wait on a CI job, or sleep before a deploy without blocking its own
turn.
SASE agents are single-turn: a provider turn runs, the runner captures it, and the agent
is done. Provider-native background-execution or scheduled wake-up tools assume a
multi-turn session and do nothing useful here — the turn ends and the wake-up never
fires. Monitors are the SASE-native replacement: use /sase_monitor (or
sase monitor start directly) instead of any built-in monitor, background-execution, or
scheduled wake-up tool.
The agent-family picture¶
Starting a monitor promotes the calling sase-agent to an agent family, exactly as
%id(suffix, family=parent) would, and adds the monitor as a proc shell member:
sase-agent "acme" before after `sase monitor start`
────────────────────────────────────────────────────────────────────────────────
acme (one-shell agent, RUNNING) acme (agent family)
├─ acme--0 DONE ← starter shell, killed
├─ acme--mon MONITORING ← monitor proc shell
└─ acme--1 RUNNING ← follow-up shell
| Term | Meaning |
|---|---|
| starter shell | The agent shell that ran sase monitor start (absent for host-started monitors) |
| monitor shell | The --mon proc shell representing the supervised command |
| supervisor | The detached process that runs the command and streams its output |
| follow-up | The agent shell launched under the family after the command finishes |
sase monitor start, run from inside an agent, is the last thing that agent does: it
kills the calling agent's turn (the same handoff mechanism sase plan propose and
sase questions use), while the supervisor keeps the command running under the same
workspace claim. The workspace is never released and re-claimed between the starter and
the follow-up — the follow-up sees exactly the tree the monitor started with, plus
whatever the command itself changed.
A monitor shell has an ordinary artifacts directory, just like an agent shell, so
everything that already understands agent families — the Agents tab, family roster,
runtime aggregation, sase chats, %wait/#fork resolution — works on it with no
special casing. There is no separate monitor store: a monitor's durable record is its
agent_meta.json plus done.json, and sase monitor list/show are queries over the
existing agent artifact index.
Starting a monitor¶
sase monitor start \
--command 'just check-full' \
--reason 'Verify the refactor before replying to the user' \
--timeout 45m \
--next 'Fix anything just check-full reported, then reply to the user.'
--command/-cis the full command handed to the shell.--reason/-rand--timeout/-t(bare seconds, or90s/45m/2h) are required — a monitor always says why it exists and has a bounded budget.--idle-timeout/-iis optional. It kills a command that stops producing output for the given duration, while still allowing intentionally quiet commands when omitted.--next/-nis the follow-up agent's instruction. Omit it for a fire-and-forget monitor: the command still runs to completion and its output, exit state, and runtime are recorded for later inspection, but no agent launches afterward.--next-output none|tail|filecontrols how much retained command output is handed to the follow-up agent.tailis the default;filepoints at the on-disk log;nonegives only the outcome summary andsase monitor show --all-linespointer.--start-status/-sand--stop-status/-Soverride the defaultMONITORING/MONITOREDlabels shown on the row — useful for asleep-based wait (SLEEPING FOR 300s→SLEPT FOR 300s).--label/-L,--agent/-a(--laneremains accepted as a deprecated alias),--cwd/-C, and--tail-lines/-Tare optional; seesase monitor start --helpfor the full list.
Only one monitor may be running per agent at a time. Repeating the same full request
returns the existing running record; changing the command, cwd, timeout, next action,
status labels, or output policy is rejected until the active monitor settles. A lost
monitor is never implicitly replayed.
Resolving the implicit agent¶
--agent / -a is only needed to start a monitor outside an agent shell (no
SASE_AGENT_NAME set) or to target a different agent than the caller. From inside an
agent -- including an epic phase lane and a promoted agent family -- omitting it
resolves the calling agent shell metadata-first:
- The caller's own artifacts dir (
SASE_ARTIFACTS_DIR), when it belongs to the caller. - An exact
SASE_AGENT_NAMEmatch against an artifact's own name. - The newest non-monitor member of the caller's own family, when
SASE_AGENT_NAMEnames a family container rather than a concrete shell -- family members can replace one another inside a single process, leavingSASE_AGENT_NAMEset to the family while the running shell's own artifacts carry the concrete member name. A settled--monmember is never selected here, even when it is the newest member of the family.
An unresolvable caller (no artifacts match any of the above) is a clear error naming
-a/--agent, not a silent fallback to the current working directory or another agent's
lane. sase monitor show/stop with no id resolve the same way, against the caller's
own durable family -- never a parent's or sibling's.
The command is executed through the platform shell (sh -c on Unix), so shell quoting,
redirection, and variable expansion are the caller's responsibility. Monitors are for
batch commands: do not use them for interactive programs or commands that require a TTY.
Supervision guarantees¶
The detached supervisor owns the command's process group and writes combined stdout and stderr to a bounded rotating log. Completion is based on process exit, not pipe EOF, so a backgrounded grandchild that holds stdout open cannot keep the monitor running after the command process exits. Total timeouts and TERM-to-KILL escalation are checked on every supervisor tick, independent of whether the command is quiet, chatty, writing partial lines, or emitting non-UTF-8 bytes. Non-UTF-8 output is retained with replacement characters rather than crashing the supervisor.
Monitor logs are bounded. The active log is live_reply.md; when it rotates, readers
stitch the rotated live_reply.md.1 and active file where appropriate. Very large
output therefore keeps a recent on-disk view and a head-plus-tail retained summary for
the follow-up agent rather than preserving unlimited bytes.
The command does not inherit the starter agent's SASE_AGENT* identity or
SASE_ARTIFACTS_DIR, so tools run by the command cannot accidentally write artifacts or
variables into the dead starter's directory.
Surviving the starter's teardown¶
sase monitor start, run from inside an agent, hands the command to a supervisor and
then kills the calling agent's runner group as part of the same handoff. The supervisor
must not be a casualty of that kill: it is spawned through a double-fork bootstrap that
reparents it to PID 1 before start_monitor returns, so a process-tree teardown
launched after the call cannot reach it by walking PPIDs. The supervisor also sets
SIGHUP to ignored and installs its SIGTERM/SIGINT handling in the first statements
it runs, before any expensive import, closing the startup window in which a stray signal
could kill it silently.
Startup acknowledgement¶
start_monitor never hands back a running record for a supervisor that is not
provably alive, because its caller's very next act — inside an agent — is to kill
itself. Once the supervisor has taken ownership (dispositions set, meta read, output log
opened) it writes a .monitor_started marker carrying its real pid, pgid, and identity;
start_monitor blocks on that marker for up to 20 seconds, polling the supervisor's
liveness too so a pid that is already dead fails fast instead of waiting out the full
budget. A missing acknowledgement terminates the supervisor, hands the workspace claim
back to the still-live starter exactly as it held it (never releasing it into the free
pool), tears the member down as terminal failed, and raises MonitorError — so the
starter agent stays alive, sase monitor start exits non-zero, and nothing downstream
ever hands off to a phantom.
A monitor owns its workspace until it is reconciled¶
A running monitor's workspace claim is not released just because its supervisor's pid looks dead. The stale-claim sweeper reconciles a monitor's own markers first (killing a confirmed-dead process group, finalizing the log, disposing the claim, and running or recording the follow-up disposition) and only then releases the claim — and if reconciliation itself fails, the sweeper leaves that claim in place rather than guessing. This closes the window where another agent could be handed a workspace a monitor is still using: a not-yet-reconciled dead supervisor is exactly the state a live monitor is in from the outside, so a bare dead-pid check is not sufficient evidence to release.
Status and bucket¶
The displayed status is always the configured label. Because those labels are arbitrary
strings, the underlying bucket (Running / Done / Failed, used for grouping and
counting) is tracked separately from the label, from monitor_state:
monitor_state |
Bucket | Displayed status |
|---|---|---|
running |
Running | start status |
completed |
Done | stop status |
failed |
Failed | stop status (+ exit code or supervisor error) |
timeout |
Failed | stop status (+ total-timeout or idle-timeout note) |
stopped |
Done | stop status |
lost |
Failed | stop status; command outcome is unknown |
The stop-status label is descriptive text, not a success condition. It is reused for
every terminal state, including an explicit stop; use the bucket, monitor_state, exit
code, and output to decide what happened.
A monitor is terminal only after it is settled: the command has exited or been
reconciled, the log has been finalized, the workspace claim has been released or
transferred, and the follow-up has launched or its disposition has been recorded.
Polling commands such as sase monitor show --follow and %wait continue waiting while
a monitor is stopped but not yet settled.
A failing just check-full shows up as a Failed member with its exit code visible, and
the follow-up agent still launches so it can fix what broke. A timed-out command is
killed (its whole process group, not just the shell), and the follow-up is told plainly
which budget fired: total runtime or no-output idle time. A lost monitor means the
supervisor belongs to a previous boot, so SASE cannot know whether the command finished
or what it changed. Lost monitors are not automatically re-run, and their recorded
follow-up action is not launched.
The follow-up agent¶
When --next is set and the monitor did not end in stopped or lost, one follow-up
agent shell launches under the same agent family once the command finishes and the
monitor settles. It receives:
- the starter's full prior conversation, via
#fork; - the original
--reasonand the--nextinstruction, verbatim, under its own heading; - a command-run breakdown: outcome, exit code, elapsed time vs. the timeout budget, and
the selected output policy from
--next-output; - the full log path and the exact
sase monitor show <id> --all-linesinvocation to read more than the tail.
The follow-up inherits the starter's model, provider, and reasoning effort, so it is the same kind of agent shell that started the monitor.
The launch is not coupled to a workspace-claim handoff that can fail: if the monitor's
own workspace claim can no longer be transferred to the follow-up (for example, a stale
sweep already released it), the follow-up still launches — first against a fresh claim
on the same workspace, then against workspace 0 if that workspace has since been taken
by another agent. Either fallback is recorded as a degraded launch, and the
follow-up prompt says plainly which happened, because a follow-up in a different
workspace than the monitor ran in cannot assume the command's artifacts are present.
Only when a follow-up genuinely cannot be launched at all is it dropped — and even then
the composed prompt is persisted as a durable artifact so the instruction can be
replayed by hand instead of surviving only as an error string. See
Visibility below for how a dropped or degraded follow-up is surfaced.
The follow-up prompt's body is enclosed in an xprompt-disabled region, so directives,
#xprompt references, and $(...) command substitution inside --reason, --next,
table fields, and embedded output are delivered as literal text. Only the routing prefix
(#fork:, %model:, %effort:) remains live. When --next-output tail is used,
retained output is also fenced and labeled as untrusted program output. The command and
cwd fields are fenced too, so directive-shaped strings inside a shell command or path
remain literal even if the disabled region is ever removed. Use --next-output file for
large or hostile logs when the follow-up should inspect the log explicitly, or
--next-output none when the outcome summary and sase monitor show --all-lines
pointer are enough.
Inspecting and stopping monitors¶
sase monitor list # active monitors, newest first
sase monitor list --all --agent acme # include finished monitors for one agent
sase monitor list --status failed --status timeout
sase monitor show <id> # details plus an output tail
sase monitor show <id> --follow # stream new output until it finishes
sase monitor show <id> --all-lines --output-only
sase monitor stop [<id>] # stop a running monitor; omit id to target
# the calling agent's active monitor
ID accepts a monitor id (or unique prefix), the monitor shell's agent name, or the
owning sase-agent name. sase monitor stop never launches the recorded follow-up agent,
even when --next was given.
Every subcommand can emit machine-readable output, but not with the same flag: start,
list, and stop take -j/--json, while list and show take -f/--format
(table/markdown/json for list, markdown/json for show).
sase monitor show has no -j — use --format json there. See
sase monitor --help and each subcommand's --help for the complete flag reference, or
CLI Reference.
Reading monitors also performs dead-supervisor reconciliation. sase monitor list, the
ACE Agents tab refresh path, and the axe scheduler look for running monitor shells whose
supervisor identity is no longer alive. Same-boot dead supervisors are reconciled to
failed: SASE kills the recorded process group, finalizes the log, disposes the
workspace claim, and launches or records the follow-up disposition. Pre-reboot
supervisors reconcile to lost; their command effects are unknown, so the follow-up is
recorded as not launched.
In the ACE TUI¶
A monitor row renders with its own amber ⚙ glyph beside the agent list's bash/python
step glyphs, its configured label as the row title with the command as an annotation,
and a live elapsed suffix while running or an exit-code / timeout badge once terminal.
Monitor shells appear in the family roster and contribute to the family's total runtime,
but — like workflow steps — they are not counted as agents in sase stats or tribe/clan
summaries: a family with one agent and one monitor shell is a one-agent family that ran
one command. A collapsed family or clan container row carries a ⚙N badge for its
running monitors, so the count is visible without expanding the subtree.
Selecting a monitor row keeps the ordinary agent header and renders a MONITOR detail
section in place of the usual prompt and reply body. It shows the shell-highlighted
Command, then whichever of Cwd, Reason, and Next action were recorded, then
State (a colored glyph plus the state name, with (exit N) appended once an exit code
is known). Timeout reports elapsed time against the budget (3m12s of 45m0s budget),
falling back to a plain Elapsed row for a record with no recorded budget, and an
--idle-timeout adds its own Idle timeout row. The section ends with the full
Monitor id, its short form, and the exact sase monitor show <short-id> --follow
command to stream the rest from a shell.
Beneath that, an OUTPUT block renders the captured stdout/stderr. Because
live_reply.md holds a command's raw merged output rather than prose, it is rendered as
a plain ANSI-aware log rather than through the markdown path used for agent replies, and
a monitor whose retained output was capped shows an
… output truncated (head + tail retained) … notice above it. A monitor that has not
written anything yet shows No output yet..
With a running monitor row selected, the Agents tab's kill key (x by default)
stops the monitor instead of killing an agent: it opens a Stop Monitor confirmation
that defaults to Keep running, and confirming runs the same stop_monitor path as
sase monitor stop, so no follow-up agent launches. On a settled monitor row, x
behaves like an ordinary dismiss. See Agent Row Glyphs and
Sequential Agent Families.
Visibility¶
A stalled monitor handoff — a supervisor that never reported a real outcome, or a
follow-up that never launched — is not something a project owner should have to notice
by its absence. Two independent conditions render distinctly, in the Agents tab and
sase monitor list, wherever the plain exit-code/timeout badges above do not already
cover them:
- A terminal monitor with no recorded exit code. A
failedorlostmonitor whose supervisor never reported a real exit code (died on arrival, or belongs to a previous boot) renders with a red⚠badge in place of the exit-code badge — the command's outcome is unknown, not merely non-zero. - A dropped or degraded follow-up. A monitor carrying a
--nextaction that did not launch, or launched degraded, renders with an amber⚑flag independent of the monitor's own state — a monitor can finish cleanly and still strand its follow-up.
sase monitor list marks the same monitor row with the ⚑ flag next to its STATE
cell (in both the table and --format markdown output) so a stalled handoff is visible
without --json plumbing; sase monitor show <id> prints a Follow-up error line for
a dropped follow-up and a Follow-up degraded line for a degraded one, and both
commands' JSON envelopes carry followup_outcome (launched / launched-degraded /
not-launchable), followup_error, and followup_degraded_reason.
Monitors themselves are notification-neutral: a monitor is an execution and handoff
mechanism, not a workflow that files notifications, so neither a completed monitor nor a
dropped --next appends a notification row. The badges and flags above, plus
monitor_followup_outcome / monitor_followup_error in agent_meta.json and
done.json, are the durable signals — read them with sase monitor list,
sase monitor show <id>, or the Agents tab.
Example: approved epic launches¶
Launching an approved epic (sase bead work <plan> --yes-to-all …) is itself a
long-running command, so it normally runs under a monitor rather than a bare detached
proc. Its monitor shell reads EPIC APPROVED while running and uses the configured
EPIC CREATED label after every terminal state, even failure, timeout, stop, or loss;
check the state and exit details instead of treating that label as success. A successful
launch attempts to back-fill the epic ID; when that metadata lands, the planner row
itself moves to EPIC CREATED, and otherwise it remains EPIC APPROVED. The monitor
takes a zero workspace claim (the launch runs in the project's primary workspace, not
the planner's), and no follow-up agent is recorded — sase bead work launches the phase
agents itself. If the planner's agent family cannot be resolved (a very old artifacts
layout, a wiped agent), the launch falls back to the original global detached proc
submission rather than silently dropping the approval. Other monitor-start errors fail
the approval instead of using the proc fallback. See
Plan Approval Flow for the approval side of that handoff.
See also¶
- Agent Clans, Families, and Tribes for how a monitor shell fits into a sequential agent family.
- CLI Reference for the full
sase monitorcommand table. - ACE TUI User Guide for how monitor rows render in the Agents tab.