Configuration Reference¶
This document is the central reference for all sase configuration: config files, YAML sections, environment variables, and CLI flags.
Table of Contents¶
- Config File Location
- Owner Identity
- SASE Admin Center (interactive editor)
- Config tab
- Logs tab
- Machines tab
- Projects tab
- Statistics tab
- Updates tab
- Deep-Merge System
- Configuration Sections
- memory.h1_title
- generated templates
- is_sase_managed
- id
- machine_name (deprecated)
- ace
- artifacts
- artifact_refs
- llm_provider
- finalizers
- commit.message
- monitor
- repos
- dispatch
- vcs_provider
- vcs_repo_completion
- vcs_ref_completion
- axe
- file_hooks
- plugins
- mentor_profiles
- metahooks
- macros
- macro_aliases
- use_chezmoi
- commit_hooks
- gate
- max_running_agents
- max_agent_pipe_chain
- runner_slots
- agent_scope_teardown
- agent hold limits
- procs
- service
- disk
- managed_tmp
- markdown
- pager
- timezone
- chat_install
- telegram
- tmux_agent
- mobile_gateway
- sdd
- bead
- external_mirror
- feature_flags
- workspace
- telemetry
- tool_runs
- tools
- update
- Environment Variables
- CLI Flags
- Directory Sharding
- Local State Cutover
Config File Location¶
All sase configuration lives under ~/.config/sase/. The base config file is:
~/.config/sase/sase.yml
Overlay files matching the glob ~/.config/sase/sase_*.yml are merged on top of the
base file. In the SASE Admin Center new-overlay prompt, enter a single local overlay
name rather than a path: extra, sase_extra, and sase_extra.yml all resolve to
~/.config/sase/sase_extra.yml. SASE trims surrounding whitespace and rejects empty
names, . / .., or names containing / or \, so the create-overlay flow cannot
escape the user config directory. A project-local sase/sase.yml at the detected
project root usually takes highest priority. A root-level sase.yml remains an
exclusive read fallback during the
layout compatibility window; if both
files exist, SASE reports a collision instead of merging them. sase's TUI deliberately
disables project-local config loading for its own process so opening sase tui inside a
repo does not inherit that repo's agent-run settings. See
Deep-Merge System below.
Owner Identity¶
SASE has one explicit owner identity selected for the current machine. Initialize or migrate it interactively with either equivalent command:
sase config init
sase init config
The selected overlay owns both parts of the identity:
id:
username: alice
machine_name: athena
id.username is a path-safe, dot-free SASE username. It must be globally unique, should
be identical on every machine owned by the same user, and should normally be the user's
GitHub username. SASE validates its syntax and reserved names but cannot prove global
uniqueness. id.machine_name matches ^[a-z_]+$ and is unique among that user's
machines.
The bounded local state file ~/.sase/machine_name (or $SASE_HOME/machine_name) is
only a selector. It contains one machine name and is deliberately not portable
configuration; it is not the owner identity and cannot supply a missing username. SASE
discovers machine overlays by nested id.machine_name first, with deprecated top-level
machine_name accepted only as migration input. Foreign machine overlays do not
contribute runtime settings, Config inventory layers, or config-defined macros. Ordinary
overlays still participate.
Only the selected raw machine overlay can own provenance. An id value in bundled
defaults, plugins, ~/.config/sase/sase.yml, ordinary overlays, or project-local config
is ignored by runtime merging and cannot change the owner for one project. The selected
raw id object remains visible in merged configuration for inspection.
The initializer lists declared machines, suggests a schema-safe hostname when a machine
must be chosen, and requires an explicit valid username unless exactly one existing
username is clearly confirmed for reuse. It never chooses among conflicting usernames.
Creation and migration minimally set id.username and id.machine_name in the same
overlay, remove its deprecated top-level key, preserve unrelated YAML/comments, and then
write the selector. With use_chezmoi: true, the overlay edit is made in the chezmoi
source tree. Direct sase config init uses the normal commit/push/apply deployment;
bare sase init combines the edit with deferred chezmoi deployment. The initializer
also adds a hostname guard to the chezmoi source .chezmoiignore, staging it in the
same commit as the new overlay:
{{ if ne .chezmoi.hostname "<chezmoi-hostname>" }}
.config/sase/sase_<machine>.yml
{{ end }}
The guard uses chezmoi's hostname, which may differ from the SASE machine name, so the
overlay is applied only on the machine where it was initialized. If .chezmoiignore
already contains an entry for that overlay, the existing guard is left unchanged. The
same hostname guards are required when a machine overlay declares memory.h1_title so
sase memory init can emit a per-hostname H1 in the chezmoi AGENTS.md.tmpl source.
Prompting requires a TTY. sase config init --check, bare sase init --check, and
sase doctor report missing usernames, legacy migration, invalid values, selector
mismatches, duplicate overlays, and identity conflicts without writing. Config
inspection, help, initialization, doctor, and legacy history remain available while
identity is incomplete. Actual agent process creation, new commit provenance, and
agents-sidecar mutations require both identity fields and fail with the actionable
sase config init instruction.
There is intentionally no bundled identity default.
Machine hoods also provide stable ownership for the hidden agents sidecar. See Agent Hood Synchronization for privacy controls, package contents, import/publication commands, and recovery.
SASE Admin Center (interactive editor)¶
Press # in the sase tui TUI to open SASE Admin Center. The first press always
starts on its lightweight home page, where the working sections—Config, Logs,
Machines, Procs, Projects, Statistics, and Updates—are introduced
without loading their data. Config's nested catalog is alphabetized. With the default-on
admin_center_flags sunset flag it is All, Flags, Holds, Launch,
Memory, Snippets, and Macros, labeled 01 through 07. Disabling that flag
omits Flags and numbers the remaining six children 01 through 06. While home is
visible, press # again to resume the last section that was successfully active in this
sase's TUI process. Before the first section visit, the repeated key leaves home
unchanged and constructs no pane. Press 1–7 or click the numbered tab strip to enter
a section: 1 Config, 2 Logs, 3 Machines, 4 Procs, 5 Projects, 6 Statistics,
and 7 Updates. From home, Tab enters Config and Shift+Tab enters Updates; within a
working section they wrap across the same tabs. Pane-local [ / ] keys switch
sub-tabs or views where the active pane provides them, including Config's nested
catalog.
Inside a working section, the same opener key takes on a second meaning: it jumps to the section you were in immediately before the current one, and pressing it again toggles back—exactly two sections remembered, like a two-slot alternate rather than an unbounded history stack. A color-coded footer along the bottom of each working section names the jump target (or explains that none exists yet) and is itself clickable.
Each pane is constructed only on first entry and is then reused until the Admin Center
closes, preserving filters, selection, and scroll state while avoiding unrelated config,
project, machine, log, statistics, proc, update, and macro work on open. Direct commands
such as Open logs panel, Open procs panel, Open statistics, and update
actions still open their requested pane immediately and make that successfully mounted
section the next resume target. Closing and reopening with one # still returns to
home; only a second press while home is visible resumes. The top-level resume target and
alternate are persisted machine-locally and survive sase's TUI process restarts. Entry
bookmarks for Config, Logs, Machines, Projects, Procs, and Updates last only for the
current sase's TUI process. They restore by stable identity, along with minimal scope or
sub-tab context when needed, but reset when sase's TUI restarts. Filters, marks, scroll
positions, loaded data, pane instances, Statistics controls, and other pane-local state
are never carried between modal lifetimes.
Config tab¶
The Config tab answers four questions for every field — what value is effective, why (its provenance), where an edit will go, and whether it validates:
The nested Config catalog is alphabetized. When admin_center_flags is on (the
default), it is 01 All, 02 Flags, 03 Holds, 04 Launch, 05 Memory,
06 Snippets, and 07 Macros. With the bundled prefix, press 0 and then 1-7
to open those children. When the flag is off, the catalog runs from 01 All and 02
Holds through 06 Macros, and 0 then 1-6 selects them. Remap the prefix with
ace.keymaps.config.select_subtab without changing the visible default badges.
Holds lists the active agent holds with their selectors
and expiry. j / k move, d releases the highlighted hold immediately, and r
reloads the list. See agent hold limits for the TTL settings.
Flags is a keyboard-first control surface for every code-owned SASE feature flag. It
does not edit ~/.config/sase/sase.yml, overlays, project-local sase.yml, or chezmoi
source. Enable and disable write a SASE-owned machine-state file under SASE_HOME
(normally ~/.sase/feature_flags.json) and then restart sase's TUI and the service host
so new processes see the saved value. See feature_flags for
precedence, corruption behavior, and the CLI equivalent, and the
Config Flags pane for layout, keys, confirmation, and
self-disable recovery.
- Browse / inspect (read-only): a source rail lists each config layer with
loaded/missing/invalid/read-only badges; the field tree is generated from the schema
(
/filters,:jumps to a dotted path,mshows only modified fields,rrefreshes). In the tree,j/kmove through visible rows and wrap at the ends, while Down / Up use clamped navigation.'paints entry hints over the currently visible rows, and a hint key moves the cursor exactly asj/kwould, detail panel and selection bookmark included. Hints label nodes in place, so entering and leaving jump mode preserves whatever you collapsed; collapsed children are not hinted and cannot be jumped to. While hints are up they own the keyboard:Esc— or any key that is neither a hint nor the first character of one — leaves jump mode without moving the cursor, so reach for/,m,r, or:after exiting rather than during a jump (in a tree large enough for those letters to be hints, they jump instead). A rebuild of the tree that changes which fields are listed drops the hints and the jump-back history with them.'stays typable while the filter or path input holds focus. The detail pane shows the type, default, effective value, and the full provenance stack with the winning layer marked. Structured values (object maps and arrays of objects, such asaxe.routinesorrepos) render as a multi-line, syntax-highlighted YAML block instead of a one-line JSON blob, while scalars and short flat lists keep their compact inline form. - Edit (
↵oreon a field): a typed editor is generated from the schema — a toggle for booleans, an option cycle for enums, validated inputs for numbers and strings, a line editor for string lists, and a raw-YAML escape hatch for complex shapes. Pick the write scope (ctrl+tcycles user base / overlays / a selected local file;ctrl+ncreates a new overlay), or reset a field to its default (ctrl+r, which deletes the key from the chosen scope). A banner states the list-merge consequence (replace vs. append) for the chosen scope. - Preview / write (
ctrl+s): before anything is written you see the exact per-file text diff, the resulting effective merged value, and schema validation of the candidate config. The write is source-preserving (comments, key order, and quoting are kept) and is remapped to the chezmoi source tree whenuse_chezmoiis enabled.
For a chezmoi-remapped write, sase's TUI first applies the changed target; an apply
failure leaves the source edit in place and keeps the editor open. After a successful
write and any targeted apply, sase's TUI checks the file that was actually changed. If
that file is dirty inside a git repository, it offers to commit and push the change
as a tracked proc. Confirming stages that config file, commits the repository's current
index, pulls with rebase, and pushes; pre-existing staged changes are therefore included
in the same commit. The repository is discovered from the written file, so a remapped
edit uses the chezmoi source repository. When use_chezmoi is enabled, a successful
push is followed by a full chezmoi apply. Each failure stops the sequence at that
step, without undoing the written config change. Skipping the offer—or editing a file
outside git—also leaves the successful write in place. The
Launch Control uses the same workflow for persistent alias
edits, while its fixed Ctrl+E binding previews and writes
llm_provider.default_effort specifically to the user-base layer. Ctrl+E is local to
Launch Control modal (including bucket rows), not a configurable leader-key entry.
Choosing Provider default writes the empty schema sentinel; a currently active temporary
effort override remains effective until expiry or clear.
The deprecated linked_repos and sibling_repos keys remain readable as compatibility
aliases for repos.linked, but the Config tab no longer offers a one-key
migration action. Prefer editing the config to use repos.linked directly.
SASE Admin Center never writes without showing the diff and validation first, and never edits a built-in or plugin default (those layers are read-only).
Logs tab¶
The Logs tab lists each log source and a colorized tail of the selected file. After a
launch or job failure, sase's TUI toasts a leader chord (,L by default) that opens
this tab on that failure's source, highlights the matching header line, and scrolls the
detail pane to it. The jump target is session-scoped: it is the most recent error toast
in this sase's TUI process, not a durable pointer, and it degrades to the ordinary tail
with an in-pane notice if the entry has rotated out of the log.
Machines tab¶
The Machines tab is the controller's local fleet inventory. It always includes the local
controller and adds every enrolled remote alias, with columns for enrollment state,
health, capacity, last observation, and endpoint. The local row shows configured runner
capacity; every remote row shows not reported because the status response does not
include capacity. Opening or reloading the tab reads local inventory only; it never
contacts every gateway implicitly. A remote therefore starts with state not checked
and health unknown until you press s for one bounded, authenticated hello.
Use / to filter, j / k to move, U to reload inventory, and Enter to close
Admin Center and open Agents filtered to machine:<alias> (machine:local for the
local row, whose Alias column instead shows the configured machine name). c shows the
persistent enrollment flow. For a selected remote, r, R, and x show repair,
rename, and removal guidance, while y copies the displayed command; these guidance
actions do not mutate machine state by themselves. See the
Remote Dispatch Runbook for enrollment and credential handling.
Projects tab¶
The Projects tab is an inventory and lifecycle surface with three clickable sub-tabs:
Projects · Repos · Workspaces. [ / ] cycle those sub-tabs, while Tab /
Shift+Tab switch the Admin Center's main tabs.
- Projects lists true projects—projects backed by their own main ProjectSpec,
excluding
homeand internal linked-repo backing records. Enabled and disabled rows appear together with VCS kind, claim, workspace, repo, and warning counts.a/denable or disable,i/Iinitialize the marked or highlighted set or every enabled project,r/wcross-navigate to the selected project's inventories, and the established mark, alias, edit, force, and confirmed-delete actions remain available. - Repos lists primary, sidecar, linked, and opened external repos for enabled
projects by default. It reports checkout presence, source/config metadata,
auto_clone, environment names, and SDD storage mode. - Workspaces joins registry entries with active claims, PID liveness, pins,
last-used timestamps, TTL staleness, and checkout presence. Missing checkouts point to
sase workspace repair.
On Repos and Workspaces, p opens a shared project picker. Choosing a disabled project
explicitly reveals its rows; Esc clears the project scope, / text-filters within it,
and R refreshes the off-thread cached inventory.
Statistics tab¶
The Statistics tab aggregates durable agent run and activity records over a selectable time range. Its eight numbered views are 01 Overview, 02 Runners, 03 Projects, 04 Providers, 05 Activity, 06 Macros, 07 Plans & Questions, and 08 Perf. The Runners view uses today's effective global limit—including a temporary override—as present-day context, never as historical configuration. The Projects view can group by project, by Patch, or as a project-to-Patch drilldown. Macros can group by usage, model, project, or co-usage. Perf combines TUI startup and responsiveness logs with telemetry latency and reliability; its grouping cycles through subsystem, provider, and workflow. A pane-wide project filter lets you apply the same scope to the run-backed views, but Perf is global and marks the project chip not applied.
The pane loads only while visible, refreshes every 30 seconds, and performs its queries
off the UI thread. Use [ / ] to change views or press 0 followed by 1–8 to
select the view displayed as 01 through 08. Use t/T or c to choose a preset or
custom range, g to change the Projects, Macros, or Perf grouping, p/P to cycle the
project filter forward or backward, and r to refresh immediately. Keyed scope chips
keep the effective range, grouping, and project visible; the Group chip appears only
in those three groupable views and names the selected dimension there. Project scopes
use configured display names while retaining canonical keys internally. First open seeds
the current project when ace.current_project.seed_filters is on; p / P can always
cycle away from that seed. The cycle order is All projects, followed by projects
ranked by run count in the most recently loaded unfiltered result, and then wraps: p
moves forward and P backward. Return to All after changing the range to rebuild
that list for the new range. If a selected project produces an empty result, either
project-cycle key clears directly to All projects. Every populated view includes a
compact metric legend, ? opens the complete glossary and current scope, and
empty/error states show the effective keys for widening, clearing, or retrying. The
Overview Agents Run, Success Rate, and Commits tiles open Projects, while Plans Proposed
and Questions open Plans & Questions. The plan and question tiles remain all-project
values even when a project is selected; see
Telemetry: Admin Center Statistics tab for
the view contents, range syntax, and project-filter caveats, and
Reading the Admin Center Perf view
for Perf data sources and retention.
Updates tab¶
The Updates tab keeps SASE, its plugins, and its supported agent CLIs current without leaving the TUI, as one master/detail inventory rather than separate tabs per source. Every SASE core package, plugin, and registered agent CLI is a row, grouped into SASE, Plugins · Built-in, Plugins · Community, and Agent CLIs sections. The list always shows every row (installed or not), with updatable rows first in each section.
- SASE rows show the installed and latest versions of
saseandsase-core; the highlighted row's detail includes its incoming commits. - Plugins rows bring the full
sase pluginexperience into the TUI: filter the catalog, inspect a plugin, and install, update, uninstall, or switch install mode. - Agent CLIs rows are a provider-colored master/detail browser for Claude Code,
Codex CLI, OpenCode, Qwen Code, Antigravity, Muse Code, and Grok Build. Rows show
installed → latest versions, install method,
↑availability, and update marks. Missing CLIs that SASE can install showlatest v…with an[npm]or[script]badge (others show[manual]) and install withi(orSpace/Imarks for a bulk set, mixed with plugin marks in one combined flow). Details show the resolved executable, exact automatic or manual update command, skip reason, canonical vendor docs URL, and the last result; missing CLIs instead show the install route, exact command or script URL, target, and the↓ i install now · Space mark · * mark all missingcall to action. Every install previews first — exact command, target, and PATH status, plus the full SHA-256 for a script — runs without a shell (a script CLI runs the previewed bytes; an npm CLI runsnpm install -g <package>), and stays visible afterwards (row, detail, toast, and history), including "installed but not on PATH" with the exactexportline.Htoggles the agent-CLI run history between the highlighted CLI and every CLI.
The always-visible header above the list shows either the all-current banner or a digest (update counts per source, cache age, install mode, and an offline badge) plus a line per failed source; it never renders "all current" while any enabled source is unknown or failed.
Opening the tab never refreshes from the network. The first open in an ACE session
builds the inventory from local caches the automatic update check maintains (plugin
catalog, latest-version caches, editable checkouts' last-fetched upstream refs), without
PyPI/GitHub/npm or git fetch. Later opens reuse the in-memory inventory instantly;
when the automatic check has published newer results, the open re-reads those caches
instead. r refreshes everything from the network, and checked … ago is the age of
the last network check.
The Plugins rows stay visually consistent with the CLI by reusing the same catalog
loader and Rich renderables. They are split into Built-in and Community
(third-party, shown with a warning) sections; status glyphs match the CLI exactly: ●
installed, ○ available, ↑ update available. Editable / dev installs (both core
packages and plugins) carry a lowercase dev marker and are compared against their git
upstream instead of PyPI. Update actions route editable packages through the
dev-update planner and managed packages through the
uv path. Blocked editable states appear as dim reasons such as dev · local changes,
dev · diverged, dev · detached HEAD, dev · no upstream, or dev · offline.
sase's TUI computes one composite SASE/plugin/agent-CLI snapshot after first paint. The
existing ten-minute session tick only revalidates that cached snapshot and locally
probes provider names already present in it. A full inventory/network recompute is
eligible on the longer ace.updates.recompute_interval_minutes cadence (one hour by
default), while npm latest-version lookups retain their separate six-hour cache. Source
failures remain independent, so a provider lookup failure does not erase known
SASE/plugin results and vice versa.
The persistent updates: top-bar group is the top bar's only dark chip (a deep moss
surface), so it never blends with its neighbors: lime ⬆ N for SASE/plugin updates,
with a bright lime core tag when sase-core-rs requires a Rust rebuild, and a sage
CLI ⬆ N segment for supported agent CLIs. While SASE is updating, a green ⚙ gear
inset leads the badge. Mixed states join the SASE and CLI segments, and the tooltip
spells out both counts plus any manual-only CLI updates. Clicking the badge opens this
tab without mutating anything, or the Procs tab on the running update while one runs.
The global ,U action opens the Update panel from already-fetched SASE and provider
snapshots (no Admin Center, no live inventory load). The providers row lists each
captured provider with its installed-to-latest version transition and marks manual-only
providers by name, and the Everything row summarizes both legs on one line. When the
SASE leg needs a sase-core-rs Rust rebuild, the SASE and Everything rows carry the
same core tag as the top-bar badge. Lowercase e / s / p (or ⏎ / mouse on the
highlighted row) choose Everything, SASE, or providers and still require the final
y/n confirmation. Capital E / S / P plan the same scopes and skip only that
confirmation after a runnable preview succeeds; failed or already-current previews still
do not mutate. Global ,E is equivalent to ,U then capital E: it submits the same
Everything preview from cached snapshots and only the runnable preview continues to the
tracked update proc. The providers leg still captures provider names from the latest
completed automatic snapshot and never adds a newly discovered provider to that
invocation. Safe commands run sequentially; Homebrew, non-writable npm, and
unknown-provenance installs remain visible with manual guidance. The pane-wide u
remains SASE/core/plugins-only, pane-wide A remains the deliberate action for the
current agent-CLI inventory, and pane-wide a runs a tracked agents-sidecar publication
sync: it publishes and reconciles this machine's own agent hoods for every enabled
project and is not part of the comprehensive update. See
Agent Hood Synchronization for that command's
behavior.
Admin Center mutations preview first, and long confirmation panes scroll with
Ctrl+D / Ctrl+U. The Update panel's capital shortcuts are the documented exception:
they skip only the final y/n after that same preview succeeds. Plugin and core
actions show the exact uv command or editable-checkout plan. When commit previews are
enabled and a comparable range is available, confirmations for core and installed-plugin
updates load incoming commits by repository in the background; install, uninstall,
and mode-switch confirmations do not claim a commit range. An Everything confirmation
from ,U groups SASE and Agent CLI work into labeled sections with update/current/
skipped glyphs, counts, and commands (home paths display as ~/). The tracked proc runs
Agent CLI commands first and the SASE/core/plugin leg second, reporting independent
partial failures. A previews every exact agent-CLI command and every skip with its
reason and docs URL; it uses the marked subset from anywhere in the pane, otherwise it
targets every safely updatable installed CLI. Agent-CLI commands execute sequentially as
one tracked proc and refresh the browser without restarting sase's TUI; new agent
launches naturally use the updated binaries. Space (or its alias I) marks any
markable row — installable plugins, installable agent CLIs, and updatable agent CLIs —
in one shared mark set whose aggregate line counts plugin installs, CLI installs, and
CLI updates separately; Esc clears every mark, of any kind and regardless of the
active filter, before closing. All slow work runs off the event loop. Core/plugin code
changes retain the existing automatic sase's TUI and service host restart behavior after
the other legs finish. The context-sensitive keymaps are:
| Key | Action |
|---|---|
j / k |
Move the highlight down / up |
' |
Jump to a row via adaptive hints, across every section |
I / Space |
Mark / unmark the highlighted row (I is an alias of Space) |
* |
Mark / unmark every visible row in the highlighted row's section with the same action |
i |
Open the install preview for the marked set (plugins and agent CLIs together), or for the highlighted row when unmarked |
x |
Uninstall the highlighted plugin (only when installed) |
u |
Run sase update for SASE core plus all installed plugins |
A |
Update marked agent CLIs from anywhere, or every safely updatable installed agent CLI otherwise |
H |
Toggle the agent-CLI run history between the highlighted CLI and every CLI |
a |
Publish and reconcile every enabled agents repository, draining queued publication retries |
U |
Update the highlighted installed plugin when that row has an update available |
m |
Switch install mode (PyPI managed ↔ dev editable; the sase update --to analog) |
r |
Refresh — refetch the catalog and latest versions (the -r/--refresh analog) |
Ctrl+D |
Scroll the detail panel down |
Ctrl+U |
Scroll the detail panel up |
g |
Scroll the detail panel to the top |
G |
Scroll the detail panel to the bottom |
o |
Toggle offline (cache-only) mode, with a header badge (the -o/--offline analog) |
v |
Toggle verbose list columns — stars / last-updated (the -v/--verbose analog) |
/ |
Focus the filter input (matches every row's own name / description / topics, across all sections) |
# (default) |
From home, resume the last section used; in a section, jump to the previous one, press again to toggle |
Tab / Shift+Tab |
From home enter Config / Updates; otherwise switch SASE Admin Center tabs (1–8 jump directly) |
Esc |
Clear every mark first, including any hidden by the filter; close when no marks are active |
q |
Close SASE Admin Center |
The Admin Center opener is the effective ace.keymaps.app.open_config_center binding
(number_sign / # by default), so a custom binding is repeated in the same way and
appears in the landing-page hint. The remaining section-navigation keymaps above are
widget-local and are not configurable through default_config.yml.
Deep-Merge System¶
Sase builds a merged configuration through five layers, each merged on top of the previous:
default_config.yml— bundled package defaults- Plugin
default_config.ymlfiles — from installed plugin packages (viasase_configentry points), sorted by entry-point name; lists concatenate sase.yml— user config (~/.config/sase/sase.yml); lists replace defaults (not concatenate)- Selected
sase_*.ymloverlays — ordinary overlays plus only the machine overlay whose nestedid.machine_name(or deprecated top-level fallback) matches~/.sase/machine_name, sorted alphabetically; lists concatenate - Local
sase/sase.yml— project-level config at the detected project root (or the legacy rootsase.ymlfallback); lists concatenate (highest priority)
This allows splitting shared configuration across ordinary files (e.g., sase_work.yml,
sase_personal.yml) without duplication and keeping machine-specific settings in
selector-safe overlays. Plugins can provide sensible defaults that users can override,
and individual projects can customize behavior without changing global config.
Merge semantics:
| Type | Behavior |
|---|---|
| Dicts | Merged recursively (overlay keys override base keys). |
| Lists | Concatenated in layers 2, 4, and 5; replaced in layer 3 (user config). |
| Scalars | Override (overlay value replaces base value). |
For example, given a base file with two mentor profiles and an overlay or local project
config that adds a third, the merged result contains all three profiles. A user
~/.config/sase/sase.yml list replaces earlier defaults instead. If two files define
the same scalar key (e.g., axe.max_hook_runners), the later layer wins.
The axe block is the one exception to plain dictionary merging: after the layers are
combined, SASE recomposes it from the same layer chain so that keyed routine and job
entries merge by identity and legacy AXE spellings (see axe) normalize to one
effective shape.
Source: src/sase/config/core.py, src/sase/config/loading.py
Configuration Sections¶
memory.h1_title¶
Optionally customizes the Markdown H1 title of a generated managed AGENTS.md.
memory:
h1_title: "Structured Agentic Software Engineering (SASE) - Agent Instructions" # default: null
| Field | Type | Default | Description |
|---|---|---|---|
memory.h1_title |
string | null | null |
H1 title used by the sase memory init AGENTS.md generator when enabled for a scope. |
For ordinary project roots, is_sase_managed: true in that root's own sase/sase.yml
is the authorization switch. A managed project with no title derives
<project> - Agent Instructions; memory.h1_title alone does not opt a project in. The
legacy top-level amd_h1_title key has been removed; it is now reported as an
unsupported key by sase config layers instead of being silently ignored.
Home roots are the exception. For the live home root, user config from
~/.config/sase/sase.yml and ~/.config/sase/sase_*.yml can provide the home
AGENTS.md title. For the chezmoi home source root, source-side config under
dot_config/sase/ is used instead. With use_chezmoi: true, sase memory init
initializes the chezmoi home source root rather than writing a live-home AGENTS.md.
When at least one machine overlay (sase_*.yml with a valid id.machine_name) under
that chezmoi source config dir declares memory.h1_title, initialization writes
AGENTS.md.tmpl and matching *.md.tmpl provider shims whose H1 is a chezmoi hostname
switch. Each title-declaring overlay must already have the hostname guard that
sase config init writes to .chezmoiignore; missing or duplicate guards block
initialization. Machines without their own guarded title keep the last-declared-wins
fallback from sase.yml plus overlays. Without any machine-overlay title, chezmoi
generation stays static AGENTS.md as before.
Source: src/sase/default_config.yml, src/sase/config/sase.schema.json
generated templates¶
SASE packages default Jinja templates for managed agent instructions and generated
memory Markdown. Managed projects can replace them with root-relative files named in
their own sase/sase.yml:
memory:
agents_template: templates/AGENTS.template.md
agents_minimal_template: templates/AGENTS.minimal.template.md
sase_template: templates/memory-sase.template.md
readme_template: templates/memory-README.template.md
| Field | Required Jinja variables | Generated target or use |
|---|---|---|
memory.agents_template |
title, core_sections, web_sections, reference_entries |
Managed root AGENTS.md |
memory.agents_minimal_template |
title, core_sections |
Create-if-missing minimal AGENTS.md |
memory.sase_template |
project_name, linked_repo_entries |
Generated sase/memory/sase.md |
memory.readme_template |
memory_notes, total_notes, core_notes, reference_notes, web_descriptor_notes, total_lines, total_tokens |
Generated sase/memory/README.md |
{{ web_sections }} renders the entire ## Memory Webs section, including its own
heading, as one numbered H3 subsection per memory web; it is the empty string when a
root has no webs, which is what makes the whole section disappear cleanly for a webless
root. {{ reference_entries }} renders the entire Reference Memory body: the
reference-memory instruction paragraph plus one ordered-list entry per top-level
reference note. When a root has no top-level reference notes, {{ reference_entries }}
is empty. A custom agents_template must not repeat either section's prose.
The packaged managed template places {{ web_sections }} after the Reference Memory
block, so ## Memory Webs renders as the final generated H2 when a root has webs. A
custom agents_template controls its own authored layout instead: SASE does not reorder
an arbitrary custom template's H2 sections, so a project that keeps its own template in
the former Core → Memory Webs → Reference order continues to render in that order.
The legacy top-level amd_agents_template, amd_agents_minimal_template,
memory_sase_template, and memory_readme_template keys are deprecated but still read
as aliases; when both paths appear in one file, the nested memory.* form wins.
Every configured path must remain inside the project root. Rendering uses strict
variables: required placeholders must appear, unknown placeholders are rejected, and the
rendered instruction/memory structure is validated before any file is written. Generated
agent documents are numbered automatically, so custom AGENTS.template.md headings
should not carry their own numbers.
The packaged sase.md template includes the /sase_final terminal-action contract. A
custom memory.sase_template is a full-body override and may replace that default
prose; the host recovery turn remains the correctness fallback whenever a declaration is
actually required.
Home initialization uses convention-based files instead of the project-local path keys.
Put AGENTS.template.md, AGENTS.minimal.template.md, memory-sase.template.md, or
memory-README.template.md directly in ~/.config/sase/; with use_chezmoi: true, put
them in the corresponding source-side dot_config/sase/ directory. See
Memory initialization for ownership, preview, and
deployment behavior.
Source: src/sase/amd/_template.py, src/sase/main/init_memory/root_rendering.py
is_sase_managed¶
Controls whether SASE owns repository resources such as project memory, the root
AGENTS.md, and explicit SDD initialization.
is_sase_managed: false # default
| Field | Type | Default | Description |
|---|---|---|---|
is_sase_managed |
boolean | false |
Explicitly authorize SASE to manage resources in the current repository. |
Only the target repository's own checked-in sase/sase.yml is consulted for this
authorization. A legacy root sase.yml remains readable only when the canonical file is
absent. Defaults, user config, and merged overlays cannot opt repositories in globally.
When false or absent, memory init does not create, refresh, or validate project memory
and does not create or alter the root AGENTS.md; it still propagates every existing
project AGENTS.md to provider files beside it. Explicit sase repo init and its
sase init repo alias become successful no-ops before provider and storage work.
Invalid local YAML or a non-boolean marker fails safely.
This is a direct migration: memory.enabled is retired and does not authorize
repository management. Existing managed projects must replace it with top-level
is_sase_managed: true.
Home and chezmoi-home memory initialization does not use this project-local switch, and
provider instruction copies for existing project AGENTS.md files remain independent of
it.
Source: src/sase/default_config.yml, src/sase/config/sase.schema.json
id¶
Declares the explicit owner in the selected machine overlay:
id:
username: alice
machine_name: athena
| Field | Type | Default | Description |
|---|---|---|---|
id.username |
string | none | Stable per-user identity shared across that user's machines; syntax and reserved names are validated. |
id.machine_name |
string | none | Per-user machine name matching ^[a-z_]+$ and the local selector. |
The schema permits partial objects so sase config init can diagnose and repair
interrupted or legacy migrations, but provenance requires both valid fields.
machine_name (deprecated)¶
Top-level machine_name remains schema-valid only as read-only migration input for
legacy overlays:
machine_name: athena
New writers never emit this form. Run sase config init to move it under id, add the
required username, and preserve the rest of the overlay. See
Owner Identity for selection, authority, and deployment behavior.
Source: src/sase/config/core.py, src/sase/config/sase.schema.json,
src/sase/core/paths.py
ace¶
Configures sase's TUI behavior. Defaults are provided by src/sase/default_config.yml.
ace:
axe_description_expanded: true # Services-tab description panel starts expanded
artifacts:
description_mode: summary # off | summary | full
panes:
"ref:plan":
description: Project implementation plans
description_body: Proposed, approved, and archived plan documents.
relations_expanded: false # Relation panel starts collapsed as a rail; . expands it
stitches:
default_query: "sidecar:false merges:hide since:24h"
tribes:
default:
icon: "⌂"
color: "#87D7FF"
description: "Agents with no assigned tribe."
job:
icon: "†"
color: "#FFAF5F"
initially_expanded: false
description: "Scheduled AXE job automation."
updates:
startup_toast: true # show SASE/plugin/agent-CLI updates on startup
startup_toast_max_commits: 20 # total incoming subjects across repositories
post_update_toast: true # confirm the version transition after self-update restart
post_update_toast_diffstat: true # show applied file and line counts
post_update_toast_commits: true # show applied commits grouped by repository
post_update_toast_max_commits: 5 # applied subjects shown per repository
agent_cli_history: true # show Agent CLIs update history in the Updates tab
agent_cli_history_max_rows: 8 # history rows/runs shown; 0 shows all
indicator: true # show the segmented SASE + agent-CLI update badge
prebuild_rust: true # background-cache editable sase-core Rust artifacts
incoming_commits:
enabled: true # show incoming commit subjects in the Updates tab
max_per_repo: 7 # cap subjects per repository
confirm_max_per_repo: 250 # larger per-repository cap in confirmations
check_interval_minutes: 10 # attempt a periodic check this often
check_ttl_minutes: 10 # refresh latest-version checks at most this often
recompute_interval_minutes: 60 # periodic full network recompute cadence
prompt_submission:
confirm_on_enter: true # plain Enter opens the prompt submission panel first
keymaps:
config:
select_subtab: "0"
statistics:
prev_view: "left_square_bracket" # active only while Statistics is focused
next_view: "right_square_bracket"
select_view: "0"
jump_to_entry: "apostrophe"
cycle_range: "t"
cycle_range_reverse: "T"
custom_range: "c"
cycle_group: "g"
cycle_project_filter: "p"
cycle_project_filter_reverse: "P"
scroll_down: "ctrl+d"
scroll_up: "ctrl+u"
refresh: "r"
help: "question_mark"
app:
next_patch: "j"
prev_patch: "k"
edit_query: "slash" # structured query on Artifacts and Agents; defaults render as `/`
show_help: "question_mark" # app-level Help; defaults render as bare `?`
# ... all app-level keybindings are configurable
modes:
# Built-in modes (fold, copy, leader, bang) are configurable
leader_mode:
prefix: "comma"
keys:
repeat_last: "comma" # press the leader prefix, then this key; defaults render as `,,`
search_forward: "slash" # Agents metadata search; defaults render as `,/`
models_panel: "m"
update_sase: "U"
update_everything: "E"
full_history_refresh: "y" # with refresh_panel on, opens the Refresh panel
collapse_fold_by_hint: "H" # Agents fold-collapse hints; `,H`
fold_mode:
prefix: "z"
keys:
set_level_1: "1" # PR detail: set every section to level 1
set_level_2: "2"
set_level_3: "3"
cycle_stitches: "c" # `cycle_commits` is still accepted as a legacy alias
cycle_hooks: "h"
agents:
set_level_1: "1" # session 1-2; clan/agent 1-3; tribe 1-4
set_level_2: "2"
set_level_3: "3"
set_level_4: "4"
# Custom modes can be added here
my_mode:
prefix: "B"
keys:
run_tests:
key: "t"
shell: "just test"
| Field | Type | Default | Description |
|---|---|---|---|
artifact_file_viewer |
dict | see below | mpv-backed terminal video playback settings for the external artifact-file viewer. |
artifacts |
dict | see below | Per-pane settings for sase's TUI Artifacts tab. |
axe_description_expanded |
bool | true |
State the Services-tab description panel for service procs, routines, and jobs starts each session in; d toggles it in memory. |
current_project |
dict | see below | Top-bar +<project> chip and session seeds for project filters. |
keymaps |
dict | - | Configurable keybindings (see below). |
notification_indicator_max_counts |
int | 4 |
Per-tab counts shown in the top-bar notification indicator before the rest collapse into +N. |
notification_rules |
list | [] |
Ordered rules for whether a notification toasts and what sound it plays; see Delivery Rules. |
notification_tabs |
dict | see below | Per-tab colors, icons, priorities, and grouping for notification tabs. |
page_size |
int | 100 |
Ctrl+J / Ctrl+K step and the default Artifacts limit: value. Must be at least 1. Launch Control alias history uses a fixed 10-run step instead. |
prompt_completion |
dict | see below | Live soft-completion settings for sase's TUI prompt input. |
prompt_inputs |
dict | see below | Prompt input collection settings for raw <placeholder> tags and macro-save conversion. |
prompt_spellcheck |
dict | see below | Sticky misspelling highlight settings for sase's TUI prompt input. |
prompt_stash |
dict | see below | Stash Trash recovery settings for sase's TUI Prompts overlay. |
agent_decks |
dict | see below | Agent data deck spread versus paged rendering settings for the Agents tab. |
agent_header |
dict | see below | Collapsed Agents-tab header panel MACRO preview budget. |
agent_tabs |
dict | see below | Agent tab machine mode and per-tab styling for the Agents tab. |
prompt_submission |
dict | see below | Plain-Enter submission confirmation settings for sase's TUI prompt input. |
repro_output_dir |
str | "" |
Base directory for Agents-tab reproduction bundles. Empty means <SASE_HOME>/repros (default ~/.sase/repros). |
snippet_config_path |
str | "" |
Config file that receives new ace.snippets entries written from the prompt bar (see below). |
snippets |
dict[string] | {} |
Trigger-word → template mappings for prompt input snippet expansion. |
tool_calls |
dict | see below | Tool-call presentation settings for sase's TUI Agents tab. |
tribes |
dict | see below | Per-tribe sase's TUI icons and identity colors, plus Agents-tab panel initial expansion. |
updates |
dict | see below | Startup update checks, the top-bar update badge, and the one-shot post-update restart confirmation toast. |
ace.artifact_file_viewer¶
Controls terminal video playback when the external artifact-file viewer opens a video artifact through mpv.
ace:
artifact_file_viewer:
video:
audio: false
loop: false
vo: "kitty"
extra_mpv_args: []
| Field | Type | Default | Description |
|---|---|---|---|
video.audio |
bool | false |
Start playback unmuted. |
video.loop |
bool | false |
Loop playback with mpv --loop-file=inf. |
video.vo |
str | "kitty" |
mpv video output driver, such as kitty or tct. |
video.extra_mpv_args |
list[str] or str | [] |
Extra mpv arguments appended after SASE's own playback options. |
SASE launches mpv with --no-config, so put viewer-specific customization here rather
than in an mpv profile. See Video Preview for playback
keys.
ace.artifacts¶
| Field | Type | Default | Description |
|---|---|---|---|
description_mode |
str | summary |
Initial Artifacts pane description mode: off, summary, or full. The configured cycle key changes it in memory for the current session only. |
panes |
dict | {} |
Per-pane description overrides keyed by pane id (agents, stitches, patches, beads, files, or provider ids such as ref:plan). description and description_body resolve independently and are both optional for each pane. |
relations_expanded |
bool | false |
Whether the Artifacts relation panel starts expanded; . (toggle_relation_panel) toggles it in memory for the current session only. |
ace.artifacts.stitches¶
ace.artifacts.commits is a deprecated alias for this block; it still loads and warns.
| Field | Type | Default | Description |
|---|---|---|---|
default_query |
str | sidecar:false merges:hide since:24h |
Initial persistent Stitches query. Supports one configured project name, directory key, or alias in project:; startup may add the current registered project. Accepts repeatable/negatable origin:stitch, origin:auto, or origin:manual, plus repeatable/negatable type: labels including manual, automatic (auto alias), stitch, merge, patch, and observed SASE_TYPE values. merges:hide, merges:show, or merges:only still controls merge visibility. Relative windows re-anchor on refresh; changes apply on next start. |
The Stitches pane validates this value with its live query parser. Invalid runtime
configuration produces a warning and falls back to the bundled query. An empty
configured query is valid and includes sidecars; the visible canonical row renders that
state as sidecar:true merges:hide. At startup, an explicit project from sase's TUI
query takes precedence over a project: in this setting, which takes precedence over
read-only current registered-project inference. The selected project is merged into the
query before the pane is composed. project: is singular and cannot be negated or
contain an unquoted comma list. It accepts a configured project name, ProjectSpec
directory key, or alias, and known committed values are rewritten to the configured
name. Once startup merging is complete, no project: token always means a true
all-project collection. The Stitches project picker replaces only that token, and All
projects removes it while preserving the rest of the query.
Startup injects limit:<ace.page_size> (default 100) when this query has no limit:
token. An explicit limit: in the configured string — including limit:all — is left
alone, and deleting the token at runtime leaves the list uncapped. When a numeric cap
clips the result, sase's TUI keeps the token visible and shows a lower-bound total such
as [1/40+] in the repository legend while the filter row says capped. The legend's
[P/N] form means selected one-based position over displayed matched entries.
limit:all is accepted as an unlimited synonym but is omitted from canonical query
text. Day-granular until: values include the full named day. This setting is
independent of the sase stitch list CLI's sidecar opt-in and limit contract.
ace.axe_description_expanded¶
Sets whether the Services-tab description panel for service procs, routines, and jobs
starts expanded (true, the default) or collapsed to its summary line in each
sase tui session. The toggle_axe_description keymap action — d by default,
configurable under ace.keymaps.app — flips the state in memory for the rest of the
session; it never writes the toggle back to configuration, so this key is the only
durable setting. Descriptions themselves follow the
AXE description grammar, and the panel's layout, height
budget, and overflow row are described in
sase's TUI — Description Panel.
Because the Services tab claims d, the show_diff action is active only on the
Patches sub-tab.
ace.current_project¶
The current project is derived from the head of the VCS macro MRU store — the project
you last launched an agent on. sase project set-current and the Projects tab perform
the same MRU promotion without a launch.
| Field | Type | Default | Description |
|---|---|---|---|
indicator |
bool | true |
Show the +<project> chip in sase's TUI top bar, right of the default-model indicator. Governs the top-bar chip only; the Admin Center Projects tab always shows the current project regardless of this setting. |
seed_filters |
bool | true |
Seed project filters that have no value yet. Never overrides an explicit choice or an already-open surface. |
seed_agents_query |
bool | false |
Also seed the active Agents-tab query dialect with the current project's exact project: term when no submitted Agents query, including explicit empty, is remembered. |
seed_agents_query is off by default on purpose. The Agents tab is the primary
at-a-glance view, and its structured query is also read by unread-jump candidates and
prospective-clan selection -- not just the visible list. Turning this on silently
re-scopes those surfaces. The capability is fully built; one line of config enables it.
When seed_filters is on, a filter that already has a value — an explicit project: /
+name query term, or a pick made this session — is left alone. The Patches query is
one of those seeded surfaces: the seed appends a visible project:<name> term for the
session only and does not write it to last_query.txt. The Agents query seed is also
session-only unless you submit it; it does not update the Agents remembered-query file
on its own. A mid-session MRU change moves the chip but does not re-scope surfaces that
are already open.
ace.tribes¶
ace.tribes is keyed by bare tribe name (without @). The special default key
configures the reserved @default panel. Every configured tribe entry is required
to carry a description; the other fields are optional:
| Field | Type | Default | Description |
|---|---|---|---|
icon |
str | "" |
Short glyph on structured identity surfaces that already include an icon. Set "" to remove an icon inherited from defaults. |
color |
str | "" |
#RRGGBB foreground for structured tribe icons and names throughout the TUI. Set "" to restore sase's TUI gold fallback. |
initially_expanded |
bool | true |
State applied every time the Agents-tab panel comes into existence. |
description |
str | required | One-line explanation of the tribe, 1-160 characters. Shown as an unlabeled row beneath the header fields (Name/Status/Composition/Runtime/Fold) when that tribe's Agents-tab panel is selected. |
The bundled defaults use ⌂ in sky blue for default, ▲ in lavender-purple for epic,
and † in amber-orange for job. They also use ◆ for pinned and ◉ for review, whose
identities retain sase's TUI gold fallback; job starts collapsed. Because config
entries merge deeply, setting color: "" explicitly clears an inherited color without
replacing that tribe's other defaults — overriding only icon or color on a bundled
tribe still inherits its bundled description. A manual panel expand/collapse lasts
only while that panel remains live in the current sase's TUI session. On restart, or
when a tribe panel disappears and later returns, initially_expanded is applied again.
SASE bundles display config only for the tribes its own source assigns (default,
epic, job, pinned, review); a tribe your own macros assign with
%id(tribe=...), %clan(..., tribe=...), or the #tribe macro has no bundled entry,
renders with sase's TUI gold fallback and no icon until you configure it under
ace.tribes, and — once configured — requires a description like any other entry.
A missing or blank description on any configured tribe is an error-severity config
diagnostic: sase's TUI Config Center refuses to write any change while it is present,
not just an edit to that tribe. Run sase doctor -C config.tribes to list every tribe
missing a description and the exact ace.tribes.<name>.description key to set.
Identity colors apply only where sase's TUI already has a structured tribe value. They
do not scan free-form @... text or recolor selection markers, fold controls, counts,
statuses, headings, or explanatory copy. Icons likewise appear only on identity surfaces
that already include them; configuring an icon does not add one to compact name-only
rows.
sase's TUI reads this TUI setting from the user-level ~/.config/sase/sase.yml (and
user overlays), not project-local sase/sase.yml.
ace.notification_tabs¶
ace.notification_tabs colors, iconifies, and reorders the per-tab counts the top-bar
notification indicator renders, keyed by notification-panel tab key. Keys use the
user-facing tab names: the synthetic hitl, errors, general, snoozed, and muted
tabs (never the internal __snoozed__ / __muted__ spellings), a gate-declared panel
name such as beads, or a notification tag.
| Field | Type | Default | Description |
|---|---|---|---|
color |
str | "" |
#RRGGBB foreground for that tab's count chip and its tooltip label. Set "" to restore the built-in default for the tab. |
icon |
str | "" |
One emoji or display glyph for that tab's chip and tab-strip icon. Set "" to restore the built-in default for the tab. |
priority |
int | inherit | Sort weight for that tab in the panel strip and the indicator; higher sorts earlier. Omit to inherit the default for the tab's kind. |
grouping |
str | "" |
Row grouping strategy for the notification modal. "" uses the built-in default, and recent opts out to a flat newest-first list. |
Colors resolve by precedence, highest first: this setting, then a color the sending gate
declared through presentation.color, then the built-in default for a tab sase's TUI
ships knowing about, and finally a stable auto-palette entry derived from the tab key.
The last rung means a brand-new tag tab is never colorless and keeps the same color
across restarts. The bundled defaults are amber-orange hitl, sky-blue attention, red
errors, lavender-purple beads, gold general, grey snoozed, and teal muted.
Icons resolve through the same shape, with one deliberate difference at the last rung:
this setting, then an icon the sending gate declared through presentation.panel_icon,
then the built-in default for a tab sase's TUI ships knowing about, then a default keyed
by the tab's own kind (panel or tag, so an unrecognized panel or tag tab still gets
a glyph that means something about what kind of tab it is), and finally • for a tab
with no kind at all. Unlike color, an icon never falls back to a hashed auto-palette
entry — an arbitrary glyph would teach the reader something false, so the chain always
bottoms out at a meaningful or honestly generic mark instead. The bundled defaults are
⚑ hitl, ? attention, ✖ errors, ◈ beads, ✉ general, ☾ snoozed,
and ⊘ muted.
Configured icons are explicit choices and are never overridden. sase's TUI guarantees
distinctness only for SASE-chosen generic icons from the kind and last-resort rungs: on
collision it derives an unused ASCII letter or digit from the tab key, and if the key is
exhausted it keeps the generic mark rather than inventing one. Run
sase doctor -C config.notification_tabs to report two configured tabs that use the
same glyph.
Priority is an integer in -1000..1000. Omit the field to inherit the default for the
tab's kind; there is no empty-string reset, because 0 is a legitimate value. Writing
the number you want at a lower config layer is how an override is cancelled. The
defaults restated from the core's tab order are Gates (hitl) 60, any declared
panel 50, Errors 40, General 30, Done (a done tag) 20, any other tag
10, Snoozed -10, and Muted -20. The shipped beads default is 0, which
drops Beads below every ordinary tab and above the two put-away tabs. A tab whose
effective priority differs from its default renders a compact ▴ (raised) or ▾
(lowered) mark in the panel strip and the indicator tooltip.
Grouping controls section headers in the notification modal, not tab membership. The
shipped strategy is bead_type for the beads tab, grouping task-bead notifications by
task type with Due, Cleanup, and Other fallback buckets. Set
ace.notification_tabs.beads.grouping: recent to open Beads flat by default. Press S
in the notification modal to toggle the active tab for the current sase's TUI process
without rewriting config.
ace.notification_indicator_max_counts¶
Maximum number of per-tab counts the top-bar notification indicator renders before the
remaining tabs collapse into a single dim +N chip. Must be at least 1; defaults to
4. Suppressed tabs are still described in the indicator's hover tooltip.
ace.page_size¶
Integer step used by Ctrl+J (load more) and Ctrl+K (unload) on sase's TUI lists, and the
default Artifacts limit: value when a pane has no explicit cap. Must be at least 1;
defaults to 100. Invalid or missing values fall back to 100. Changing this changes the
chord step and any default query that had no explicit limit:; it does not rewrite a
user-authored limit:40. Launch Control's model-alias history panel is the exception:
Ctrl+J / Ctrl+K there always step by 10 runs, independently of this setting and of
llm_provider.model_alias_history_limit.
ace.tool_calls¶
ace:
tool_calls:
slow_threshold_seconds: 20
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
slow_threshold_seconds |
int | 20 |
0 |
Minimum tool-call duration shown as slow in Agents metadata and tool-call timelines. |
The threshold drives the SLOW TOOL CALLS section of the Agents metadata header and the
slow markers in the Agents Tab LLM Calls Panel. A
missing, negative, or non-integer value falls back to 20.
ace.updates¶
| Field | Type | Default | Description |
|---|---|---|---|
startup_toast |
bool | true |
Show the startup toast when cached status reports SASE, plugin, or supported agent-CLI updates. |
startup_toast_max_commits |
int | 20 |
Maximum total incoming commit subjects shown across all repositories in the startup toast. |
post_update_toast |
bool | true |
Show a one-shot combined result after an update changes SASE code and restarts sase's TUI. |
post_update_toast_diffstat |
bool | true |
Show per-repository applied file and line-change statistics when available. |
post_update_toast_commits |
bool | true |
Show applied commits grouped by repository when available. |
post_update_toast_max_commits |
int | 5 |
Maximum applied commit subjects shown per repository; 0 keeps totals but hides subjects. |
agent_cli_history |
bool | true |
Show the durable update-history panel beneath the selected Agent CLI row's details. |
agent_cli_history_max_rows |
int | 8 |
Maximum rows in the this-CLI view or runs in the all-CLIs view; 0 shows all history loaded for the pane. |
indicator |
bool | true |
Show the segmented SASE and agent-CLI badge when cached status reports available updates. |
prebuild_rust |
bool | true |
Prebuild exact-match editable sase-core Rust artifacts in the background; update confirmation falls back on every cache miss. |
incoming_commits.enabled |
bool | true |
Fetch and show incoming commit subjects for SASE core and plugin repositories. |
incoming_commits.max_per_repo |
int | 7 |
Maximum incoming commit subjects to show per repository in Updates-tab details. |
incoming_commits.confirm_max_per_repo |
int | 250 |
Maximum subjects fetched per repository in update confirmations; larger ranges show an explicit +N more marker. |
check_interval_minutes |
number | 10 |
Interval between local cached-snapshot revalidation attempts in a running sase's TUI session. |
check_ttl_minutes |
number | 10 |
Minimum age before a startup update check recomputes cached status; this bundled default always wins over the legacy hours key. |
check_ttl_hours |
number | unset | Deprecated and schema-valid, but currently has no effect in a normal merged config because check_ttl_minutes is always present. |
recompute_interval_minutes |
number | 60 |
Minimum snapshot age before a full SASE/plugin/agent-CLI network recompute; intervening checks only revalidate locally. |
Set check_ttl_minutes to change the startup cache TTL. Although check_ttl_hours
remains accepted for compatibility, sase's TUI resolves the merged check_ttl_minutes
value first; the bundled 10-minute default therefore prevents an hours-only override
from taking effect.
ace.keymaps¶
All TUI keybindings are configurable. The keymaps section has focused modal and pane
scopes, app-wide bindings, and prefix-mode maps:
gate — Bindings active in the shared branch controls used by plan and custom gate
modals, plus the input panel those modals open when a selection needs typed input:
| Field | Default | Description |
|---|---|---|
next_control |
j |
Focus the next branch control. |
previous_control |
k |
Focus the previous branch control. |
toggle_option |
space |
Toggle the focused option in an AND group. |
submit_primary |
enter |
Submit the gate's declared primary branch. |
submit_branch |
ctrl+s |
Submit the currently active branch and feedback. |
open_inputs |
i |
Open the input panel for the focused option's note or declared fields. |
next_input |
tab |
Focus the next field in the gate input panel. |
previous_input |
shift+tab |
Focus the previous field in the gate input panel. |
decision_next |
l,right |
Next decision choice, or set yes. |
decision_prev |
h,left |
Previous decision choice, or set no. |
decision_reset |
r |
Reset the focused decision row to ★. |
decision_reset_all |
R |
Reset every decision row to ★. |
Gate keys are scoped to the active modal and may overlap app-level bindings.
open_inputs is bound on the gate modal and opens the panel even when the selection
would otherwise submit immediately, unless the option takes no input. next_input and
previous_input dispatch only while that panel is open. activate_control remains
accepted as a deprecated alias for submit_primary.
config — Bindings active only while the Admin Center Config hub is focused. The
available actions are:
| Field | Default | Description |
|---|---|---|
select_subtab |
0 |
Arm numbered Config-child selection (01-07 when Flags is visible, 01-06 when it is not). |
statistics — Bindings active only while the Admin Center Statistics pane is
focused. The available actions are:
| Field | Default | Description |
|---|---|---|
prev_view |
left_square_bracket |
Select the previous Statistics view. |
next_view |
right_square_bracket |
Select the next Statistics view. |
select_view |
0 |
Arm 01–08 selection for a numbered Statistics view. |
jump_to_entry |
apostrophe |
Arm the same numbered-view selection used by select_view. |
cycle_range |
t |
Cycle to the next statistics time range. |
cycle_range_reverse |
T |
Cycle to the previous statistics time range. |
custom_range |
c |
Enter a custom statistics time range. |
cycle_group |
g |
Cycle grouping in the Projects, Macros, or Perf view. |
cycle_project_filter |
p |
Cycle forward through All and the latest unfiltered project ranking. |
cycle_project_filter_reverse |
P |
Cycle backward through All and the latest unfiltered project ranking. |
focus_macro |
x |
Focus one Macro in the Macros Statistics view. |
clear_macro_focus |
X |
Return the Macros Statistics view to all Macros. |
scroll_down |
ctrl+d |
Scroll the Statistics body down by half a page. |
scroll_up |
ctrl+u |
Scroll the Statistics body up by half a page. |
refresh |
r |
Refresh the active view from its durable data sources. |
help |
question_mark |
Open contextual Statistics help; the same key closes it. |
Statistics keys may overlap app-level bindings because they are registered on the focused pane, not globally.
glossary — Deprecated legacy bindings for the retired standalone Glossary panel,
accepted for one release for config-compatibility and always ignored: no defaults ship
for this scope and it builds no bindings. sase doctor warns when a loaded config layer
sets it explicitly. Move any customization to ace.keymaps.memory below — the Glossary
panel's browsing, relation-chip, and add/delete actions now live in the Memory panel,
opened the same way (gG / Ctrl+G G, seeded on the glossary term under the cursor).
memory — Bindings active only inside the Memory panel, the
browse-and-edit surface for notes, webs, and strands opened from a prompt pane with
gm, Ctrl+G m, gG, or Ctrl+G G. A value may list more than one key, separated by
commas:
| Field | Default | Description |
|---|---|---|
next_note |
j |
Move the note rail cursor to the next note. |
prev_note |
k |
Move the note rail cursor to the previous note. |
first_note |
g |
Jump to the first note. |
last_note |
G |
Jump to the last note. |
toggle_web |
space |
Expand or collapse the selected memory web. |
next_strand |
s |
Jump to the next strand in the selected memory web. |
prev_strand |
S |
Jump to the previous strand in the selected memory web. |
scroll_body_down |
ctrl+d |
Scroll the note card down by half a page. |
scroll_body_up |
ctrl+u |
Scroll the note card up by half a page. |
filter_notes |
slash |
Filter notes by stem and description. |
toggle_body_filter |
greater_than_sign |
Extend the active filter into note bodies. |
next_link |
tab |
Focus the next PARENT/CHILDREN (or SEE ALSO/REFERENCED BY on a mention strand) chip. |
prev_link |
shift+tab |
Focus the previous link chip. |
follow_link |
enter,l |
Travel to the focused chip's note or strand (or chip ① when none focused). |
travel_back |
backspace,h |
Walk back one step along the travel trail. |
next_scope |
p |
Cycle forward through the memory scope ring. |
prev_scope |
P |
Cycle backward through the memory scope ring. |
pick_scope |
ctrl+p |
Open the filterable scope picker. |
add_note |
a |
Open the add-note form, or the add-strand form on a web row. |
edit_note |
e |
Open the edit form for the selected note's type/parent/description. |
delete_note |
d |
Confirm and delete the selected note, or the selected strand. |
publish |
I |
Open the publish confirmation (sase memory init). |
open_source |
o |
Open the note or strand body in $EDITOR. |
open_viewer |
Z |
Hand the source file to the artifact viewer. |
open_history |
H |
Open the selection in the pager at the card's exact version and view (see docs/memory_history.md). |
open_changes |
C |
Open or close the Changes lens (day-grouped changesets; see docs/memory_history.md). |
history_older |
( |
Step the memory card to the older version. |
history_newer |
) |
Step the memory card to the newer version. |
history_first |
{ |
Step the memory card to the first version. |
history_now |
} |
Return the memory card to now. |
history_toggle_diff |
= |
Toggle the memory card word-diff view (sticky for the pane session). |
history_timeline |
@ |
Open or close the Timeline lens (the rail becomes the subject's version timeline). |
history_compare_base |
b |
Set or clear the Timeline lens compare base (pager hand-off carries it). |
history_toggle_hidden |
. |
Reveal hidden versions in the Timeline lens (chip shortcuts are inert there). |
toggle_deleted |
D |
Show or hide the DELETED group of tombstoned subjects (see docs/memory_history.md). |
mark_reviewed |
m |
Mark the Changes lens scope(s) reviewed through the newest changeset (see docs/memory_history.md). |
copy_body |
y |
Copy the note or strand body to the clipboard. |
copy_source_path |
Y |
Copy the source path to the clipboard. |
refresh |
r |
Re-read the current scope. |
help |
question_mark |
Open the panel-scoped help overlay. |
Like gate and statistics keys, memory keys are scoped to the panel and may overlap
app-level bindings. . then 1–9 follows a numbered link or relation chip in the
Notes lens; that prefix is a fixed pane key. history_toggle_hidden (default .)
re-routes the same physical key to the hidden-versions toggle inside the Timeline lens,
where chip shortcuts are inert — see the on_key ordering in the Memory pane. A user
override of history_toggle_hidden changes only the Timeline lens key, never the Notes
chip prefix. greater_than_sign (>) remains a valid configurable key. On the embedded
Admin Center Config sub-tab, bare digits remain top-level Admin Center tab selectors.
snippets — Bindings active only inside the
Snippets panel, the browse-and-edit surface opened from a
prompt pane with gT or Ctrl+G T. A value may list more than one key, separated by
commas:
| Field | Default | Description |
|---|---|---|
next_snippet |
j |
Move the trigger-list cursor to the next snippet. |
prev_snippet |
k |
Move the trigger-list cursor to the previous snippet. |
first_snippet |
g |
Jump to the first snippet. |
last_snippet |
G |
Jump to the last snippet. |
scroll_template_down |
ctrl+d |
Scroll the template card down by half a page. |
scroll_template_up |
ctrl+u |
Scroll the template card up by half a page. |
filter_snippets |
slash |
Filter triggers and source labels. |
toggle_body_filter |
full_stop |
Extend the active filter into raw and composed bodies. |
next_relation |
tab |
Focus the next CALLS / CALLED BY chip. |
prev_relation |
shift+tab |
Focus the previous relation chip. |
follow_relation |
enter,l |
Travel to the focused chip's trigger (or chip ① when none focused). |
travel_back |
backspace,h |
Walk back one step along the travel trail. |
next_project |
p |
Cycle forward through the enabled-project ring. |
prev_project |
P |
Cycle backward through the enabled-project ring. |
add_snippet |
a |
Open the add-snippet form. |
edit_snippet |
e |
Edit the selected config snippet, or open macro source. |
delete_snippet |
d |
Confirm and delete the selected writable snippet. |
open_source |
o |
Open the source in $EDITOR. |
open_viewer |
Z |
Hand the source file to the artifact viewer. |
copy_template |
y |
Copy the raw template to the clipboard. |
copy_source_path |
Y |
Copy the source path to the clipboard. |
refresh |
r |
Re-read the current project's snippets. |
help |
question_mark |
Open the panel-scoped help overlay. |
Like gate, statistics, and memory keys, snippets keys are scoped to the panel and may
overlap app-level bindings. Snippets still uses bare 1–9 for numbered chips and
keeps . (full_stop) as the default body-filter toggle.
projects — Bindings active on all three Admin Center
Projects-tab sub-tabs (Projects, Repos, Workspaces), so
focus_filter, jump_to_entry, reload, and the sub-tab cycle keys stay identical
across the three rather than configuring the list separately from the inventories. A
value may list more than one key, separated by commas:
| Field | Default | Description |
|---|---|---|
next_option |
j,down,ctrl+n |
Move selection to the next row. |
prev_option |
k,up,ctrl+p |
Move selection to the previous row. |
focus_filter |
slash |
Filter the active sub-tab. |
cycle_subtab |
right_square_bracket |
Cycle to the next sub-tab (Projects, Repos, Workspaces). |
cycle_subtab_reverse |
left_square_bracket |
Cycle to the previous sub-tab. |
toggle_project_mark |
m |
Toggle the mark on the highlighted project. |
clear_project_marks |
u |
Clear all marks. |
edit_project_spec |
e |
Edit the highlighted project's ProjectSpec in $EDITOR. |
edit_project_aliases |
A |
Edit the highlighted project's aliases. |
enable_project |
a |
Enable the highlighted project or marked set. |
disable_project |
d |
Disable the highlighted project or marked set. |
delete_project |
ctrl+d |
Delete the highlighted or marked SASE project directories. |
force_current_state_change |
F |
Force the last blocked disable after confirming live-work checks. |
default_project_action |
enter |
Run the highlighted project's default lifecycle action. |
reload |
R |
Reload records or the current inventory. |
show_project_repos |
r |
Show repos pre-filtered to the highlighted project. |
show_project_workspaces |
w |
Show workspaces pre-filtered to the highlighted project. |
jump_to_entry |
apostrophe |
Jump to a row via adaptive hints, within the active sub-tab only. |
pick_project |
p |
Open the shared project picker on the Repos or Workspaces sub-tab. |
clear_project_filter |
escape |
Clear an inventory project filter. |
set_current_project |
c |
Make the highlighted project current. |
initialize_project |
i |
Initialize the highlighted project or marked set. |
initialize_all_projects |
I |
Initialize every enabled project (sase init --all). |
Like gate, statistics, and memory keys, Projects-tab keys are scoped to the pane and may overlap app-level bindings.
machines — focused Admin Center Machines-tab keybindings. These bindings are
scoped to the Machines pane and do not become app-level sase's TUI shortcuts.
| Field | Default | Action |
|---|---|---|
next_option |
j,down,ctrl+n |
Select the next machine row. |
prev_option |
k,up,ctrl+p |
Select the previous machine row. |
focus_filter |
slash |
Focus the machine filter input. |
connect_machine |
c |
Show the persistent Connect flow. |
check_status |
s |
Run a bounded authenticated status check for the selection. |
repair_machine |
r |
Show repair guidance for the selected remote machine. |
rename_machine |
R |
Show the rename command for the selected remote machine. |
remove_machine |
x |
Show removal guidance for the selected remote machine. |
show_agents |
enter |
Open the selected machine's agent tab (or filter Agents when the tab strip is hidden). |
show_agents_filter |
f |
Open Agents filtered to the selected machine in all cases. |
copy_command |
y |
Copy the current Machines action command. |
reload |
U |
Reload the machine inventory. |
tool_runs — focused Admin Center Tools-tab keybindings (Runs,
Failures, and Catalog views). These bindings are scoped to the Tools pane and do not
become app-level sase's TUI shortcuts. A value may list more than one key, separated by
commas:
| Field | Default | Action |
|---|---|---|
next_option |
j,down,ctrl+n |
Select the next run, group, or catalog row. |
prev_option |
k,up,ctrl+p |
Select the previous run, group, or catalog row. |
focus_filter |
slash |
Focus the Runs filter input. |
cycle_subtab |
right_square_bracket |
Cycle to the next sub-tab (Runs, Failures, Catalog). |
cycle_subtab_reverse |
left_square_bracket |
Cycle to the previous sub-tab. |
toggle_scope |
A |
Toggle between the current project and all projects. |
focus_detail |
enter |
Focus the selected run's detail. |
jump_to_agent |
a |
Jump to the owning Agents-tab row. |
open_log |
v |
Open the retained run log in the pager. |
copy_run_id |
y |
Copy the selected run id. |
stop_run |
s |
Stop the focused live run (with confirmation). |
run_tool |
r |
Run the focused catalog tool (with confirmation). |
reload |
R |
Reload the active view from the run ledger. |
Like the Machines-tab keys, Tools-tab keys are scoped to the pane and may overlap app-level bindings.
The Agents-tab copy mode (%) gains a tool run id target on nodes with tool runs,
configured as ace.keymaps.modes.copy_mode.keys.agents.tool_run_id (default r, so
%r).
app — App-level keybindings. Each key is an action name mapped to a key string.
See src/sase/default_config.yml for the full list of configurable actions and their
defaults. Rebinding open_config_center also changes the Admin Center's home-page
resume key; it does not add a second keymap action or setting.
The Artifacts split actions are remappable as cycle_artifacts_split and
cycle_artifacts_split_reverse. Their defaults use right_curly_bracket (}) and
left_curly_bracket ({); both curly-bracket key names are accepted anywhere an sase's
TUI keybinding is configured.
The Artifacts pane brief cycles with
cycle_artifacts_description, default D. It shares that key with the Agents-only
toggle_attempt_view; see the shared-key allowlist below.
The Agents header panel toggles with toggle_agent_header, default d. It shares that
key with the Artifacts show_diff, the Services toggle_axe_description, and the
Stitches stitches_toggle_sdd actions; see the shared-key allowlist below. The toggle
is available only on the Agents tab while the header panel is shown.
The Agents jump panel toggles with toggle_agent_jump_panel, default . (full_stop).
It shares that key with the Artifacts toggle_relation_panel and the Services
toggle_hide_reverted actions; see the shared-key allowlist below. The toggle is
available only on the Agents tab while the jump panel is shown.
The Agents show/hide non-run agents toggle is toggle_hide_non_run_agents, default I.
It moved off . (which now toggles the Agents jump panel); the Services
toggle_hide_reverted keeps . for axe commands.
Agents-tab Enter is act_on_agent, default enter:
it opens the selected row's pending gate or jumps to its Patch, and offers a chooser
when more than one target applies. The older direct Patch jump remains as
jump_to_agent_patch, default unbound. A stale
ace.keymaps.modes.leader_mode.keys.jump_to_notification override (the retired ,n
chord) is ignored with a warning pointing at act_on_agent.
start_agent_from_patch, default space, prefills the prompt with the most recently
launched VCS macro, or opens a blank home-workspace prompt when there is none. The
former start_agent_home action is removed; a leftover override for it is ignored as an
unknown action. The leader ,h chord still opens a home-context prompt.
Remote Agents actions are also app-level fields. They intentionally ship as unbound:
the command palette exposes them contextually, and a configured key becomes active only
when the Agents tab and selected remote row support that action.
The top-level Agents query editor is available as the app-level edit_query binding,
default /, and the direct agents_filters binding, default f. The leader-mode
search_forward chord, default ,/, starts inline deck search on Agents only.
| Field | Default | Action |
|---|---|---|
agents_filters |
f |
Open the top-level Agents agents-live filter bar. |
agents_refresh |
r |
Refresh the Agents tab, or open the Refresh panel while the refresh_panel sunset flag is on. |
agents_retry |
R |
Retry the selected local or remote agent. |
choose_agent_grouping |
o |
Open the Agents grouping picker (p/d/s/m modes; local o toggles split/merged panels). |
view_agent_metadata |
V |
Open the selected local agent's metadata panel in the SASE pager. |
connect_agent_machine |
unbound |
Open the Admin Center Machines tab. |
setup_agent_machine |
unbound |
Open the Admin Center Machines tab for enrollment guidance. |
retry_remote_agent |
unbound |
Compatibility id: retry the selected row on its owning host. |
view_remote_agent_content |
unbound |
Fetch bounded remote chat, output, or diff content. |
answer_remote_attention |
unbound |
Answer a pending remote question or approve a pending gate. |
check_dispatch_launch_outcome |
unbound |
Reconcile the selected provisional remote dispatch-launch row with its operation outcome. |
The former toggle_layout, toggle_thinking, toggle_thinking_reverse,
choose_agent_view, next_agent_metadata_section, and prev_agent_metadata_section
app-key settings are retired. Existing overrides for those names are ignored; the Agents
tab now uses deck keys instead (next_deck_card / prev_deck_card for cards,
next_deck / prev_deck for decks, pick_deck (p) for the deck picker,
toggle_deck_split_below / toggle_deck_split_right for splits).
On the Agents tab, r refreshes and R retries; every other tab keeps r for
run_workflow and R for refresh, which is why those pairs share keys (see the
allowlist below). With the default-on refresh_panel sunset flag, both refresh actions
open the Refresh panel, and the leader full_history_refresh chord (,y) opens it with
the cursor on the Agents full-history rescan. Likewise, V opens the Agent Run Log
modal (show_agent_run_log) everywhere except the Agents tab, where
view_agent_metadata owns it.
Remote retry, stop, and fork reuse the ordinary agents_retry, kill_agent, and
edit_hooks actions when the row advertises those capabilities. See
Machines.
modes — Prefix-key mode definitions. Built-in modes (fold_mode, copy_mode,
leader_mode, bang_mode) can be reconfigured, and custom modes can be added. Each
mode has:
| Field | Type | Description |
|---|---|---|
prefix |
str | The activation key for the mode. |
keys |
dict | Sub-key definitions. For custom modes, each entry needs a key field and either shell or action. |
The built-in fold_mode direct actions are set_level_1 through set_level_3 for PR
details and the nested agents.set_level_1 through agents.set_level_4 for Agents
metadata. Their defaults produce z1-z3 on PRs; Agents accepts levels 1-2 for a
session, 1-3 for a clan or single-agent scope, and 1-4 for a selected whole tribe panel.
The configured prefix and subkeys are used by dispatch, the command palette, footers,
and help.
Query editing is one app-level action. ace.keymaps.app.edit_query controls Patches,
Stitches, Plans, Files, Services, and the top-level Agents query editor, and defaults to
bare /. The top-level Agents filter bar also has ace.keymaps.app.agents_filters as a
direct alias, default f. Agents metadata search is
ace.keymaps.modes.leader_mode.keys.search_forward, default ,/. Stale
ace.keymaps.app.search_forward and ace.keymaps.modes.leader_mode.keys.edit_query
overrides are ignored with a warning naming those replacements; they are not merged back
as runnable commands. Help is an app-level action controlled by
ace.keymaps.app.show_help and defaults to bare ?; the retired
leader_mode.keys.show_help override is dropped at load time.
Agents panel layout is picker-local now. Configure
ace.keymaps.app.choose_agent_grouping to change the opener; with the default opener,
oo toggles between tribe-split panels and one merged panel, then closes the picker.
The second o is a literal key inside the picker, not a separate configurable app or
leader binding. Old ace.keymaps.modes.leader_mode.keys.toggle_agent_panel_grouping
overrides are ignored with a warning instead of being remapped onto the picker opener.
The Agents deck picker works the same way: configure ace.keymaps.app.pick_deck
(default p) to change the opener. Pressing that opener again inside the picker is the
return gesture (last deck, or cycle-previous when the panel has no history) and is not
its own binding. The in-picker deck letters (m / f / t / n for Main, Files,
Tools, and FINAL) and their capitals (M / F / T / N, which show the deck in the
most recently focused other panel with a position glyph hint) are fixed and not
configurable. When the opener is a single letter, its capital (P by default) sits next
to M / F / T / N and shows the same resolved return deck in the other panel. p
is shared with the Artifacts pick_artifacts_project action; the two are disambiguated
by tab (see the allowlist below).
The leader update keys are separate remappable actions. update_sase opens the cached
Update panel, while update_everything directly runs the same previewed Everything flow
as ,U then capital E, including failed-preview and already-current no-op behavior.
A small allowlist of app actions intentionally shares a key because the two actions can never be available on the same surface. Validation permits exactly these pairs and rejects every other duplicate app binding:
| Shared key (default) | Spelled in YAML as | Pair | Disjoint because |
|---|---|---|---|
a |
a |
add_axe_item / open_artifact_files |
Services vs Artifacts |
d |
d |
show_diff / toggle_axe_description |
Patches vs Services |
d |
d |
toggle_agent_header / show_diff |
Agents vs Artifacts |
d |
d |
toggle_agent_header / toggle_axe_description |
Agents vs Services |
d |
d |
toggle_agent_header / stitches_toggle_sdd |
Agents vs Artifacts Stitches |
E |
E |
beads_open_bug / files_open_external |
Beads vs Files panes (the shared open-externally verb) |
w |
w |
agents_revive / beads_launch_work |
Artifacts Agents pane vs Beads pane |
w |
w |
agents_revive / reword |
Artifacts Agents pane vs Patches |
. |
full_stop |
toggle_relation_panel / toggle_hide_reverted |
Artifacts vs Services |
. |
full_stop |
toggle_relation_panel / toggle_agent_jump_panel |
Artifacts vs Agents |
. |
full_stop |
toggle_hide_reverted / toggle_agent_jump_panel |
Services vs Agents |
X |
X |
open_agent_cleanup_panel / patches_toggle_reverted |
Agents vs Patches |
D |
D |
toggle_attempt_view / cycle_artifacts_description |
Agents vs Artifacts |
_ |
underscore (or _) |
next_query / collapse_all_panel_folds |
Artifacts query history vs Agents fold sweep |
r |
r |
agents_refresh / run_workflow |
Agents vs Patches/Services |
R |
R |
agents_retry / refresh |
Agents vs every other tab |
o |
o |
choose_agent_grouping / cycle_grouping_mode |
Agents vs Artifacts |
p |
p |
pick_deck / pick_artifacts_project |
Agents vs Artifacts |
Ctrl+B |
ctrl+b |
toggle_deck_focus_reverse / scroll_prompt_up |
Agents vs Services/Artifacts |
Ctrl+Shift+F / > |
ctrl+shift+f,greater_than_sign |
swap_deck_panel_next / start_child_mode |
Agents vs Artifacts |
Ctrl+Shift+B / < |
ctrl+shift+b,less_than_sign |
swap_deck_panel_prev / start_ancestor_mode |
Agents vs Artifacts |
Ctrl+Shift+D / Ctrl+X |
ctrl+shift+d,ctrl+x |
close_deck_panel / prev_chop_run |
Agents vs Services |
Ctrl+T |
ctrl+t |
turn_deck_layout / beads_toggle_note_audience |
Agents vs Artifacts Beads pane |
configurable then o |
varies | choose_agent_grouping local panel-layout toggle |
Agents picker-local o; oo with defaults |
| configurable | varies | choose_agent_grouping / cycle_grouping_mode_reverse |
Agents vs Artifacts |
The first column is what the key looks like on your keyboard; the second is the name to
write in sase.yml, matching how src/sase/default_config.yml spells it. Punctuation
keys generally use their long name (slash, full_stop, question_mark, backslash,
vertical_line), the same convention as the curly-bracket names described above; the
raw \ and | glyphs are also accepted for the last two.
The allowlist is keyed by action pair, not by key, so moving one of these actions onto a
different key keeps the exemption, and pointing a third action at a shared key does not
gain it. Only actions you overrode are checked: an override that collides with any
action outside its allowed pair is logged as a duplicate and reverted to that action's
default, leaving the rest of your keymap in place. The shipped V shared by
show_agent_run_log and view_agent_metadata is not on the list: it loads as shipped,
but moving both actions onto one new key is logged as a duplicate and both revert to
V.
ace.keymaps.app.start_saved_query_mode (default 0) arms direct saved-PR-query slot
selection: press it, then a slot digit (1-9, then 0) to load that slot. Its digit
sub-keys are not configurable -- they are the slot identifiers themselves, the same way
start_checkout_mode's 1-9 workspace digits aren't. The prefix is scoped to the
Artifacts tab (any sub-tab); it does not arm on Agents or Services.
Custom mode key fields:
| Field | Type | Required | Description |
|---|---|---|---|
key |
str | yes | The sub-key to press after the prefix. |
shell |
str | no* | Shell command to execute. |
action |
str | no* | Built-in action name to invoke. |
*Exactly one of shell or action must be provided.
The keymap loader validates configuration: invalid keys are reverted to defaults, duplicate bindings within a scope are warned, and prefix conflicts between custom modes and app bindings are detected.
Source: src/sase/default_config.yml, src/sase/ace/tui/keymaps/
ace.snippet_config_path¶
Names the config file that receives new ace.snippets entries written from the prompt
bar — the gt / Ctrl+G t snippet target pane, the gX / Ctrl+G X save panel's
snippet mode, and the Snippets panel add form all default to
it.
An empty string (the default) resolves to the user's sase.yml — the chezmoi source
file under dot_config/sase/ when use_chezmoi is enabled, otherwise
~/.config/sase/sase.yml. A relative configured value resolves against
~/.config/sase/, so sase_snippets.yml means ~/.config/sase/sase_snippets.yml. The
path's suffix must be .yml or .yaml; the file itself need not exist yet, but its
parent directory must be writable.
A configured value that is unusable — wrong suffix, unwritable parent, invalid YAML, or
a project sase/sase.yml that still needs its legacy migration — falls back to the
default and reports why: both the gt location picker (as a footer warning) and
trigger-name panel and the gX save panel's snippet mode append the reason to the
destination line (e.g. configured path unusable: read-only) rather than silently
writing somewhere else. When set to a usable file, it is the ★ configured default of
the snippet location picker and outranks last-used. The gX save panel additionally
always offers the resolved ace.snippet_config_path destination as a selectable,
pre-highlighted row — even when it is a custom filename or path that falls outside the
standard discovered locations (sase.yml / sase_*.yml under ~/.config/sase/ or the
chezmoi equivalent, and the project's sase/sase.yml) — so a configured preference is
never silently dropped from the picker.
ace:
snippet_config_path: "sase_snippets.yml"
See docs/ace.md — Authoring a snippet from the prompt bar.
Source: src/sase/macro/snippet_targets.py
ace.snippets¶
Defines expandable text snippets for the prompt input widget. Each entry maps a trigger
word to a template string. Press Tab in the prompt input to expand the trigger word
before the cursor.
ace:
snippets:
fix: "Please fix the following issue:\n$0"
review: "Review this code for correctness, performance, and style."
plan: "#plan\n$0"
Templates can contain $1, $2, ... tabstops plus $0 for the final cursor position.
In sase's TUI prompt input, Tab advances through those stops and Shift+Tab retreats
through stops already visited. Expanding a trigger inside an active snippet nests the
new snippet's tabstops before the remaining outer stops. Templates can also splice
another merged snippet with #[trigger]; use #[trigger(value)] or #[trigger:value]
to fill referenced $1, $2, ... tabstops before splicing.
Every effective snippet also gains a generated initial-capital alias: only the first
character of the trigger and of the resolved template is uppercased, so
foo: "foo bar baz" also exposes Foo → Foo bar baz. Already-capitalized,
digit-leading, and underscore-leading triggers produce no extra entry, and an explicitly
authored Foo is never replaced. These aliases are runtime-only and are never written
back into config. See docs/ace.md — Capitalized aliases
for the full rule.
See docs/ace.md — Snippets for usage details and
docs/macros.md — Snippet CLI for sase snippet list, show,
add, and delete.
Source: src/sase/ace/tui/widgets/prompt_text_area.py
ace.prompt_completion¶
Controls automatic non-disruptive suggestions and manual prompt-local and prompt-history
word completion in sase's TUI prompt input. Suggestions appear in the prompt-bar
subtitle and are accepted with Ctrl+L; Enter still submits the prompt as typed.
Manual structured/path Ctrl+T completion is independent of the automatic settings, and
the Ctrl+R recursive fuzzy file finder is always manual.
ace:
prompt_completion:
auto: soft
debounce_ms: 90
auto_file_paths: false
auto_macro_menu: true
auto_directive_menu: true
auto_artifact_menu: true
auto_jinja_menu: true
max_auto_rows: 1
history_word_count: 10000
common_placeholder_count: 100
word_min_length: 5
word_ranking: smart
word_ranking_signals: true
placeholder_ranking: smart
placeholder_ranking_signals: true
next_word: auto
next_word_max_words: 4
next_word_confidence: balanced
next_word_sources: [history]
| Field | Type | Default | Description |
|---|---|---|---|
auto |
bool/string | soft |
Automatic mode. soft, true, on, yes, or 1 enable subtitle suggestions; false/off disables them. |
debounce_ms |
int | 90 |
Delay before computing a live suggestion after text or cursor changes. |
auto_file_paths |
bool | false |
Allow live suggestions to scan file-path candidates. Manual Ctrl+T file completion still works when false. |
auto_macro_menu |
bool | true |
Automatically open the macro/skill completion menu while typing matching #name, #!name, or /skill tokens. |
auto_directive_menu |
bool | true |
Automatically open directive completion while typing % tokens, fixed values such as %model:, =alias, and ==model shortcuts. |
auto_artifact_menu |
bool | true |
Automatically open the grouped @ reference menu from bare @, narrowed path/kind queries, or @kind: payloads. |
auto_jinja_menu |
bool | true |
Automatically open the Jinja variable completion menu while typing inside a {{ }} or {% %} tag. Manual Ctrl+T still works when off. |
max_auto_rows |
int | 1 |
Reserved row limit for automatic completion modes; current soft mode shows one suggestion. |
history_word_count |
int | 10000 |
Maximum unique recent prompt-history words retained for manual completion; 0 disables the history fallback. |
common_placeholder_count |
int | 100 |
Maximum saved <placeholder> tags retained and offered after prompt-local placeholder matches; 0 disables them. |
word_min_length |
int | 5 |
Shared minimum length for prompt-local and prompt-history word candidates; values below 1 clamp to 1. |
word_ranking |
string | smart |
History-word ordering. smart ranks by relation, recency, and frequency; recent restores plain most-recently-used order. |
word_ranking_signals |
bool | true |
Whether smart-ranked history-word rows render the score meter, dominant-reason chip, and panel legend. |
placeholder_ranking |
string | smart |
Saved-placeholder ordering. smart ranks by relation, recency, and frequency; recent restores stored count-then-recency order. |
placeholder_ranking_signals |
bool | true |
Whether smart-ranked saved-placeholder rows render the score meter, dominant-reason chip, and panel legend. |
next_word |
string | auto |
Next-word prediction mode. auto predicts after each typed character and completes the current word while you type it. chain arms a ghost or border peek only after word commits and on explicit Ctrl+T. off disables predictions. |
next_word_max_words |
int | 4 |
Maximum words in one ghost or peek; clamped to 1–8. A mid-word continuation counts the completed word toward this cap. |
next_word_confidence |
string | balanced |
Confidence gate for ghost predictions: cautious, balanced, or eager. |
next_word_sources |
list | [history] |
Prediction corpus sources. Add archive to also learn from the pruned cross-machine prompt archive at low weight; history is always kept. Unknown entries are dropped. |
The minimum applies to the complete candidate, so a shorter typed prefix can still
complete an eligible word. Prompt-local words below the threshold are skipped before
sase's TUI considers the prompt-history fallback. Candidates from history retain their
original spelling and, under the default word_ranking: smart, are ordered by a
weighted composite of how strongly each word relates to the words already in the prompt
(0.50), how recently it was used (0.30), and how often it was used (0.20). Setting
word_ranking: recent restores plain most-recently-used order. A warm next-word model
promotes current-word matches ahead of that order only while word_ranking is smart
and next_word is not off. recent leaves the menu on its non-prediction order. The
warm cache holds the prompt-word index off-thread and is rebuilt when history shards or
the shared minimum change, while Ctrl+D deletions apply at query time without a
rebuild. Setting history_word_count: 0 disables only the history fallback; eligible
prompt-local words remain available. See the History-word completion bullet in
docs/ace.md for the score meter, reason chip, and legend that word_ranking_signals
controls.
Common placeholders are stored at sase_home()/prompt_placeholders.json and are learned
from complete raw <foobar> tags outside literal zones in submitted, failed-launch, and
cancelled prompt drafts. When the store is first created, sase's TUI seeds it once from
bounded prompt history so existing tags can appear immediately. Retention evicts
least-recently-used entries down to common_placeholder_count. By default
(placeholder_ranking: smart) the < menu ranks saved tags by relation to the prompt
being edited, recency, and frequency; placeholder_ranking: recent restores the stored
count-then-recency order. Setting common_placeholder_count: 0 disables recording,
loading, and display of saved placeholders; prompt-local placeholder completion still
works.
The former history_word_min_length key has been replaced by word_min_length.
Existing overrides must rename the key to keep controlling word completion.
The +query project/Patch picker uses the same completion panel and opens when the plus
is at the start of the prompt or directly follows whitespace, {, or |. It is not
disabled by auto_macro_menu. Manual Ctrl+T project/Patch completion uses the same
token rule and works regardless of these automatic-completion settings.
@ reference completion uses a project-scoped artifact catalog and warm prompt path
inventory. auto_artifact_menu controls automatic opening of the grouped menu from bare
@, narrowed artifact-kind or local-path queries such as @pl and @src/, and
@kind: payload contexts; manual Ctrl+T remains available. Before a : appears,
local file rows stay hidden while the query prefix-matches an artifact kind (including
bare @). The panel's [^T] files hint marks that state; the first Ctrl+T reveals
files without completing the kind, and a later press completes normally. Queries with no
kind prefix match show file rows automatically. File rows preserve the @ sigil on
insertion, directories drill down, and dotfiles are hidden unless the typed path segment
starts with .. A cold path inventory can briefly show a loading row while sase's TUI
refreshes it off-thread. Document, chat, indexed-file, bead, and agent payloads use
bounded project-scoped catalogs; commit and bug candidates are projected only from
already-loaded Artifacts-pane snapshots.
The %model: / %m: value menu and the =alias / ==model model shortcut menus are
also controlled by auto_directive_menu. The %model: menu lists inline-typable model
names, the five built-in size aliases (@xsmall, @small, @medium, @large,
@xlarge), configured model aliases, and provider drill-down rows. Provider short
aliases are shown as filter/display hints but are not inserted. The =alias shortcut is
alias-only: accepting =la on @large rewrites the token to %m:@large. The ==model
shortcut lists concrete model rows, so accepting ==gpt can rewrite the token to
%m:gpt-6.1-sol; provider-qualified input such as ==codex/g narrows to that provider.
The old *alias and **model forms remain ordinary prompt text. Ctrl+T remains
available for both shortcut menus when automatic directive menus are disabled.
File-path completion roots relative lookups in the prompt-selected workspace. A
+<project> project tag, registered workspace-provider refs,
and known-project refs such as #git:<project> or #gh:<owner>/<repo> can root lookup
in that project checkout. If no prompt workspace ref resolves, lookups fall back to the
TUI process directory. These root rules are shared by live path suggestions, manual
Ctrl+T path completion, and the manual Ctrl+R recursive finder.
Source: src/sase/ace/tui/widgets/prompt_completion.py,
src/sase/ace/tui/widgets/_prompt_soft_completion.py,
src/sase/ace/tui/widgets/history_word_completion.py,
src/sase/history/prompt_word_index.py, src/sase/history/prompt_word_ranking.py,
src/sase/history/prompt_placeholders.py,
src/sase/ace/tui/widgets/prompt_completion_root.py,
src/sase/ace/tui/widgets/recursive_file_finder.py
ace.prompt_spellcheck¶
Controls the sticky misspelling highlight in sase's TUI prompt input. Every word K
proves misspelled (an aspell misspelled verdict) is remembered durably and underlined
in every prompt input from then on; K on a word already remembered is what teaches
sase's TUI about it, not a background spell-checker.
ace:
prompt_spellcheck:
highlight: true
max_remembered_words: 5000
| Field | Type | Default | Description |
|---|---|---|---|
highlight |
bool | true |
Whether remembered misspellings are underlined in prompt inputs. K still records and clears misspellings when false. |
max_remembered_words |
int | 5000 |
Maximum words retained in each of the misspelled and accepted-word lists; 0 disables remembering new misspellings. |
Remembered words are stored at sase_home()/prompt_misspellings.json, casefolded for
matching but keeping the first-seen spelling. Pressing a in the correction panel
accepts a word instead of applying a suggestion, moving it into the accepted list so it
is never flagged again; K on an already-correct remembered word (for example, after
adding it to your aspell personal dictionary) clears it automatically. Retention
evicts the oldest entries once a list exceeds max_remembered_words.
Source: src/sase/history/prompt_misspellings.py, src/sase/core/word_lookup.py,
src/sase/ace/tui/widgets/_misspelling_highlight.py,
src/sase/ace/tui/actions/_startup_misspellings.py,
src/sase/ace/tui/modals/spellcheck_panel_modal.py
ace.prompt_stash¶
Controls Stash Trash recovery in sase's TUI Prompts overlay (see Prompts Overlay). Trash recovers only drafts deliberately discarded from Stash, up to the configured row limit.
ace:
prompt_stash:
trash_limit: 100
| Field | Type | Default | Description |
|---|---|---|---|
trash_limit |
int | 100 |
Maximum Trash rows kept. An entry-count limit, not a byte quota. 0 disables Trash recovery and permanently discards rows marked for Trash (rows stay recoverable with sase prompt stash-archive). Must be an integer >= 0; booleans and malformed values fall back to 100. |
A lowered limit is applied the next time the overlay opens. Over-limit rows are
permanently deleted, oldest discarded first, and a toast reports how many were deleted.
The Trash list in that same window still shows the rows from before the deletion.
Enter on one of those already-deleted rows does not bring it back. The Trash view then
repaints from the store, so every already-deleted row disappears. Close the overlay and
open it again to see the rows that remain without pressing Enter. See
Prompts Overlay for the toast text and the failure case.
Trash requires the current stash core binding: restart old TUI processes before the new
behavior takes effect, since mixed-version operation is unsupported.
Source: src/sase/ace/config.py, src/sase/core/prompt_stash_facade.py,
src/sase/ace/tui/actions/agent_workflow/_prompt_bar_stash_restore.py
ace.agent_decks¶
Controls whether a multi-card agent data deck renders spread (every card on one scrollable page) or paged (one card at a time) on the Agents tab deck panels, under the automatic deck view (see Deck Views). A fixed deck view bypasses these thresholds entirely.
ace:
agent_decks:
spread_max_screens: 1.5
block_spread_max_screens: 1.5
final_tail_delay_seconds: 5.0
Jump to any deck with p then its picker letter: m Main, f Files, t Tools, n
FINAL (capitals open it in the other panel). See
Agent data decks and cards and
Finalizers on the Agents tab.
| Field | Type | Default | Description |
|---|---|---|---|
spread_max_screens |
number | 1.5 |
A multi-card deck renders spread when its cards fit within this many panel viewport heights ("screens"), and paged otherwise. 0 means always paged. Applies to the automatic deck view only; a fixed view bypasses measurement. |
block_spread_max_screens |
number | 1.5 |
A card shown alone renders its card blocks spread (all inline) when the card fits within this many panel heights, and paged (one block per page) otherwise. 0 means always one block per page. Applies to the session Reply card's per-turn blocks. Applies to the automatic deck view only; a fixed view bypasses measurement. |
final_tail_delay_seconds |
number | 5.0 |
Seconds a finalizer op must run before its live tail renders in the ⊛ FINAL deck. 0 renders immediately; fast ops go straight from running to done with no tail. Non-numbers, negatives, and booleans fall back to the default. |
Source: src/sase/ace/tui/agent_decks_settings.py
ace.agent_header¶
Controls how many rows of the agent's AGENT MACRO the collapsed Agents-tab header
panel previews below its two chip rows. The preview shows at most
collapsed_preview_max_rows rows, and fewer when collapsed_max_share leaves less room
on a short column.
ace:
agent_header:
collapsed_max_share: 0.35
collapsed_preview_max_rows: 3
| Field | Type | Default | Description |
|---|---|---|---|
collapsed_max_share |
number | 0.35 |
Share of the detail-column height the collapsed header may take (0 to 0.6). The preview gets that cap minus the border, chip rows, and MACRO tab row, at least 1 row. 0 turns the preview off. |
collapsed_preview_max_rows |
integer | 3 |
Most macro preview rows the collapsed header shows (at least 1). The collapsed_max_share budget can lower it on short columns. d expands to the full prompt. |
Source: src/sase/ace/tui/agent_header_settings.py
ace.agent_tabs¶
Agent tab settings for the Agents tab. machine_tabs selects machine-tab mode: auto
shows one machine tab per configured dispatch machine (and a single roster when no
machines are configured), on always uses machine tabs, and off never does. tabs
holds per-tab styling and ordering keyed by canonical tab name; invalid names are
dropped.
ace:
agent_tabs:
machine_tabs: "auto"
launch_from_view: true
tabs:
sase:
color: "#AF87FF"
icon: "◈"
order: 0
description: "SASE project agents."
| Field | Type | Default | Description |
|---|---|---|---|
machine_tabs |
string | "auto" |
auto uses machine tabs when dispatch machines exist, on always does, off never does. Unknown values fall back to auto. |
launch_from_view |
bool | true |
Launch new agents onto the currently visible tab. Non-boolean values fall back to true. |
tabs |
dict | {} |
Per-tab color, icon, order (integer or null), and description, keyed by canonical tab name. |
Source: src/sase/ace/tui/agent_tabs_settings.py
ace.prompt_submission¶
Controls whether plain Enter asks for confirmation before launching agent prompts from
sase's TUI prompt input.
ace:
prompt_submission:
confirm_on_enter: true
| Field | Type | Default | Description |
|---|---|---|---|
confirm_on_enter |
bool | true |
When true, plain Enter opens the submission panel and Enter inside that panel confirms its primary launch action. |
When enabled, the panel appears for non-empty ordinary single-pane prompts, targeted
single-pane drafts, and multi-pane prompt stacks. g<enter> in NORMAL mode and
Ctrl+G Enter in INSERT mode stay direct selected-pane launch routes. Set
confirm_on_enter: false to restore immediate plain-Enter launch: a single pane sends
normally, a stack submits the active pane, and a targeted pane launches instead of
opening the launch/write/save chooser.
Source: src/sase/ace/tui/prompt_submission_settings.py,
src/sase/ace/tui/widgets/_prompt_text_area_key_handling.py,
src/sase/ace/tui/modals/prompt_submit_choice_modal.py
ace.prompt_inputs¶
Controls how sase's TUI treats raw <placeholder> tags when a prompt is submitted or
saved as a macro.
ace:
prompt_inputs:
collect_raw_placeholders: true
macro_placeholder_args: true
| Field | Type | Default | Current behavior |
|---|---|---|---|
collect_raw_placeholders |
bool | true |
When true, submitting an sase's TUI prompt opens Fill in this prompt for each live raw placeholder. When false, raw tags launch unchanged; declared input: collection still works. |
macro_placeholder_args |
bool | true |
When false, gX, gL, and fresh gx extraction keep live raw tags as literal text and mint no placeholder-derived text inputs. Jinja-variable input inference for gL is unaffected. |
Raw placeholders in YAML frontmatter, inline code, fenced code, or
%macros_enabled:false regions are never collected. See
Raw Prompt Placeholders for the submit panel,
literal-tag control, and macro conversion workflow.
Source: src/sase/agent/prompt_placeholder_inputs.py,
src/sase/ace/tui/actions/agent_workflow/_launch_start.py,
src/sase/ace/tui/actions/agent_workflow/_prompt_bar_save_macro.py,
src/sase/ace/tui/widgets/_prompt_input_bar_local_macro_actions.py,
src/sase/ace/tui/widgets/_local_macro_conversion.py
artifacts¶
Bounds on automatic artifact capture at agent finalization, and the opt-in retention
policy that bounds the store afterwards. Capture keeps bytes only for files a run
authored that version control cannot reproduce; content already reachable from a durable
commit becomes a byte-free reference row. See
VCS-Backed Artifact Files for the decision
matrix and the vcs-cache directory, and
Store Lifecycle for the report → dry run → opt-in
retention progression that artifacts.retention completes.
artifacts:
capture:
max_stored_per_agent: 50
max_history_scan: 20
max_file_size_bytes: 104857600
pool_max_bytes: 1073741824
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
artifacts.capture.max_stored_per_agent |
int | 50 |
1 |
Maximum byte-copying automatic captures per agent run. Reference rows cost no bytes, are uncounted, and uncapped. |
artifacts.capture.max_history_scan |
int | 20 |
1 |
Durable commits searched per file when looking for one holding its exact content. |
artifacts.capture.max_file_size_bytes |
int | 104857600 |
1 |
Maximum size of one file copied into the workspace-local prompt-artifact pool; larger files are hashed and recorded instead. |
artifacts.capture.pool_max_bytes |
int | 1073741824 |
1 |
Workspace-local prompt-artifact pool budget before opportunistic garbage collection removes published terminal-run copies. |
These fields are read fail-open: a missing, non-integer, or out-of-range value falls
back to the built-in default rather than failing capture. Once max_stored_per_agent is
reached, the remaining byte-copy candidates are skipped and finalization reports
cap_fired=true on its [artifacts] default capture: summary line. Raising
max_history_scan widens the bounded search that recovers content whose recorded commit
was squash-rewritten, at the cost of a longer walk per file.
max_file_size_bytes and pool_max_bytes apply to launch-time prompt-artifact staging
under the workspace-local .sase/artifacts/ tree. The local manifest still records
oversized files, but SASE does not copy their bytes into .sase/artifacts/pool/. See
Prompt Artifact Staging and Archive
for the layout and garbage-collection rules.
artifacts.retention is the opt-in policy that runs once after each agent finalization,
immediately after automatic capture. It ships disabled with generous values pre-filled,
so enabling it later is a flag flip rather than a policy design exercise.
artifacts:
retention:
enabled: false
empty_shard_removal_budget: 2000
keep_per_label: 3
keep_recent_run_months: 2
max_age_days: 90
trash_grace_days: 14
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
artifacts.retention.enabled |
bool | false |
- | Run the artifact-file retention pass after agent finalization. While false, retention removes nothing. |
artifacts.retention.empty_shard_removal_budget |
int | 2000 |
1 |
Max empty ace-run month/day/run shard directories listed by one sase artifact prune-runs preview. |
artifacts.retention.keep_per_label |
int | 3 |
0 |
Newest automatic captures kept per label; older generations are trashed first. 0 disables the predicate. |
artifacts.retention.keep_recent_run_months |
int | 2 |
1 |
Calendar months of ace-run directories kept whole by sase artifact prune-runs. |
artifacts.retention.max_age_days |
int | 90 |
0 |
Trash automatic captures created more than this many days ago. 0 disables the predicate. |
artifacts.retention.trash_grace_days |
int | 14 |
0 |
Days a trashed artifact stays restorable before a purge removes it. |
These fields are read fail-open the same way the capture fields are. The pass is bounded
and defensive: it never fails a run, and it removes nothing that retention's protection
contract keeps — explicit artifacts, artifacts referenced by a ProjectSpec, plan, bead,
or research document, artifacts recorded in the consumption ledger, and the newest
capture of every label. If any required protection source cannot be read, the whole pass
is skipped rather than under-protecting, and finalization prints
[artifacts] retention skipped: protection sources unavailable: <sources>. Otherwise it
prints one [artifacts] retention: line with rows trashed, bytes reclaimed, and trash
entries purged.
The same values drive the manual surfaces, so a dry run previews exactly what enabling
retention would do: keep_per_label is what sase artifact prune plans with when -g
is omitted, both predicates define the default-policy selection sase artifact stats
reports last, and trash_grace_days is the cutoff sase artifact trash purge honors
without -a/--all and the one trash list marks entries against. Setting both
predicates to 0 leaves a policy that selects nothing.
keep_recent_run_months drives sase artifact prune-runs, which previews old terminal
artifacts/ace-run/ directories plus empty month/day/run shards outside sase's TUI
startup watch window. Deletion is currently preview-only: --apply fails closed before
removing run directories, empty shards, or artifact-index rows. The preview protects
recent months, incomplete runs, referenced agent names or paths, artifact-file
producers, and runs tied to non-closed beads. empty_shard_removal_budget bounds how
many empty shard directories one preview lists.
Source: src/sase/config/core.py, src/sase/core/artifact_capture_policy.py,
src/sase/core/artifact_file_retention.py,
src/sase/core/agent_artifact_run_retention.py,
src/sase/axe/run_agent_exec_finalize.py
artifact_refs¶
Allow-lists path-backed @file:<path> prompt references. Indexed
@file:default:<digest> and @file:explicit:<digest> references do not need these
roots; this section controls only references authored as absolute or ~/ paths.
artifact_refs:
file:
roots:
- name: notes
path: ~/notes
path_globs: ["**/*.md", "!private/**"]
- name: reports
path: /srv/reports
| Field | Type | Required | Description |
|---|---|---|---|
artifact_refs.file.roots[].name |
string | yes | Stable lowercase slug used in published logical identities. |
artifact_refs.file.roots[].path |
string | yes | Absolute or ~/-rooted allow-list directory. |
artifact_refs.file.roots[].path_globs |
list[string] | no | Root-relative POSIX includes and !-prefixed exclusions. |
Root lists concatenate across ordinary config layers. A layer using the global
list-replacement merge strategy replaces the inherited list instead. When the same
name appears more than once, the later root overrides the earlier definition without
changing its position. Invalid entries are skipped with a warning rather than disabling
all usable roots.
Resolution accepts existing regular files only, requires the path to stay within an
effective root and its glob policy, and never interprets relative paths against the
current directory. At launch SASE snapshots accepted bytes into the workspace-local
artifact pool, so subsequent changes to the source file do not change the agent's
captured input. The normal artifacts.capture.max_file_size_bytes limit applies.
Run sase doctor -C config.artifact_refs to find malformed, missing, nested,
overlapping, or zero-usable-root configurations. See
Artifact References for prompt syntax and context rules.
llm_provider¶
Configures which LLM backend sase uses and how size aliases map to concrete models. See docs/llms.md for the full LLM provider architecture, preprocessing pipeline, and invocation lifecycle.
llm_provider:
provider: claude # or "codex", "qwen", "opencode", "agy", "muse", "grok", "fakey" (default: auto-detect)
# Scalar launch settings. Same grammar as %model; may reference a built-in
# size alias.
default_model: "@large" # used when a launch has no %model directive
epic_lander_model: "@large" # epic land agents below bead.big_epic_phase_threshold
big_epic_lander_model: "@xlarge" # epic land agents at/above that threshold
model_alias_history_limit: 10 # runs shown per alias in Launch Control history
# Override examples; shipped size-alias defaults are generated in docs/llms.md.
model_aliases:
builtin:
medium: codex/gpt-6.1-sol # specialize the medium size alias
large: claude/opus | codex/gpt-6.1-sol # custom large-phase pool
custom:
blogger:
model: claude/opus
description: Agents that draft and edit blog posts.
bucket: writing # optional Launch Control grouping
buckets:
writing:
description: Writing and editing roles.
| Field | Type | Default | Description |
|---|---|---|---|
llm_provider.provider |
string | auto-detect | Which registered provider to use. Auto-detects by plugin-declared priority; built-ins default to claude → codex → qwen → opencode → agy. muse and grok declare no priority and are never auto-detected; name them explicitly. |
llm_provider.model_tier_map |
dict | - | Accepted by the config schema for compatibility. No runtime path currently reads it; setting large/small here has no effect. Size aliases and default_model / lander settings select models. |
llm_provider.default_model |
string | @large |
Model expression used when a launch has no explicit %model directive. |
llm_provider.epic_lander_model |
string | @large |
Model expression used by epic land agents when the epic has fewer authored phases than bead.big_epic_phase_threshold. |
llm_provider.big_epic_lander_model |
string | @xlarge |
Model expression used by epic land agents when the epic has bead.big_epic_phase_threshold or more authored phases. |
llm_provider.default_effort |
string | "" |
Reasoning effort applied when a launch requests none: none, minimal, low, medium, high, xhigh, or max. Empty leaves each provider on its own default; unsupported levels are skipped with a warning. |
llm_provider.model_alias_history_limit |
int | 10 |
Maximum prior runs returned per alias for the Launch Control agent-history panel. Must be at least 1; malformed runtime values defensively fall back to 10. |
llm_provider.model_aliases.builtin |
dict | - | Overrides for the five built-in size aliases (xsmall, small, medium, large, xlarge). Values use the single-target grammar, \| round-robin pools, \|\| ordered fallbacks, or (A \| B) \|\| C last-resort. |
llm_provider.model_aliases.custom |
dict | - | User-defined aliases usable from %model:@<alias> / %m:@<alias>. Each requires model (single target or selector) and description. |
llm_provider.model_aliases.buckets |
dict | - | Optional display-only sase's TUI Launch Control bucket descriptions. |
Model aliases are resolved when an agent launches, so reusable macros can point at names
such as %model:@medium or %model:@blogger while each user's sase.yml controls the
concrete provider/model. Alias config keys stay bare; the @ marker is only used in
%model/%m directive values. Alias values may reference another alias with
@<alias>; the reference may carry a trailing effort such as @medium@high, which
overrides the referenced alias's effort (chains are followed with cycle/depth
protection). Unknown non-alias model values keep the existing fallback behavior and run
on the default provider. Use model_aliases.builtin to override one of the five
built-in size aliases and model_aliases.custom for user-defined aliases with
descriptions. A | B round-robins across real launches, skips providers whose CLI is
unavailable, and stores its machine-global cursor in ~/.sase/llm_lb.json; display and
preview surfaces only peek. A || B always selects the first installed provider CLI
that is not hard-disabled (a soft-disabled first candidate still wins) and never
reads or advances that cursor. (A | B) || C load-balances the parenthesized pool and
uses the || tail only when every pool member is unavailable (CLI missing or
hard-disabled); an all-soft pool still rotates and does not divert, and tail
selection does not consume the pool cursor. Unparenthesized mixing is still rejected.
Ordered fallback is based on CLI installation plus temporary provider-disable state, not
later model/runtime success, and preserves its first candidate for normal diagnostics
when none are available. Members may carry a trailing effort. Selectors cannot be
nested, and selectors are not accepted in %model directives or launch-scoped/temporary
overrides. In sase's TUI Launch Control, the pool row reports the available/total count,
selector member lists mark the current selection with →, and active temporary
overrides label selection suspended unless their provider is hard-disabled; then the
override is paused and the underlying alias resolves. A soft disable does not pause
the override.
On top of any configured aliases, SASE ships a fixed set of built-in size aliases
that resolve even when unset: @xsmall, @small, @medium, @large, and @xlarge.
Each is a direct selector with no fallback chain to another alias — override one by
setting model_aliases.builtin.<size> to a concrete model, an A | B round-robin pool,
an A || B ordered fallback, or a parenthesized (A | B) || C last-resort. Phase and
task launches route directly to the size alias matching their size metadata; a legacy
phase or task with no size metadata routes through @small. New tasks require an
explicit size after /sase_new_task has ruled out a semantic duplicate and a causally
related in-progress epic. See Built-in size aliases for
the full shipped-defaults table and
Role Aliases for Delegated Work for how
delegated launches pick a model.
Three scalar llm_provider fields choose the model for launches that aren't driven by
phase/task/tale size routing: default_model (used when a launch has no explicit
%model directive), epic_lander_model (used by epic land agents below
bead.big_epic_phase_threshold authored phases), and big_epic_lander_model (used by
epic land agents at or above that threshold). Each accepts the same grammar as an alias
target — a concrete model, a provider-qualified model, an @alias reference (optionally
with a trailing effort such as @large@high), an A | B pool, an A || B fallback, or
a parenthesized (A | B) || C last-resort. Precedence is unchanged from before the
migration: an explicit prompt/plan/phase/task/approval-picker %model wins first, then
an active temporary override of the selected setting, then the config field resolves
through the normal alias/effort/selector/provider-disable machinery, and a missing or
malformed field falls back to its shipped default (@large / @large / @xlarge).
model_alias_history_limit bounds the number of prior runs requested for each alias in
Launch Control's agent-history panel. It defaults to 10, must be at least 1, and
falls back to 10 at runtime when a malformed value bypasses schema validation. From
that initial window, the panel's Ctrl+J / Ctrl+K keys add or subtract a fixed 10-run
step and never drop below this configured limit; they do not use ace.page_size.
Accepted tale follow-ups without an approval-time model validate the actual handoff plan
and choose the matching size alias directly. Legacy tale plans without size metadata use
@medium. An approval-time model, a %model directive in a custom coder prompt, or an
outer effort suffix remains authoritative.
model_aliases.builtin.epic_creator is retired. SASE no longer launches an epic-creator
agent, resolves that alias implicitly, or treats it as a builtin override, so a stale
entry should be deleted rather than repointed. sase doctor reports a leftover entry
under the model_aliases.builtin.epic_creator key.
The
llm_provider.worker_modelsmap and the reserved@worker/@otheraliases were removed in epic sase-5d. Use a size alias (@xsmall,@small,@medium,@large,@xlarge) or an explicit model instead of@worker, andllm_provider.default_modelinstead of@other. Thephase_workerbucket and its<size>_phase_workeraliases from that epic were themselves retired by the later size-alias simplification below.sase doctorreports configs that still reference removed keys or aliases, including retired@coderand registered@<provider>_coderbuiltin entries.The implicit role aliases (
@default,@epic_lander,@big_epic_lander), the five<size>_workeraliases, the automaticworkerbucket, and the capability/cost aliases (@smart,@smarter,@smartest,@cheap,@cheaper,@cheapest) were removed in favor of the five direct built-in size aliases above plus the three scalardefault_model/epic_lander_model/big_epic_lander_modelfields. A custom alias can still opt into a bucket of any name, includingworker— it just has no special behavior anymore. Runsase doctor -C config.model_aliasesfor the exact destination of any retired name still present in your config or directives.
The TUI also supports temporary, per-alias session-level provider/model overrides
(set from Launch Control, ,m) that do not edit this
config. They are persisted to ~/.sase/llm_override.json and expired entries are
deleted on next read. See docs/llms.md for the
resolution order, state-file format, and precedence relative to
SASE_MODEL_TIER_OVERRIDE.
The same panel's p=Providers flow manages temporary provider disables in
~/.sase/llm_provider_disables.json. This is runtime state, not configuration: it does
not add a disabled_providers key, does not edit llm_provider.provider, and does not
rewrite any alias. A hard disable is fail-closed: alias selectors skip that member,
temporary alias overrides targeting it pause until the disable clears or expires, and
direct explicit provider/model requests fail with an actionable diagnostic instead of
silently switching providers. A soft disable is spared in | pools while another
member can cover, never diverts a || fallback, and does not pause overrides or fail
explicit requests. See
Temporary Provider Disables.
llm_provider.usage_limit¶
Automatic usage-limit detection temporarily disables a provider when that provider's own
error output positively matches its configured usage-limit patterns. This is separate
from llm_provider.retry: provider-scoped usage-limit classification wins before retry
policy, while a plain transient 429 or other retryable error that does not match a
usage-limit pattern still follows retry/fallback policy.
llm_provider:
usage_limit:
enabled: true
disable_seconds: 86400
min_disable_seconds: 60
max_disable_seconds: 604800
honor_reset_hint: true
honor_usage_windows: true
notify: true
relaunch: true
relaunch_limit: 20
providers:
claude:
patterns:
- "you've hit your usage limit"
exclude_patterns:
- "usage limit approaching"
replace_patterns: false
disable_seconds: null
honor_reset_hint: null
honor_usage_windows: null
| Field | Type | Default | Description |
|---|---|---|---|
llm_provider.usage_limit.enabled |
bool | true |
Enable usage-limit classification and automatic provider disables. |
llm_provider.usage_limit.disable_seconds |
int | 86400 |
Fallback disable duration in seconds used only when neither a provider reset hint nor a corroborated usage-window reset is available. |
llm_provider.usage_limit.min_disable_seconds |
int | 60 |
Lower bound applied to provider-reported reset-hint and usage-window-derived durations. |
llm_provider.usage_limit.max_disable_seconds |
int | 604800 |
Upper bound applied to provider-reported reset-hint and usage-window-derived durations. |
llm_provider.usage_limit.honor_reset_hint |
bool | true |
Parse a provider-reported reset time when present: a bare or zoned clock time ("resets at 8pm", "resets 6:38pm (America/New_York)"), an absolute date ("try again at Aug 20th, 2026 6:38 AM", "resets Aug 22, 8pm (America/New_York)", "resets 2026-08-20 06:38 UTC"), or a relative duration ("try again in 2 hours"). |
llm_provider.usage_limit.honor_usage_windows |
bool | true |
When no reset hint is found, fall back to a corroborated reset from the collected usage-window data (see Usage-Limit Auto-Disable) before using the flat disable_seconds. |
llm_provider.usage_limit.notify |
bool | true |
Send one notification for each newly-created automatic disable window. |
llm_provider.usage_limit.relaunch |
bool | true |
Behind the provider_drain beta flag: submit a durable sase agent drain proc when a usage-limit disable hard-disables a provider, relaunching the agents it stranded and folding the report into the notify notification. Ignored while the flag is off. |
llm_provider.usage_limit.relaunch_limit |
int | 20 |
Most agents an automatic drain relaunches (passed as the drain's --limit). |
llm_provider.usage_limit.providers.<provider> |
dict | - | Provider-specific detection and duration overrides. |
llm_provider.usage_limit.providers.<provider>.patterns |
list[str] | [] |
Positive case-insensitive substring patterns. User patterns are additive with provider defaults unless replacement is set. |
llm_provider.usage_limit.providers.<provider>.exclude_patterns |
list[str] | [] |
Case-insensitive exclusions that suppress otherwise positive matches; exclusions are always additive. |
llm_provider.usage_limit.providers.<provider>.replace_patterns |
bool | false |
When true, the configured patterns list literally replaces built-ins; patterns: [] intentionally disables that detector. |
llm_provider.usage_limit.providers.<provider>.disable_seconds |
int/null | null |
Per-provider fallback duration used only when neither a reset hint nor a corroborated usage-window reset is available; null inherits llm_provider.usage_limit.disable_seconds. grok ships a non-null built-in default of 172800 (48h): Grok Build reports no reset instant in any usage-limit message, and paid usage meters against one shared weekly pool, so this now-usually-preempted flat default still matters when the collector has no fresh corroborating window. |
llm_provider.usage_limit.providers.<provider>.honor_reset_hint |
bool/null | null |
Per-provider reset-hint policy; null inherits the global value. |
llm_provider.usage_limit.providers.<provider>.honor_usage_windows |
bool/null | null |
Per-provider usage-window fallback policy; null inherits the global value. |
Automatic disables are written to the same machine-wide provider-disable state used by
Launch Control, with source: "usage_limit". They expire and self-clean like manual
disables, can be cleared early from Launch Control, and do not unregister or rewrite a
provider. If a fallback is available, retry/fallback may proceed only to a different
enabled provider; the disabled provider remains skipped until expiry or clearing. See
Usage-Limit Auto-Disable for reset-hint parsing,
notification, and replacement details.
llm_provider.usage_metrics¶
Subscription-capacity collection settings. This is distinct from
llm_provider.usage_limit, which classifies provider error text and writes temporary
routing disables. usage_metrics controls whether SASE probes installed provider CLIs
for included-allowance windows.
enabled is the durable user preference; setting it false stops probes, isolated
workers, passive observation writes, scheduled requests, and attention.
Claude, Codex, Grok, Muse Code, and Antigravity ship collectors. Muse's is a free local
probe against muse serve — no model call and no tokens — so it costs only about three
seconds of wall clock per refresh. To turn it off, set
llm_provider.usage_metrics.providers.muse.enabled to false; see
Muse Code subscription usage.
Antigravity's is likewise a free local /usage probe (agy >= 1.1.11, about three to
five seconds per refresh); see
Antigravity subscription usage.
Every recorded attempt carries its outcome, failure reason, and any Retry-After hint,
and refresh backoff follows the failure class. sase usage list -v shows the
per-provider retry state; JSON output passes the collector_health snapshot through
unchanged.
| Class | Failure reasons | Retry policy |
|---|---|---|
| Success | ok, not_applicable, unsupported outcomes |
next probe at the provider cadence |
| Transient | timeout, deadline_exceeded, probe_failed, parse_error, malformed_payload, account_context_changed |
cadence-based backoff up to 30 minutes |
| Rate-limited | rate_limited |
honored Retry-After (clamped to 15 minutes–6 hours) when present, otherwise an escalating delay from 15 minutes up to 2 hours |
| Auth | unauthenticated, logged_out, api_mode outcomes |
generic backoff up to 30 minutes |
| Parked | not_installed, unsupported_cli_version |
6-hour backoff, released early when the CLI changes |
| Vendor drift | vendor_drift |
fixed 1-hour backoff |
Providers in active use refresh on the hot cadence instead of the idle one. A provider
counts as hot while a recent agent launch or limit-event hint (each good for 15 minutes)
is still live, or while one of its stored un-reset windows sits at or above
warn_percent used. Hotness is suppressed while live stream events already keep every
stored window fresh — a window received within the active cadence of now needs no probe.
The hot interval is max(active_refresh_seconds, floor), so a hot Claude still waits
for its 300 s floor while a hot Codex is due at about 120 s.
llm_provider:
usage_metrics:
enabled: true
refresh_seconds: 300
active_refresh_seconds: 120
warn_percent: 75
critical_percent: 90
indicator:
enabled: true
default:
below_remaining_percent: 20
weekly_all: always
providers:
muse:
windows:
session: never
providers:
claude:
enabled: true
| Field | Type | Default | Description |
|---|---|---|---|
llm_provider.usage_metrics.enabled |
bool | true |
Collect subscription usage. False stops probes, passive writes, scheduled requests, and attention; inspection can still explain the opt-out. |
llm_provider.usage_metrics.refresh_seconds |
number | 300 |
Idle refresh cadence in seconds. Must be finite and at least 60. The scheduler's usage routine ticks every 60 s and probes each provider at max(refresh_seconds, floor); display freshness uses the same max. The TUI header indicator uses the same max(refresh_seconds, floor) freshness as sase usage list and the Models panel, so a provider polled at its floor never shows as stale or unknown between probes. Explicit refreshes bypass the floor but still respect the 60 s cooldown and any Retry-After. |
llm_provider.usage_metrics.active_refresh_seconds |
number | 120 |
Hot refresh cadence in seconds for providers in active use. Must be finite and at least 60; values above refresh_seconds are capped at it. A hot provider probes at max(active_refresh_seconds, floor), so plugin floors still bound hot polling. |
llm_provider.usage_metrics.warn_percent |
number | 75 |
Percentage used that classifies a window as low. Must satisfy 0 <= warn_percent < critical_percent <= 100. UI copy uses percentage left. |
llm_provider.usage_metrics.critical_percent |
number | 90 |
Percentage used that classifies a window as very low. |
llm_provider.usage_metrics.indicator.enabled |
bool | true |
Show sase's TUI header usage indicators. False hides display entries while collection and Providers · Usage remain active. |
llm_provider.usage_metrics.indicator.default |
policy | {below_remaining_percent: 20} |
Fallback display policy for windows that do not match a more specific display override. |
llm_provider.usage_metrics.indicator.weekly_all |
policy | always |
Display policy for positively classified weekly all-model windows after exact-window and provider defaults. |
llm_provider.usage_metrics.indicator.providers.<name>.default |
policy | inherit | Optional display policy for all observed windows from one provider. This map is separate from the sibling collection providers map. |
llm_provider.usage_metrics.indicator.providers.<name>.windows.<key> |
policy | inherit; Muse session never |
Exact provider-reported window-key override. Find keys with sase usage list -p <provider> --json at windows[].key. |
llm_provider.usage_metrics.providers.<name>.enabled |
bool | inherit | Optional per-provider collection override. Keys are registered provider names; the generic schema does not hard-code any provider list. |
An indicator policy is exactly one of:
always: display each matching observed window at any remaining percentage.never: suppress matching display entries.{below_remaining_percent: N}: display only when the unrounded remaining percentage is strictly less than finiteN, where0 <= N <= 100.
Thresholds use percentages remaining, not percentages used. 0 selects no numeric
windows, 100 still excludes an exactly full window, and always is the only policy
that includes full capacity.
Display policy precedence is exact window key, provider default, weekly_all for a
positively classified weekly all-model window, then global indicator.default. Provider
and window IDs are open-ended; unmatched future provider/window keys are inert.
The bundled default adds one exact Muse override:
llm_provider:
usage_metrics:
indicator:
providers:
muse:
windows:
session: never
Claude's observed weekly Fable window has no bundled entry, so the global
indicator.default threshold governs it and the header shows it only when its remaining
capacity is strictly below 20%. Set the exact key "weekly:claude-fable-5" to always
to restore the always-visible behavior, to never to hide it even when low, or to a
{below_remaining_percent: N} of its own. Missing or collection-ineligible Fable
windows are not synthesized. Because config layers merge recursively, an empty user
indicator.providers: {} does not erase this bundled key, while an explicit value for
the key does override it. Broader provider/global defaults have lower selection
precedence than the exact window key.
Muse reports two windows: a 5-hour session window and a weekly window. The header
shows only the weekly one by default. Muse's weekly window is classified as a weekly
all-model window, so the existing weekly_all: always policy selects it with no entry
of its own, and it appears at any remaining percentage once Muse is an eligible provider
(the muse CLI is installed and Muse is referenced by a model alias or enabled in
llm_provider.usage_metrics.providers). The session: never override deliberately
suppresses the 5-hour window even when it runs low; it stays visible in Providers ·
Usage and sase usage list -p muse. Restore header attention for it with:
llm_provider:
usage_metrics:
indicator:
providers:
muse:
windows:
session:
below_remaining_percent: 20
Antigravity reports four windows — gemini-weekly, gemini-5h, 3p-weekly, and
3p-5h — and has no bundled indicator entry. Its Gemini weekly window is treated as the
provider's weekly anchor (it matches the weekly_all policy while keeping its
family:gemini scope), so weekly_all: always anchors it in the header unlabeled
whenever agy is an eligible provider; a providers.agy.default policy would take
precedence. The Gemini 5-hour window and both 3p-* (Claude/GPT) windows fall through
to the global indicator.default threshold and appear only below 20% remaining. Pin or
hide them with exact keys:
llm_provider:
usage_metrics:
indicator:
providers:
agy:
windows:
gemini-5h: always
3p-weekly: never
Invalid indicator config is diagnosed with its config path, ignored at the smallest invalid override, and inherited/default policy is used instead of resetting unrelated valid usage settings. sase's TUI display settings are cached by the merged-config token, so config changes reload on the normal usage refresh cadence even when the usage-state file does not change.
Routing-disabled providers still refresh when collection is otherwise eligible, because reset information remains useful for them.
The same panel's fixed Ctrl+E binding manages the separate machine-wide default-effort
override at ~/.sase/llm_effort_override.json. It uses the alias override duration and
exact-time cards, but its state and precedence are independent: explicit prompt effort
and alias/member effort win, then the temporary effort override, then
llm_provider.default_effort, then the provider default. See
Reasoning Effort.
Its fixed Ctrl+R binding manages max_running_agents: persistent edits target the
user-base sase.yml (or its chezmoi source), while temporary values live independently
in ~/.sase/max_running_agents_override.json. This is a Launch Control binding, not an
ace.keymaps option.
llm_provider.continuation_budget¶
Byte budget for the continuation-budget preflight. Monitor successors always run it, and
so does any invocation with SASE_CONTINUATION_BUDGET_ENFORCE=1. The preflight measures
the final expanded prompt; when its essential content cannot fit the effective budget,
SASE records the decision and refuses before calling the provider.
llm_provider:
continuation_budget:
context_limit_bytes: 800000
estimate_uncertain: true
instruction_reserve_bytes: 0
tool_reserve_bytes: 0
output_reserve_bytes: 0
reasoning_reserve_bytes: 0
providers:
agy:
transport_limit_bytes: 122880
instruction_reserve_bytes: 512
# models:
# <model-name>: { context_limit_bytes: 400000 }
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
context_limit_bytes |
int | 800000 |
1 |
Provider context budget, in UTF-8 bytes, before reserves are subtracted. |
transport_limit_bytes |
int | unset | 1 |
Largest prompt the provider transport can carry safely, such as a single-argv limit. |
checkpoint_threshold_bytes |
int | unset | 1 |
Prompt size at which checkpoint-backed reductions are considered. |
instruction_reserve_bytes |
int | 0 |
0 |
Bytes reserved for provider-side system instructions or command framing. |
tool_reserve_bytes |
int | 0 |
0 |
Bytes reserved for provider tool definitions or tool-call framing. |
output_reserve_bytes |
int | 0 |
0 |
Bytes reserved for the model's response. |
reasoning_reserve_bytes |
int | 0 |
0 |
Bytes reserved for hidden reasoning or other provider overhead. |
estimate_uncertain |
bool | true |
- | Mark byte estimates as uncertain when exact provider accounting is unavailable. |
providers.<name> |
dict | - | - | Overrides of any field above for one registered provider. |
providers.<name>.models |
dict | - | - | Overrides keyed by provider-resolved model name, applied on top of that provider's entry. |
Each field resolves independently: a matching model entry wins over its provider entry,
which wins over the shared values, and the matching SASE_CONTINUATION_* variable (see
LLM Provider environment variables) outranks all of them. A zero,
negative, or non-integer value uses the built-in default instead. The bundled agy
entry reflects that Antigravity sends the print prompt as one argv element; SASE keeps
those agy transport and instruction-reserve values even without configuration and
never lets the agy instruction reserve drop below 512 bytes.
Source: src/sase/llm_provider/continuation_budget_request.py
llm_provider.retry¶
Per-provider retry and fallback configuration. See docs/llms.md for the full retry flow and TUI display.
llm_provider:
retry:
claude:
max_retries: 3
error_patterns:
- "API Error: 500"
wait_times: [60, 300, 1800]
fallback_model: "sonnet"
continuation_prompt: "Please continue from the last preserved work."
preserve_workspace: true
spawn_new_agent: false
| Field | Type | Default | Description |
|---|---|---|---|
llm_provider.retry.<provider> |
dict | - | Retry config for a specific provider (e.g., agy, claude, codex). |
llm_provider.retry.<provider>.max_retries |
int | 0 |
Maximum retry attempts. 0 disables retrying. |
llm_provider.retry.<provider>.error_patterns |
list | [] |
Case-insensitive substring patterns matched against error output. |
llm_provider.retry.<provider>.wait_times |
list | [30] |
Per-retry wait times in seconds. Last value reused if list is shorter. |
llm_provider.retry.<provider>.fallback_model |
str | null |
Alternate model to use after exhausting all retries. |
llm_provider.retry.<provider>.continuation_prompt |
str | null |
Prompt text prepended when continuing after a retryable failure. |
llm_provider.retry.<provider>.preserve_workspace |
bool | false |
Preserve on-disk edits across legacy in-process retry attempts. |
llm_provider.retry.<provider>.spawn_new_agent |
bool | false |
Retry by launching a fresh detached agent that inherits the workspace. |
Configured retry policy is merged with provider-supplied retry defaults when a provider
declares them. For list fields such as error_patterns, built-in patterns are kept and
configured patterns are appended with duplicates removed. Claude's provider hook adds
workspace-preserving matching for context-limit, socket-close, and Claude CLI API-error
output, plus a continuation nudge. Those hook defaults are merged with the bundled
Claude policy in default_config.yml, so the configured wait times and fallback model
still apply unless you override them.
Source: src/sase/llm_provider/retry_config.py, src/sase/llm_provider/config.py
finalizers¶
Configures host-owned completion finalizers for SASE-launched agent runs. The bundled default selects the built-in commit instance, preserving ordinary commit enforcement without runtime-specific hooks.
finalizers:
defaults: [commit]
required: []
instances:
commit:
use: builtin@commit
after: []
max_attempts: 2
refusal: defer
defaults is the ordered selection used when a prompt omits %final. required
instances cannot be removed by %final:none or %final:!name. instances is keyed by
lowercase slug; each instance names a trusted provider with use, optional dependency
edges in after, bounded attempts through max_attempts, refusal handling, and
provider-specific config.
sase final submit is authoring-only: every dirty repository obligation takes a
Conventional Commit message, because SASE agents work in ephemeral numbered workspace
clones where uncommitted work is lost work. There is no free-text refusal action. The
only escape hatch is sase final defer, a separate, deliberate command that names a
typed reason from a closed set (protected_paths, foreign_work, unsafe_content,
belongs_to_another_turn) and is adjudicated by the host against run evidence at submit
time — a deferral the host can disprove is rejected with counter-evidence so the agent
can repair and resubmit in the same turn. refusal controls what happens when the host
upholds a deferral: fail (still fails the run; the historical behavior) or defer
(the shipped default — the run completes with a distinct deferred status and the tree
stays dirty, and a notification names the repository, reason, paths, and the command to
finish the commit by hand).
Only trusted configuration can define providers, commands, cwd policy, environment
allowlists, timeouts, or retry policy. Prompt text can only select configured instances:
%final:lint adds lint, %final:none clears defaults unless blocked by required,
and repeated/comma-separated %final operations replay left to right before dependency
ordering. Plugin packages may advertise providers through the sase_finalizers
entry-point group, but installation alone is inert; a trusted config layer must declare
and select an instance. Plugin-contributed config layers cannot activate finalizers.
Use sase final list, sase final show <instance>, and sase final doctor to inspect
effective policy, provider provenance, and diagnostics. During an active agent turn,
generated agent instructions tell the model to finish with /sase_final. That skill
uses sase final context -f json and sase final submit to publish the one turn-bound
declaration required by selected finalizers, and it returns after reading context when
no payload is required. If a required submission is missing or stale, the host opens one
bounded recovery turn that explicitly requests /sase_final. The host executes and
verifies finalizers after the model returns.
sase final prepare <wrapper.json> is the conditional alternative for a long
verification handoff. It seals a completed declaration, exact verification argv, current
context, and repository observations without submitting or executing them. Bind the
returned single-use ref with sase monitor start -f <ref>; only an eligible successful
monitor result lets the host install that declaration and run finalizers without another
model turn. A plain monitor profile carries no completion authority. See
Prepared host completion.
The built-in builtin@commit provider checks the main workspace, configured linked Git
worktrees, and repositories opened through /sase_repo. Dirty enforced repositories
become declaration obligations; each repository must receive exactly one commit
decision with a Conventional Commit message, and commit is the only legal repository
action. Typed deferrals can name explicit paths that must not be committed, using the
adjudicated reasons protected_paths, foreign_work, unsafe_content, or
belongs_to_another_turn. When the run has an assigned bead (SASE_BEAD_ID), the
primary repository's commit decision also carries a keep or close bead action, which
is forwarded to sase stitch create -B; see
Explicit Bead Action. Commit decisions
dispatch through sase stitch create sequentially, preserve protected pre-existing
dirt, write stitch evidence, stop on the first conflict for repair/resume, and fail
completion when a rejected deferral or unrepaired dirty state remains. Later mutating
finalizers can reactivate commit until the controller reaches its bounded fixed point.
When $SASE_ARTIFACTS_DIR is set, new runs write generic finalizer artifacts:
finalizer_baseline.json, final_context.json, final_submission.json,
final_submission_attempts.jsonl, finalizer_result.json, and per-instance files under
finalizers/<instance>/. The commit provider also keeps current stitch evidence in
commit_results.json. Historical commit_finalizer_* artifacts are read only for
archived run reporting and are not a supported output format for new runs.
commit.message¶
Configures the Conventional Commit subject gate that sase stitch create applies to
every create_commit, create_proposal, and create_pull_request message before any
side effect runs.
commit:
message:
require_conventional_subject: true
allowed_types:
[build, chore, ci, deps, docs, feat, fix, perf, refactor, revert, style, test]
| Field | Type | Default | Description |
|---|---|---|---|
commit.message.require_conventional_subject |
bool | true |
Reject a sase stitch create message whose subject line is not a Conventional Commit. |
commit.message.allowed_types |
list | the 12 types above | Commit types this project accepts. A configured list replaces the built-in set. |
The subject must match <type>[(<scope>)][!]: <description>. The scope is optional, one
or more spaces may follow the colon, and no length or capitalization rule is applied to
the description. The type itself must be lowercase, because release tooling does not
classify capitalized types reliably. Only the first line is inspected; nothing below the
subject is validated.
Subjects beginning with Merge, Revert ", fixup!, squash!, or amend! are
exempt and always pass — these are mechanical git-generated or rebase-directive
subjects. An empty message is always rejected.
A rejection fails the workflow before beads are closed, plans are staged, or the
before-commit hook runs, and the -M message file is preserved so the same command can
be re-run after the subject is rewritten. There is no per-invocation bypass flag or
environment variable; a project that does not use Conventional Commits sets
require_conventional_subject: false.
Source: src/sase/finalizers/controller.py, src/sase/finalizers/commit.py,
src/sase/commit_instructions.py, src/sase/workflows/commit/message_validation.py,
src/sase/core/commit_subject_facade.py
monitor¶
Controls which monitors run their command inside sase tool run and the amount of
command evidence projected into a monitor continuation prompt. The evidence limits do
not change the monitor's bounded rotating log or the output available to
sase monitor show; they bound only automatically selected prompt content.
monitor:
tool_wrap: verify
evidence_limits:
selected_diagnostics_bytes: 8192
fallback_tail_bytes: 4096
total_raw_excerpt_bytes: 12288
raw_tail_lines: 200
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
monitor.tool_wrap |
off, verify, or all |
verify |
- | Which monitors run inside sase tool run: verify wraps only -p verify monitors, all wraps every monitor, off wraps none. |
monitor.evidence_limits.selected_diagnostics_bytes |
int | 8192 |
1 |
UTF-8 bytes of selected failed-stage diagnostics eligible for embedding. |
monitor.evidence_limits.fallback_tail_bytes |
int | 4096 |
1 |
UTF-8 bytes of fallback raw tail when diagnostics are unavailable. |
monitor.evidence_limits.total_raw_excerpt_bytes |
int | 12288 |
1 |
Aggregate raw-evidence byte cap for one continuation prompt. |
monitor.evidence_limits.raw_tail_lines |
int | 200 |
1 |
Retained-output lines considered when selecting raw-tail evidence. |
selected_diagnostics_bytes and fallback_tail_bytes must each be no larger than
total_raw_excerpt_bytes. The runtime falls back to all four packaged defaults if that
relationship is invalid; schema validation rejects non-positive values. Strict
--next-output file and none policies still embed no raw command output.
An unrecognized tool_wrap value falls back to verify at runtime. Wrapping changes
only the argv the proc supervisor execs, never the recorded monitor command; see
Tool-run wrapping for the per-command rules.
Source: src/sase/default_config.yml, src/sase/config/_settings_display.py,
src/sase/config/sase.schema.json, src/sase/monitor/result_projection.py,
src/sase/monitor/tool_wrap.py
repos¶
Declares linked and sidecar repositories related to a project. Git linked-repo worktrees
are eligible for commit-finalizer checks at their resolved workspace_dir. Agents use
/sase_repo to prepare them; its audited sase repo open command records manually
opened linked workspaces in run artifacts for sase's TUI context and appends a durable
audit event. SASE materializes a hidden sibling-state ProjectSpec for the linked repo
when needed. Entries can live in user config or a project-local sase/sase.yml; local
entries are resolved relative to the project's primary workspace directory.
Linked repositories are lazy by default. Set auto_clone: true for a repository that
every launched agent needs; SASE materializes and prepares those entries before
execution. Lazy entries remain available through sase repo open, but their
per-repository *_DIR environment variables are not exported until the clone exists.
Repositories with auto_clone: true are omitted from generated agent instructions
because agents do not need to open them manually.
auto_clone and auto_sync are independent settings on a sidecar entry. auto_clone
controls whether a numbered workspace materializes its own clone of the sidecar before
each agent launch; that clone is disposable and owned by the launched agent's workspace.
auto_sync instead opts the primary checkout's already-materialized sidecar clone
into conservative background convergence: SASE fetches and fast-forwards it only while
it is clean, attached, and non-diverged, and never rebases, commits, hard-resets, or
removes user state. A dirty, detached, diverged, remote-mismatched, or
not-yet-materialized primary sidecar clone is left untouched and reported rather than
repaired. Managed projects enable auto_sync for the generated plans, beads, and
research entries; custom roles default to false and opt in with the same property.
See Ownership Boundary for the underlying
primary/leased distinction and how sync is scheduled.
repos.sidecar is a two-bucket mapping keyed by role: builtin holds overrides of the
reserved plans, beads, agents, attachments, and attachments-private roles, and
custom holds user-declared document sidecars such as research. The map key is the
role, so an entry never carries a name field and a role cannot be declared twice in
one bucket. Because both buckets are mappings, a later config layer merges into an
inherited entry per key — a project-local custom: {research: {disabled: true}} opts
out of a global research sidecar. The former list form (a sequence of entries each
carrying name) is no longer accepted and is ignored; run sase doctor to see which
bucket each stale entry belongs in.
Sidecar entries use their role key as the primary CLI lookup key. Ordinary roles use
sase/repos/<role> as their workspace clone directory. Their repository defaults to
<project>--<role> in the primary repository's GitHub organization; repo can pin a
bare slug or owner/repo. An explicit unpinned entry uses that project-local derivation
even when a legacy SDD store record names a different repository. Configured sidecars
appear in sase repo list even before cloning and can be opened by role name or
repository slug. Enabled ordinary sidecars that are not auto-cloned also appear by
repository slug in generated agent instruction files, where their description tells
agents when to open them with /sase_repo. Set disabled: true in a later config layer
to suppress a matching global entry or implicit fallback; disabled and auto-cloned
sidecars are omitted from generated instructions.
The roles plans, beads, agents, attachments, and attachments-private are
reserved and are configured under repos.sidecar.builtin. plans owns canonical plans,
beads owns the event store, agents is the hidden machine-level publication store
plus the canonical prompt and prompt-artifact archive, attachments is the hidden bare
store for public bead attachment bytes, and attachments-private is the hidden bare
store for private bead attachment bytes. Every other enabled role lives under
repos.sidecar.custom and is a document sidecar: a <YYYYMM>/*.md corpus whose kind
label is the role name. attachments and attachments-private are not document roles,
so neither can be declared under custom. Document roles receive clone/store
resolution, sase repo path <role>, doctor validation, commit routing,
SASE_SDD_<ROLE>_DIR, plan-search visibility, and an sase's TUI Plans kind. research
is simply the default-seeded document role; only its illustrated README/directory-map
preset is name-specific.
The agents role is intrinsically hidden from agent workflows. It never appears in
generated memory, launch metadata, linked-repository environment variables, or a
workspace's sase/repos/ tree, even if an override sets auto_clone: true. It remains
visible to users as a sidecar row in sase repo list, and sase repo path agents or
sase repo open agents -r "<reason>" explicitly accesses the one machine-level clone at
~/.sase/projects/<project_key>/repos/agents. The derived or pinned repository slug is
also accepted by those commands.
attachments-private is also hidden from agent instructions. Managed projects receive
an implicit private <project>--attachments-private entry unless configuration names
that role or default_linked_repos is false. Its clone is a bare partial repository
(git clone --bare --filter=blob:none) at
~/.sase/projects/<project_key>/repos/attachments-private, with no worktree and no
README. sase repo list shows the role, and sase repo path attachments-private prints
that path. auto_clone: true does not put a copy in a workspace's sase/repos/ tree.
Resolved visibility is always private: a visibility: public override is rewritten to
private, and preflight rejects a provider that would create the remote with another
visibility. SASE does not verify the visibility of a remote that already exists. Opt out
of sidecar setup with repos.sidecar.builtin.attachments-private.disabled: true.
Attachment commands still use an existing local clone even when that role is disabled;
-L/--local-only keeps an individual attachment on this machine. When available, the
git store takes objects up to bead.attachments.git_max_bytes; see
Attachments.
The beads role does not default to auto_clone: true: the beads sidecar materializes
on demand, in sase bead and in the agent-launch bead claim, instead of during
workspace preparation. It is unconditionally omitted from generated agent instructions
regardless of its auto_clone setting, the same way agents is, because agents reach
bead state through sase bead rather than /sase_repo. Unlike agents, it otherwise
behaves like an ordinary reserved sidecar: it appears in sase repo list and resolves
through sase repo path beads or sase repo open beads -r "<reason>".
The workspace provider owns sidecar transport. GitHub sidecars use canonical SSH origins
on the primary repository's GitHub host (git@host:owner/repo.git, or
ssh://git@host:port/owner/repo.git when a port is configured). Read-only store
resolution converts a legacy GitHub HTTPS record to that exact SSH form in memory, so
inventory, launch-time auto-cloning, and on-demand materialization are safe immediately
without rewriting the durable record. Matching retained HTTPS clones keep their checkout
and local state while SASE rewrites origin in place. Any HTTP(S) sidecar remote that
cannot be derived from consistent GitHub provider, host, and repository metadata fails
materialization before Git runs. Rerun sase repo init to persist the migrated record;
it is not required to make a launch safe.
Managed projects (is_sase_managed: true) receive deterministic <project>--plans
(auto_clone: true), <project>--beads (auto_clone: false), <project>--agents
(auto_clone: false, public visibility), and private <project>--attachments-private
entries when no matching explicit sidecar is configured. Research is config-declared per
project and defaults to <owner>/<project>--research; sase repo init writes the plans
and research entries. A project-local agents or attachments-private entry replaces
that implicit entry. For agents, use disabled: true to opt out or
visibility: private to retain a private remote. For attachments-private, use
disabled: true to opt out; visibility stays private. Project-local
default_linked_repos: false suppresses those implicit managed-project entries.
sase repo init can create and seed the agents remote only after its separate
default-no consent prompt. Successful agent commit/PR workflows publish the committing
hood, while sase agent sync publishes every locally commit-eligible hood through the
stable machine-level clone. See Agent Hood Synchronization before
enabling a public remote.
The deprecated linked_repos and sibling_repos keys are still accepted as aliases
during the compatibility window. Canonical repos.linked entries take precedence over
both aliases when the same name is defined.
A linked entry may declare revision_pin, a pin file relative to the primary checkout
root that holds that repo's pushed SHA (for example sase-core-revision.txt). The path
must be relative and stay inside the checkout. Absolute paths, ~, drive-letter paths,
and .. that would leave the checkout are rejected when the config is read. A relative
path that still leaves through a symlinked parent directory or a symlinked pin file is
not followed: sase doctor reports that escape, and the commit finalizer skips the pin
write with pin-escapes-checkout. sase doctor also flags a pin file that is missing
or does not hold a 40-character hex SHA. It resolves the pin from the current working
directory, so run it from the primary checkout. When one accepted declaration commits
both the primary and a pinned sibling, the host commits the sibling first and writes its
pushed SHA into the pin file before the primary commit. A skipped pin records the reason
and does not fail the run.
github_orgs:
- sase-org
repos:
linked:
- name: core
path: ../sase-core
description: Shared backend/domain behavior used by SASE frontends.
auto_clone: true
sidecar:
builtin:
plans:
auto_clone: true
ref:
use: builtin@plan
agents:
visibility: private
custom:
research:
description: Durable SASE research reports and generated media.
visibility: public
ref:
icon: ∴
inventory:
globs: ["reports/**/*.md", "!drafts/**"]
| Field | Type | Default | Description |
|---|---|---|---|
github_orgs |
string or list | - | GitHub user/org namespaces available to provider completion and PR workflows. |
default_linked_repos |
boolean | true |
Inject managed-project --plans, --beads, and hidden --agents, --attachments, and --attachments-private sidecars. |
repos.linked[].auto_clone |
boolean | false |
Materialize and prepare the repository automatically before each agent launch. |
repos.linked[].name |
string | required | Stable alias used in generated environment variable names and memory summaries. |
repos.linked[].path |
string | required | Primary checkout path. Relative paths resolve from the project's primary workspace. |
repos.linked[].description |
string | required | Human-readable purpose used when generating agent memory for the linked repository. |
repos.linked[].revision_pin |
string | - | Pin file relative to the primary checkout root holding this repo's pushed SHA. The commit finalizer commits the pinned sibling first and writes its SHA into the pin file before the primary commit. The path must stay inside the checkout, including through symlinks. |
repos.sidecar.builtin.<role> |
object | - | Override for a reserved role; the key must be plans, beads, agents, attachments, or attachments-private. |
repos.sidecar.custom.<role> |
object | - | User-declared document sidecar; the key is the role and must not be a reserved one (plans, beads, agents, attachments, attachments-private). |
repos.sidecar.*.<role>.repo |
string | derived | Optional bare slug or owner/repo pin. |
repos.sidecar.*.<role>.description |
string | - | Purpose shown in inventory; required in generated instructions for lazy entries. |
repos.sidecar.*.<role>.auto_clone |
boolean | false |
Materialize before agent launch; intrinsically ignored for the hidden agents and attachments-private roles. |
repos.sidecar.*.<role>.auto_sync |
boolean | false |
Fetch/fast-forward the primary clone when clean; intrinsically ignored for the hidden agents and attachments-private roles. |
repos.sidecar.*.<role>.visibility |
public/private | public |
Requested visibility for remote creation; project-local private overrides the agents default. attachments resolves to public and attachments-private resolves to private. |
repos.sidecar.*.<role>.disabled |
boolean | false |
Disable the entry and suppress matching implicit sidecars, including agents. |
repos.sidecar.*.<role>.ref.use |
string | role/provider dependent | Installed artifact-reference provider, qualified <plugin>@<id>, to use as the base policy. |
repos.sidecar.*.<role>.ref.kind |
string | role name (plan for plans) |
Prompt kind exposed as @<kind>:<path>. |
repos.sidecar.*.<role>.ref.icon |
string | role/provider dependent | Artifacts tab mark shown beside the pane label. |
repos.sidecar.*.<role>.ref.expansion_format |
string | the {repo_relative_path} file in the {sidecar_role} sidecar repo |
Provider expansion format; see Expansion. |
repos.sidecar.*.<role>.ref.properties |
object | {} |
Typed metadata fields extracted by the provider. |
repos.sidecar.*.<role>.ref.detail |
object | {} |
Metadata fields shown by completion and detail surfaces. |
repos.sidecar.*.<role>.ref.identity |
object | {} |
Optional provider identity rule. |
repos.sidecar.*.<role>.ref.inventory.globs |
list[string] | ["**/*.md"] for document sidecars |
Repo-relative POSIX includes and ! exclusions. |
repos.sidecar.*.<role>.ref.publication |
object | VCS permalink / Markdown references | Publication link and reverse-reference policy. |
Every enabled document sidecar exposes one compact @<kind>:<path> reference. Plans use
the built-in plan provider, and sase repo init records ref: {use: builtin@plan}.
Other document roles default to their role name, the sidecar pointer format
the {repo_relative_path} file in the {sidecar_role} sidecar repo, and **/*.md
inventory even when ref is omitted. Explicit custom path-bound providers may opt into
the {checkout_path} file.
ref.use selects a declarative provider installed through the sase_artifact_refs
plugin entry-point group, qualified as <plugin>@<id> where <plugin> is the literal
builtin or the installed distribution name. Any sibling keys deep-merge over that
provider's base spec. A cloned sidecar does not install its provider: when use names
an unavailable provider or omits its plugin prefix, the role's reference policy is
disabled and sase doctor -C config.repos reports how to fix it. Authors may omit use
and provide the inline fields in the table instead.
ref.macro is retired and invalid. ref.filters.path_globs remains a deprecated alias
that warns and maps to ref.inventory.globs; new configuration must use
inventory.globs. The beads and agents sidecars are entity-backed rather than
document inventories, so document filters do not apply to them. See
Artifact References for canonical prompt forms and
project-context rules.
Workspace numbers 0 and 1 use the linked repo's primary checkout. Higher workspace
numbers use <host_workspace>/sase/repos/linked/<linked_repo>, naturally namespaced by
host project and workspace number. Agent and workflow launch preparation atomically
removes the numbered checkout's entire <host_workspace>/sase/repos/ tree. The required
plans sidecar is then cloned directly from the canonical SSH or local remote resolved
from its recorded metadata; other linked repositories and sidecars remain lazy unless
configured with auto_clone: true. Legacy GitHub HTTPS metadata is normalized before
the clone command is built, and unresolved HTTP(S) metadata stops launch setup before
Git executes. The hidden agents sidecar is excluded from this clone mapping and always
resolves every registered workspace number to
~/.sase/projects/<project_key>/repos/agents. Agents materialize ordinary lazy entries
on demand through /sase_repo. sase repo init manages the tracked /sase/repos/
ignore rule, while SASE also installs the rule in .git/info/exclude before
materialization. SASE passes resolved metadata for all entries and exports
per-repository paths only for materialized entries:
| Variable | Description |
|---|---|
SASE_LINKED_REPOS_JSON |
JSON metadata for all resolved linked repos. |
SASE_LINKED_REPO_<ENV_NAME>_DIR |
Workspace-matched directory for a linked repo. |
SASE_LINKED_REPO_<ENV_NAME>_PRIMARY_DIR |
Primary checkout directory for that linked repo. |
The legacy SASE_SIBLING_REPOS_JSON and SASE_SIBLING_REPO_<ENV_NAME>_* variables are
still emitted alongside the canonical ones during the compatibility window.
<ENV_NAME> is the uppercased, sanitized repo name; duplicates are uniquified with a
numeric suffix.
Source: src/sase/linked_repos.py, src/sase/agent/launch_spawn.py
External repositories¶
External repositories are per-task repos that are not part of the host project's
configured inventory. They require no configuration entry. sase repo open resolves
them after inventory names in two forms:
- Another registered SASE project name opens that project's primary repo from its local
checkout, without network access, under
sase/repos/external/projects/<project>. gh:owner/repo, or theowner/reposhorthand, clones through the installed GitHub workspace provider undersase/repos/external/gh/<owner>/<repo>.
Successful external opens are idempotent, audited, and included in sase repo list,
commit-finalizer enforcement, sase's TUI file and commit deltas, and revert. Agents must
use /sase_repo before reading or modifying any external repo and must use the path
printed by the skill rather than locating or cloning the repo themselves. External repos
are workspace-local and do not create project registry records.
dispatch¶
Configures remote-machine discovery, enrollment records, gateway deadlines, and the
local federation worker. Ordinary config loading is pure: it reads and validates this
section but never discovers peers or contacts a gateway. Use sase machine init,
discover, or status for explicit network work. See the
Remote Dispatch Runbook for the supported setup flow.
dispatch:
providers:
builtin@https:
enabled: true
builtin@tailnet:
enabled: true
machines: {}
discovery:
enabled_providers: [builtin@tailnet]
request_timeout_seconds: 5
status_cache_seconds: 60
federation_worker:
enabled: true
command: ""
sase_home: ""
run_root: ""
socket_path: ""
idle_timeout_seconds: 300
startup_timeout_seconds: 5
request_timeout_seconds: 5
max_frame_bytes: 1048576
remote_hosts: []
| Field | Type | Default | Description |
|---|---|---|---|
dispatch.providers.<ref>.enabled |
bool | provider-defined | Enable one provider for explicit dispatch operations. Built-in HTTPS and tailnet providers ship enabled. |
dispatch.machines |
map | {} |
Viewer-local enrolled aliases. Prefer sase machine init, add, repair, rename, and remove over hand edits. |
dispatch.discovery.enabled_providers |
list[string] | [builtin@tailnet] |
Provider refs queried by explicit discovery. |
dispatch.request_timeout_seconds |
number | 5 |
Positive default deadline for fleet gateway calls. |
dispatch.status_cache_seconds |
number | 60 |
Freshness window reserved for dispatch status caches. |
dispatch.federation_worker.enabled |
bool | true |
Allow the local worker to start when enrolled machines exist. |
dispatch.federation_worker.command |
string | "" |
Worker argv override, parsed without a shell; empty discovers the packaged or linked-development worker. |
dispatch.federation_worker.sase_home |
string | "" |
State root passed to the worker; empty uses the runtime default. |
dispatch.federation_worker.run_root |
string | "" |
Host-local runtime directory; empty derives it from SASE home and host identity. |
dispatch.federation_worker.socket_path |
string | "" |
Unix-socket override; empty derives it from the runtime directory. |
dispatch.federation_worker.idle_timeout_seconds |
number | 300 |
Positive idle time before an unused worker may exit. |
dispatch.federation_worker.startup_timeout_seconds |
number | 5 |
Positive deadline for a newly spawned worker to answer IPC health. |
dispatch.federation_worker.request_timeout_seconds |
number | 5 |
Positive default deadline for worker facade calls. |
dispatch.federation_worker.max_frame_bytes |
int | 1048576 |
Maximum IPC request or response frame size; minimum 128 bytes. |
dispatch.remote_hosts |
list | [] |
Deprecated legacy host list. When non-empty, federation uses it instead of dispatch.machines; do not mix the forms. |
Machine aliases are 1–64 characters, must start with an ASCII letter or digit, and may
otherwise contain ASCII letters, digits, _, ., and -. Each machine record stores a
dispatch provider ref, HTTPS endpoint, opaque credential reference, and pinned
sase_inst_v1_... installation identity. Optional ssh_target stores the SSH
destination used for terminal handoffs; when omitted it defaults to the alias at use
time. Optional fields select gateway, tunnel, or direct connection kind; TLS trust
mode (system_roots, pinned_ca, or pinned_server_name); and quarantine state.
Tokens do not belong in YAML: credential_ref points into the protected local
credential store.
Enrollment commands write those records and keep the installation pin authoritative. A quarantined alias cannot receive launches or lifecycle mutations until repaired.
Source: src/sase/dispatch/config.py, src/sase/dispatch/models.py,
src/sase/config/sase.schema.json
vcs_provider¶
Configures the version control system backend. See docs/vcs.md for the full VCS provider reference including per-command behavior, Git/Mercurial details, and troubleshooting.
GitHub Enterprise host configuration (github_hosts) is owned by the sase-github
plugin; see its
GitHub Enterprise setup walkthrough.
vcs_provider:
provider: auto # "git", "hg", or "auto" (default: "auto")
workspace_root: ~/workspace # optional workspace root directory
default_hooks: # optional list overriding built-in default hooks
- "!$my_presubmit"
- "$my_lint"
pr_tags: # optional key-value tags appended to PR commit messages (keys are rendered SASE_-prefixed, e.g. SASE_BUG)
BUG: "b/12345"
use_project_pr_prefix: false # prepend [<project>] to PR titles (default: false)
| Field | Type | Default | Description |
|---|---|---|---|
vcs_provider.provider |
string | "auto" |
VCS provider: "git", "hg", or "auto" for directory detection. |
vcs_provider.workspace_root |
string | - | Legacy VCS helper workspace root. New numbered-checkout layout is configured by workspace.root below. |
vcs_provider.default_hooks |
list[string] | - | Hook commands added to new Patches. Replaces built-in defaults. |
vcs_provider.pr_tags |
dict[string, str] | {} |
Key-value tags appended as SASE_TAG=VALUE lines to PR commit messages (keys are rendered SASE_-prefixed). |
vcs_provider.use_project_pr_prefix |
bool | false |
Prepend [<project>] to PR titles / PR descriptions (see below). |
When default_hooks is not set, plugins may provide their own defaults via
default_config.yml (for example, Mercurial-specific hooks from a provider plugin). The
core sase package has no built-in default hooks.
When use_project_pr_prefix is true, a [<project>] prefix is prepended to PR
titles (GitHub) or PR descriptions (Mercurial) without polluting the Patch DESCRIPTION
or git commit message. The prefix is automatically stripped when reading descriptions
back.
Source: src/sase/vcs_provider/config.py, src/sase/ace/hooks/defaults.py
vcs_repo_completion¶
Configures repository-name completion inside VCS workflow refs such as #gh:owner/.
vcs_repo_completion:
enabled: true
cache_ttl_seconds: 600
max_repos: 200
| Field | Type | Default | Description |
|---|---|---|---|
vcs_repo_completion.enabled |
bool | true |
Enable sase's TUI and helper-bridge repository completion for registered VCS workflow refs. |
vcs_repo_completion.cache_ttl_seconds |
int | 600 |
Freshness window for the shared on-disk repository candidate cache, in seconds. |
vcs_repo_completion.max_repos |
int | 200 |
Maximum repository candidates kept from a provider response and returned to completion UIs. |
When disabled, sase's TUI does not detect repository-completion triggers, and the editor
helper bridge returns an empty catalog. Repository candidates are listed through
workspace-provider hooks, so provider-specific authentication and network requirements
belong to the installed plugin. For GitHub, the sase-github plugin uses the gh CLI
and can return private repositories visible to the authenticated user.
Source: src/sase/default_config.yml, src/sase/macro/vcs_repo_completion.py
vcs_ref_completion¶
Configures project, Patch, and namespace completion at the root of VCS workflow refs
such as #gh: and #git:.
vcs_ref_completion:
enabled: true
| Field | Type | Default | Description |
|---|---|---|---|
vcs_ref_completion.enabled |
bool | true |
Enable sase's TUI and macro LSP completion at the root of VCS workflow refs. |
When disabled, sase's TUI does not detect VCS ref-root completion triggers and the materialized macro LSP VCS catalog omits namespace rows. Project and Patch candidates come from local ProjectSpecs; provider namespace rows come from fast local workspace-provider hooks.
Source: src/sase/default_config.yml, src/sase/macro/vcs_ref_completion.py
axe¶
Configures the scheduler's routine-based automation. The scheduler architecture uses an
orchestrator that spawns multiple routines, each running a set of jobs on a fixed
interval. Defaults are provided by src/sase/default_config.yml. The config section
keeps its historical axe name.
The YAML below is an abridged illustration of the shipped defaults, not the whole file:
it shows the shape of a lane and a job and omits some lanes and jobs entirely. See
AXE Automation for the complete lane-by-lane inventory, and
src/sase/default_config.yml for the literal defaults.
axe:
max_hook_runners: 3 # concurrent hook runners (default: 3)
max_agent_runners: 3 # concurrent agent runners (default: 3)
zombie_timeout_seconds: 7200 # seconds (default: 7200 = 2 hours)
query: "" # query filter for Patches (default: all)
job_script_dirs: [] # additional directories to search for job scripts
routines:
hooks:
description: |-
Fast lane that advances hook, mentor, and workflow lifecycle state every few seconds
Runs every five seconds with a 90-second per-job timeout so completed work is noticed and new work starts
promptly. Put latency-sensitive Patch lifecycle reconciliation here; slower remote polling, wait
coordination, and maintenance belong in the other lanes.
interval: 5
job_timeout: "90s"
jobs:
- name: hook_checks
script: sase_job_hook_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Complete finished hooks and start stale ones, with zombie detection
Scans hook entries on every matching Patch, records completed process results, and starts stale hooks
when a runner slot is free. Honors max_hook_runners across the tick; stale fix-hook suffixes older than
zombie_timeout_seconds become ZOMBIE, while terminal Patches may finish hooks but cannot start new ones.
- name: mentor_checks
script: sase_job_mentor_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Start mentor workflows once all hook prerequisites are met
Reconciles running mentors, stops mentors left behind by older commits, adds matching mentor profiles, and
launches ready profiles after their hooks finish. Mentor launches share max_agent_runners with other agent
workflows, and review-ineligible or terminal Patches are skipped.
- name: workflow_checks
script: sase_job_workflow_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Complete finished CRS/fix-hook workflows and start stale ones
Reads workflow state from every matching Patch, records results for finished CRS and fix-hook agents,
and launches stale workflows. New workflows share max_agent_runners and the current tick's agent-launch
budget with mentors, so a full runner pool defers work instead of queueing it.
- name: pending_checks_poll
script: sase_job_pending_checks_poll
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: checks
glob: "*/*.txt"
description: |-
Poll background is_cl_submitted and critique_comments checks for results
Scans the pending-check directory once per tick, applies completed results to matching Patches, and
reaps output files orphaned by killed or crashed checks. This job only consumes background results;
pr_submitted_checks and comment_checks launch the remote checks.
- name: comment_zombie_checks
script: sase_job_comment_zombie_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Mark comment threads older than zombie_timeout as ZOMBIE
Examines comment-entry suffix timestamps on matching Patches and writes a ZOMBIE suffix when an entry
exceeds zombie_timeout_seconds. It performs no remote comment fetch; comment_checks starts those checks and
pending_checks_poll applies their results.
- name: suffix_transforms
script: sase_job_suffix_transforms
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Strip stale suffixes from older proposals and update mail-readiness markers
Normalizes matching Patches in place by converting old proposal markers from !: to ~:, removing error
markers from superseded stitches, and acknowledging attention markers on terminal statuses. It only
repairs stored suffix state and never launches hooks or agents.
- name: orphan_cleanup
script: sase_job_orphan_cleanup
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/*.sase"
- path: projects
glob: "*/*.gp"
description: |-
Release workspace claims orphaned by reverted PRs with dead PIDs
Reads all Patches and workspace claims, regardless of the axe query, then releases unpinned claims tied
to Reverted Patches when their owning PID is absent or dead. Live claims and pinned workspaces are left
untouched.
# stale_running_cleanup keeps the default `always` trigger: dead-PID detection
# has no filesystem proxy to watch.
- name: stale_running_cleanup
script: sase_job_stale_running_cleanup
description: |-
Release workspace claims and terminalize proc rows held by dead processes
Walks every project, including disabled projects, and releases unpinned workspace claims whose owning
process has exited. A pinned held claim is preserved while its agent artifacts still exist; this fast-lane
placement frees ordinary dead claims within seconds. It also terminalizes active proc rows whose supervisor
exited without reporting.
waits:
description: |-
Reconcile bead claims and flush orphaned epic launches
Runs every ten seconds so short-lived bead claims are reconciled quickly. Agent dependency resolution
lives in the agent_waits lane and sidecar syncing in the sidecar_sync lane; Patch lifecycle checks and
general cleanup belong in their dedicated lanes.
interval: 10
jobs:
- name: bead_claim_checks
script: sase_job_bead_claim_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/artifacts/.ace_refresh_pulse"
description: |-
Acquire missing bead claims for live pre-launch agents and release claims held by dead ones
Scans pre-launch agent artifacts, backfills a missing claim for a live waiting agent, and releases a
claimed bead when its unpromoted owner has died. Reconciled dead records are tombstoned so later ticks avoid
reopening their stores, while a failed project read is retried safely.
- name: epic_launch_flush
script: sase_job_epic_launch_flush
run_every: "30s"
description: |-
Flush planner completion notifications orphaned by an unsettled epic launch
Preserves deferrals while a matching detached epic-launch task is active, flushes unowned deferrals after
a 90-second grace period with a resume command, and reaps unclaimed settle markers after one hour.
agent_waits:
description: |-
Resolve agent wait dependencies within seconds of completion
Runs every two seconds so waiting agents resume promptly once a dependency finishes. Only agent
dependency resolution belongs here; bead-claim work lives in waits and sidecar syncing in sidecar_sync.
interval: 2
job_timeout: "2m"
jobs:
- name: wait_checks
script: sase_job_wait_checks
trigger:
provider: fs
max_quiet: "120s"
paths:
- path: projects
glob: "*/artifacts/.ace_refresh_pulse"
description: |-
Resolve agent wait dependencies and write ready.json when satisfied
Walks waiting markers across projects, resolves only live waiters (provably dead runners are skipped and
counted in the dead-waiter backlog counter), and resolves named-agent, artifact, and closed-bead
dependencies from filesystem agent metadata and canonical bead state. It writes ready.json only after
every dependency is satisfied; invalid or already-ready markers are skipped without blocking other agents.
sidecar_sync:
description: |-
Fetch and fast-forward opted-in primary sidecar clones
Runs every thirty seconds so sidecar clones converge without delaying agent wait resolution. Only the
sidecar_auto_sync job belongs here; agent dependency resolution lives in agent_waits.
interval: 30
jobs:
- name: sidecar_auto_sync
script: sase_job_sidecar_auto_sync
timeout: "2m"
description: |-
Fetch and fast-forward opted-in primary sidecar clones (plans, beads, research, custom)
Scans every enabled project's auto_sync sidecar roles, syncing a role immediately when a publisher left a
pending hint and otherwise backstopping it at most every five minutes. Projects with a live agent waiting
on bead completion also hint the beads role every tick, even when that role has not opted into auto_sync,
so waiters unblock promptly instead of relying on the runner's coarser fallback. Only a clean, attached,
non-diverged clone with a matching remote is fetched and fast-forwarded; dirty, detached, diverged,
mismatched, missing, or busy clones are left untouched and reported. Bounded work budget and persistent
per-role backoff keep one unhealthy clone from stalling the rest. After a beads-role refresh, touches
the project's completion pulse so agent_waits re-evaluates bead waits on its next 2 s tick.
checks:
description: |-
Poll slower PR-submission and workspace-claim checks on a five-minute cadence
Runs every five minutes for checks that can tolerate delay or may touch remote PR state, reducing needless
polling while retaining a cleanup backstop. Fast hook progression, minute-level comments, and hourly
maintenance deliberately live elsewhere.
interval: 300
jobs:
- name: bead_task_triage
script: sase_job_bead_task_triage
timeout: "2m"
description: |-
Raise one human gate for each ready or snoozed task bead, and for each due flag-typed task bead
Scans enabled projects every five minutes and gives every live task bead exactly one pending
gate: a TaskTriage gate while a task bead is ready and has at least its effective +1 bar in reports
(its own task type's triage.min_plus_ones, else the global bead.task_triage.min_plus_ones), a BeadSnooze
wake gate while it is snoozed, and a FlagTriage gate once a flag-typed task bead's date and release removal
thresholds have both passed. A ready task bead below the +1 bar is withheld from
triage without changing its stored status, and a gate already raised for a bead that falls below the
bar is canceled and its notification dismissed. Deterministic gate generations in lane state prevent
duplicate notifications, a gate of the wrong kind is replaced when its bead's status or due-ness
changes, and a snoozed bead's notification is re-snoozed to its wake time if it ever drifts. Gates are
canceled when their beads leave those states, while answered or missing gates can be regenerated
safely if work remains. Gates stranded by removed projects or forgotten lane state are swept without
touching projects that are only temporarily unreadable. A gateable bead is deferred while a detached
launch is still in flight or a live agent is working the bead, and a pending gate is canceled when a
live agent owns the bead.
- name: plugins_required
script: sase_job_plugins_required
timeout: "2m"
description: |-
Raise one human gate per project whose required plugins are missing
Scans enabled projects every five minutes and compares each project's plugins.required list against
installed distributions. A project with a missing or version-mismatched required set gets exactly one
pending PluginsRequired gate offering Install and Dismiss. Install performs one combined install for
all missing names from the answering surface, sharing a bounded public-index probe and resolving each
plugin from the index unless public PyPI returns a definitive 404, in which case that plugin uses git.
The gate stays pending when planning or the uv mutation fails, including when sase is not a uv tool
install. Dismiss records the decision so the same missing set is not re-offered until it changes. The
job cancels the gate when the set becomes satisfied. Deterministic generations in lane state prevent
duplicate notifications. Agent and non-interactive contexts still fail closed and never auto-install.
- name: pr_submitted_checks
script: sase_job_pr_submitted_checks
description: |-
Start background is_cl_submitted checks for leaf PRs with a submitted parent
Applies the axe query, finds eligible leaf Patches with PR URLs, and launches non-blocking submission
checks whose results are collected by pending_checks_poll. A five-minute sync cache suppresses duplicate
remote work, except that the first cycle checks eligible leaves immediately.
- name: stale_running_cleanup
script: sase_job_stale_running_cleanup
description: |-
Backstop release of workspace claims and proc rows held by dead processes
Runs the same all-project dead-process reconciliation as the hooks-lane cleanup, including conservative
handling of pinned claims with agent artifacts and terminalization of orphaned active proc rows. This
five-minute placement still frees stale workspace claims and proc rows if the fast hooks lane is disabled,
restarting, or repeatedly failing.
# orphan_agent_scope_reap keeps the default `always` trigger: process death
# leaves no filesystem event to watch, so no fs proxy exists - the same
# rationale as stale_running_cleanup.
- name: orphan_agent_scope_reap
script: sase_job_orphan_agent_scope_reap
timeout: "2m"
description: |-
Reap orphaned agent scopes whose runner died without cleaning up
Scans sase-agent scopes under the user manager every five minutes and terminates leaked processes in
scopes with no live runner (SIGKILL, OOM, or crash), using the same spare-process selection rule as the
runner-exit sweep. Scopes holding a live runner or systemd-run window, scopes younger than
agent_scope_teardown.reaper_min_scope_age_seconds, and scopes whose every member is a spared shared
daemon (ssh-agent, gpg-agent, tmux server, ssh ControlMaster) are left untouched. Disabled entirely when
agent_scope_teardown.enabled is false.
comments:
description: |-
Start background critique-comment checks for mailed PRs every minute
Runs every minute so reviewer feedback reaches active Patches promptly without polling on every hooks tick.
Only remote comment-check launches belong here; pending result collection and zombie marking remain in the
faster hooks lane.
interval: 60
jobs:
- name: comment_checks
script: sase_job_comment_checks
description: |-
Start background critique_comments checks for all mailed PRs
Applies the axe query and starts non-blocking critique_comments checks for mailed Patches that have an
available workspace, then records a comment-cycle summary. The routine's one-minute interval is the
polling throttle; pending_checks_poll later consumes each background result.
housekeeping:
description: |-
Run hourly error digests, notification compaction, artifact previews, and cleanup
Runs once an hour because notification batching, live-inbox bounding, bounded scratch reclamation,
artifact/run-retention previews, and stale-backlog cleanup are useful but not latency-sensitive. Put
durable maintenance that may scan substantial local state here, not lifecycle, dependency, or remote polling
work.
interval: 3600
jobs:
- name: error_digest
script: sase_job_error_digest
description: |-
Send a notification digest of errors from the last hour
Reads the AXE error log and the last successful digest timestamp, then notifies only about newer errors
within the rolling one-hour window. The checkpoint advances to the newest notified timestamp, preventing
duplicate digests while leaving unsent errors eligible after a notification failure.
- name: managed_tmp_reap
script: sase_job_managed_tmp_reap
description: |-
Prune stale scratch under the managed SASE temp root
Removes old children from managed-temp buckets using workload-specific age limits, without following
symlinks or deleting the stable bucket directories. Launched agents default TMPDIR/TMP/TEMP,
CARGO_TARGET_DIR, and CARGO_BUILD_BUILD_DIR into managed buckets, and runners remove their own
launch-assigned scratch at exit when no live process still uses it. The reaper can also prune aged large
build output early when the managed root exceeds its size target or the filesystem falls below the
free-space floor; pressure pruning waits for the configured minimum age (12h by default), or 1h once the
free-space floor is breached, while preserving generic agent scratch, handoff data, unknown buckets, and
build trees with fresh descendants. Each pass removes at most 2,000 entries and de-indexes deleted
agent-artifact directories, so a neglected root converges without blocking interactive commands.
- name: proc_runtime_sweep
script: sase_job_proc_runtime_sweep
description: |-
Prune stale rowless proc runtime directories
Removes only canonical proc runtime directories that no longer have a durable proc row, are older than
the configured proc runtime orphan horizon, and are direct non-symlink children of ~/.sase/procs/runtime.
The Rust owner rechecks the proc store under its lock before deletion so concurrent reservations are
preserved. Runtime directories for proc rows actually pruned by retention are deleted immediately by the
proc store path; this job handles historical orphans over a bounded per-pass budget.
- name: disk_pressure
script: sase_job_disk_pressure
timeout: "5m"
description: |-
React when SASE's filesystem crosses disk-pressure thresholds
Checks proportional free-space thresholds, logs and notifies the largest SASE disk owners, then runs
unattended owner-safe cleanup passes early. Managed temp and proc runtime sweeps may apply because their
owners encode deletion policy. Artifact run directories, backups, and unowned Cargo-shaped strays are
reported for human action and are never deleted by this job.
- name: bead_stale_cleanup
script: sase_job_bead_stale_cleanup
timeout: "2m"
description: |-
Sweep stale sub-threshold ready task beads into one BeadStaleCleanup gate
Reads every enabled project's ready task beads and offers those that have sat below
their effective +1 bar (their own task type's triage.min_plus_ones, else the global
bead.task_triage.min_plus_ones) for bead.task_triage.stale_after_days once at least
bead.task_triage.stale_cleanup_min_beads such beads exist. One pending gate at a time
carries at most 50 beads, oldest first; a larger backlog is reported in omitted_count and
offered on later ticks. An unchanged roster leaves the pending gate alone. The gate is
canceled when the backlog drops below the bar.
- name: artifact_run_prune
script: sase_job_artifact_run_prune
timeout: "5m"
description: |-
Preview old ace-run directories and empty shard cleanup
Plans whole-run ace-run retention without deleting anything. The preview keeps the newest
artifacts.retention.keep_recent_run_months calendar months whole, protects runs referenced by
artifact files, text refs, agent names, and non-closed beads, and reports empty month/day shards
outside sase's TUI startup watch window. When unchanged candidates or protection problems remain, it
upserts one deduplicated Axe report notification that names the preview command. Artifact-run
deletion is currently preview-only.
Top-level fields:
| Field | Type | Default | Description |
|---|---|---|---|
max_hook_runners |
int | 3 |
Maximum concurrent hook runners (non-$ hooks) across all Patches. |
max_agent_runners |
int | 3 |
Maximum concurrent agent runners (agents and mentors) across all Patches. |
zombie_timeout_seconds |
int | 7200 |
Seconds after which a running hook or workflow is flagged as a zombie. |
query |
string | "" |
Query string for filtering Patches (empty = all). |
job_script_dirs |
list[string] | [] |
Additional directories to search for external job scripts. |
routine_log_max_bytes |
int | 52428800 |
Maximum bytes retained for each bounded routine log. |
routine_log_temp_max_age_seconds |
int | 300 |
Minimum age before orphaned log-rotation temp files are removed. |
routine_restart_backoff_max_seconds |
int | 60 |
Maximum delay between retries for a crashing routine. |
verbose_routine_diagnostics |
bool | false |
Include verbose diagnostics in job script context JSON. |
routines |
dict | - | Mapping of routine name -> config (see below). |
Routine fields (per entry under routines):
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
description |
string | yes | - | Summary line, blank line, optional body describing the lane's cadence and work. |
interval |
int | no | 1 |
Seconds between job polling cycles. |
job_timeout |
string | no | - | Positive compound duration limit, such as "90s", "1h30m", or "1d". |
wait_runners |
int | no | - | Per-launch capacity budget for proposed lane agents, emitted as %queue(capacity=N); must be at least 1. |
env |
dict[string, env-value] | no | {} |
Environment inherited by every job in this routine. |
jobs |
list[object] or map | no | [] |
Composable job definitions (see below). |
Job fields (per entry under jobs):
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name |
string | list only | - | Stable identity; map form uses the entry key. |
script |
string | no | name |
Exact executable name; no prefix is added automatically. |
enabled |
boolean | no | true |
Soft-disable a keyed entry while retaining inherited fields. |
description |
string | yes | - | Summary line, blank line, optional body describing what the job does. |
run_every |
string | no | - | Positive compound cadence such as "60m", "1h30m", or "1d". |
timeout |
string | no | - | Per-job duration limit. Overrides routine job_timeout. |
env |
dict[string, env-value] | no | {} |
Literal values or {env:}, {file:}, {pass:} references. |
inhibit_if |
list or map | no | - | patch / agent_hood / agent_clan / agent_runners guards before dispatch; changespec is a legacy alias. |
trigger |
string or map | no | always |
always, git.commits_since, or fs scheduled-run trigger. |
once_per |
string or object | no | - | Bounded per-proposal dedupe-key template. |
for_each |
list or source | no | - | Literal targets or the filtered projects source. |
vars |
object | no | {} |
Non-secret values copied to the job context. |
Both description fields use one grammar: a non-blank summary line of at most 100
characters, then — if anything follows — a blank line, then a free-form body, with the
whole string capped at 2000 characters. Violations produce the
description_summary_blank, description_summary_too_long,
description_body_separator_required, and description_too_long diagnostics. See
AXE — Description Grammar for the full contract, the
authoring style guide, and the YAML literal-block form.
Routines were formerly called lumberjacks and jobs were called chops. The old spellings
remain accepted, schema-valid input aliases but are marked deprecated: lumberjacks for
routines, chops for jobs, chop_timeout for job_timeout, chop_script_dirs for
job_script_dirs, and lumberjack_log_max_bytes,
lumberjack_log_temp_max_age_seconds, lumberjack_restart_backoff_max_seconds, and
verbose_lumberjack_diagnostics for their routine_* counterparts. New configuration
should use the canonical keys. With the default-on axe_routine_job_contract sunset
flag, sase config show prints the effective AXE block with the canonical names. Legacy
sase_chop_* executables still exist beside the sase_job_* entrypoints. See
AXE compatibility aliases for the matching CLI,
environment, and artifact-reference aliases.
All jobs are scripts. Exact-name resolution checks job_script_dirs, then the running
interpreter's bin directory, then $PATH. Invalid fields, duplicate identities,
non-positive intervals, and invalid durations fail config loading with a dotted config
path and source-layer diagnostic. agent: and macro: are rejected with a migration
message.
Environment values resolve at dispatch time. Use a literal for non-secret data or
{env: NAME}, {file: path}, and {pass: entry} references for secrets. Routine-level
env is inherited by every job, then a job's own env overrides matching names.
The built-in wait_checks job writes ready.json only after named %wait dependencies
complete successfully. Failed, killed, crashed, still-running, malformed, or missing
done.json artifacts do not satisfy the dependency.
Map form is the composable form. Higher-priority config layers patch matching fields by key, and per-field source provenance is shown by the verbose job inventory:
axe:
routines:
docs:
description:
Refresh project documentation when repositories accumulate meaningful changes
interval: 60
env:
API_TOKEN: { env: DOCS_API_TOKEN }
jobs:
refresh_docs:
description: Refresh documentation after meaningful repository drift
script: sase_job_refresh_docs
run_every: "30m"
trigger:
git.commits_since:
project: "{target.name}"
threshold: 10
checkpoint: on_action_success
for_each:
source: projects
vcs: [git, gh]
packaged_but_disabled:
description: Retain a packaged documentation check without running it
enabled: false
for_each produces stable identities such as refresh_docs[sase-core]. Each instance
has independent scheduling, history, checkpoints, and once-per state. Target data is
available in the context JSON under target and through SASE_JOB_TARGET_KEY /
SASE_JOB_TARGET_<FIELD>. Literal target rows may include overrides: for per-target
job fields such as run_every and trigger thresholds.
inhibit_if accepts keyed patch, agent_hood, agent_clan, and agent_runners
providers. The legacy changespec key remains accepted as an alias. The clan provider
requires a case-sensitive name_prefix and checks canonical clan metadata for active
agents, including waiting members; it never infers clans from dotted names.
agent_runners.max defaults to 0 and inhibits while more than that many participating
agent lanes are occupied, a participating-lane count distinct from the weighted
%queue(capacity=N) admission budget. Weighted capacity is tracked separately by the
runner-capacity snapshot and sase's TUI header. A STARTING agent has not yet been
admitted and does not count; an agent parked on a question has yielded its capacity and
does not count. trigger accepts always, git.commits_since, or fs; the git
provider requires project and threshold, and its checkpoint policy is
on_observation, on_action_accepted, or on_action_success. The fs provider
requires paths (bare path strings or {path, glob} objects, stat'd shallowly — no
recursion, no content reads) and a positive max_quiet duration, fires when its
computed state token changes or max_quiet elapses since the last fire, always uses
on_observation checkpoint semantics, and fails open (fires without advancing its
checkpoint) on an unreadable path. See
AXE — Triggers, Guards, Dedupe, and Targets
for the full contract. Skips are recorded with reasons. Manual runs bypass the trigger
but honor guards; with agent_runners, a manual run while participating lanes are
occupied skips unless sase axe job run -f/--force is used. once_per can be a key
template string or an object with key and bounded capacity; proposal-supplied
dedupe_key values take precedence. dedupe_key is durable work identity, not a retry
clock: it stays reserved after a successful no-op launch, so jobs whose work can go
stale between scans should recheck eligibility with a proposal %if:: predicate (see
Structured Results and Launch Proposals)
instead of folding a repository revision into the key. When dedupe removes a proposal
from a wait_on chain, AXE walks through the skipped dependencies to the nearest
earlier proposal that survives filtering. If none survives, AXE removes the wait.
Proposal previews expose the resulting wait_on value and explain a relink in
dedupe_reason.
The builtin sase_job_refresh_docs emits an update proposal plus a polish proposal that
waits for the update. It uses the target source's workspace, while cadence and commit
thresholds stay declarative in configuration. Its default prompts are strictly
documentation-scoped and tell agents to report suspected code bugs instead of fixing
them. The defaults can be replaced with non-blank vars.prompt and vars.polish_prompt
strings; operators are responsible for the scoping language in replacement prompts. See
Axe structured results and launch proposals
for the result document, proposal fields, lifecycle statuses, and debugging commands.
Every job entry must carry a description following the
description grammar. Bare-string list entries are no
longer valid because they cannot carry one; use map form or object-form list entries:
jobs:
# Object-form list entry
- name: hook_checks
script: sase_job_hook_checks
description: Check for completed or failed hooks
- name: custom_job
script: my_full_executable_name
description: Run custom analysis
run_every: "1h30m"
env:
MY_API_KEY: { env: MY_API_KEY }
CLI flags on sase scheduler run override max_hook_runners, max_agent_runners,
zombie_timeout_seconds, and query for a single run (see CLI Flags).
Source: src/sase/axe/config.py, src/sase/default_config.yml
file_hooks¶
Defines non-gating commands that run once per matching file event. Use
sase file-hook list to inspect the effective hooks, including the config layer that
contributed each entry; add -j/--json for machine-readable output. Use
sase file-hook history and sase file-hook show to inspect producer audits for
whether a matching event was dispatched, unmatched, or failed before a command ran.
file_hooks:
- name: research-highlights
description: Render new research reports into Highlights PDFs.
command: bob highlights create
filters:
projects: [sase]
sidecars: [research]
path_globs: ["20*/**/*.md", "!20*/*__*.md", "!20*/*/*__*.md"]
agent_name_globs:
- "!research.*.cdx"
- "!research.*.cld"
- "!research.*.grk"
- "!research.*.mus"
- "!research.*.gem"
ops: [ADD]
producers: [commit, sdd, finalizer]
timeout: 120s
Hook fields:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
use |
string | no | - | Installed sase_file_hooks provider, qualified <plugin>@<id>, whose template supplies base fields. |
name |
string | yes* | provider ID | Unique lowercase slug shown in notifications and sase file-hook list. |
description |
string | no | provider | Human-readable purpose for the hook. |
command |
string | yes* | provider | Shell command; the matched absolute file path is appended as its final arg. |
filters |
object | no | {} / provider |
Event-selection criteria. Omitted or empty means unrestricted. |
timeout |
duration | no | 120s / provider |
Per-run integer duration with an ms, s, m, or h suffix. |
Without use, name and command are required. With use, SASE deep-merges the local
entry over the installed provider's template, defaults name to the provider ID, and
requires any fields the provider marks as local. use must be qualified as
<plugin>@<id>, where <plugin> is the literal builtin or the installed distribution
name; a bare, unprefixed, or unknown use value disables only that invalid entry, is
reported while sase file-hook list loads effective hooks, and also fails
sase doctor -C config.file_hooks and sase validate so a disappeared hook can never
go unnoticed. A plugin can therefore ship safe defaults while requiring the
machine-specific command or destination in user configuration.
Filter fields under filters:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
filters.projects |
list[string] | no | all projects | User-facing project names. |
filters.sidecars |
list[string] | no | all repos | Sidecar role names such as research, plans, or beads. |
filters.path_globs |
list[string] | no | all files | Repo-relative POSIX globs; ! prefixes a veto exclusion. |
filters.agent_name_globs |
list[string] | no | all agents | SASE agent-name globs matched against the agent that produced the event; ! prefixes a veto exclusion. |
filters.ops |
list[string] | no | all operations | Any subset of ADD, MODIFY, and REMOVE. |
filters.causes |
list[string] | no | none | Non-user event causes to accept in addition to ordinary user commits; currently artifact_links. |
filters.producers |
list[string] | no | all producers | Any subset of artifact, commit, sdd, finalizer, and dispatch. |
Matching semantics:
- Event sources. Hooks receive files from commits created by
sase stitch create(producercommit), commits written through the SDD sidecar commit path (producersdd), andsase artifact create(producerartifact, treated asADD). Finalizer reconciliation re-derives a commit's events (producerfinalizer). Direct engine dispatch uses producerdispatch. - Ops. Commit operations come from
git diff --name-status; renames split intoREMOVEplusADD, root-commit files areADD, and unknown status letters fold toMODIFY. - Path glob matching.
filters.path_globsmatch the file's repo-root-relative POSIX path. Positive globs are OR-ed, while any matching!negative veto-excludes the file.*does not cross/;**does. A negative-only list means “everything except.” Dotfiles are eligible. - Agent-name matching.
filters.agent_name_globsmatch the resolved SASE agent name. Positives are OR-ed and!vetoes, exactly as for paths. Agent names contain no/, so*spans the whole name (research.*.cldmatchesresearch.7.cld). An event with no resolvable agent name matches a negative-only list, but never a list containing any positive pattern. - Attribution. The agent name is resolved in the producing process from
$SASE_ARTIFACTS_DIR/agent_meta.json'sname, falling back to$SASE_AGENT_NAME, so a commit made outside a SASE agent has no agent name. - Causes. Ordinary user-originated commits always remain eligible. Internally
generated commits are ignored unless their cause appears in
filters.causes; the current non-user cause isartifact_links, used when SASE updates managed Links and Referenced By blocks in artifact sidecars. This opt-in prevents a projection update from recursively triggering ordinary document-processing hooks. - Filters. All configured dimensions are AND-ed.
filters.projectscompares alias-resolved, user-facing project names, never ProjectSpec keys.filters.sidecarscompares sidecar role names.filters.producerscompares the dispatch producer identity; omitting it matches every producer. A project-localsase/sase.ymldeclaration withoutfilters.projectsis automatically scoped to the detected project. - Execution. Runs are post-write and non-gating. Hook configuration, matching, persistence, spawn, or command failures never fail or block an artifact copy or a commit. Each matched command runs with the absolute path appended as a shell-quoted final argument and reports success or failure through a SASE notification.
- Artifact store paths. Artifact events match against the original
repository-relative path, but the detached command receives the durable stored copy.
That copy is content-addressed and may use a digest-suffixed basename such as
<stem>-<sha256-prefix>.md. Hooks whose output identity depends on the input basename should setfilters.producersto the committed-file producers (commit,sdd,finalizer) so they do not run against the stored copy. - Producer audits. Every configured-event dispatch attempt persists a bounded record
under the file-hook state root (
audit/) before returning. That record explains a filter miss, a dispatched or already-present batch, or a producer failure without relying on Python logging. Successful matches continue to use batches and run logs as the execution record. Audits follow the same 30-day retention as batches, runner logs, and run logs. - Producer failures vs command-run failures. A producer failure is a problem
before a hook command runs: config load, event capture, batch persistence, or runner
spawn. Those create one non-gating
file-hookserror notification that points at the audit record. Ordinary no-hooks and filter-miss outcomes stay quiet. A command-run failure is the hook command itself exiting non-zero or timing out; that evidence stays on the batch run log and the existing per-run notification. - Finalizer reconciliation. After a successful built-in commit marker is verified, finalization re-derives the committed file events and ensures the deterministic commit batch exists. An already-dispatched batch is reused without spawning again; a missed first dispatch is retried.
The user layer (~/.config/sase/sase.yml) replaces the bundled/default file_hooks
list. Selected machine overlays (sase_*.yml) and project-local sase/sase.yml
concatenate entries onto the effective list, matching mentor_profiles merge behavior.
Hook names must remain unique across the effective list; invalid or duplicate entries
are warned about and skipped. Unknown file_hooks keys are rejected the same way, so a
hook carrying one is skipped with a warning rather than silently losing that filter.
Filter fields at the hook top level are no longer accepted; move projects, sidecars,
path_globs, agent_name_globs, ops, causes, and producers under filters. Note
that globs was renamed to filters.path_globs; the old key is not accepted.
Source: src/sase/config/file_hooks.py, src/sase/file_hooks/engine.py,
src/sase/file_hooks/audit.py, src/sase/file_hooks/producer.py,
src/sase/config/sase.schema.json
plugins¶
Declares the distributions this project needs installed in the running environment. A linked or sidecar checkout is not an install.
plugins:
required:
- sase-github
- sase-research-artifacts>=0.2
| Field | Type | Default | Description |
|---|---|---|---|
plugins.required |
string[] | [] |
PEP 508 requirement strings checked against installed distributions. Duplicate names are an error. |
Every non-builtin <plugin>@ prefix used anywhere in this project config — artifact
references, file hooks, and bead.task_types[].use — must name a distribution listed
here. That single rule keeps a project's declared dependencies honest.
Enforcement is graded by blast radius:
| Surface | Behavior |
|---|---|
sase memory init, sase validate |
Hard error, raised before any memory-drift comparison so a missing plugin never looks like drift |
sase bead create -T 'task(<slug>)' for a missing plugin's slug |
Hard error naming the plugin and sase plugin install <name> |
sase doctor -C plugins.required |
ERROR severity, listing each missing requirement and the install command |
| Interactive human CLI and sase's TUI | A PluginsRequired gate offering to install |
| Agent / non-interactive contexts | Fail closed with the human-directed command; never auto-install |
sase bead show / list of an unknown type |
Degraded render, never a failure |
use: values themselves must be qualified as <plugin>@<id>, where <plugin> is the
literal builtin or a distribution name. A bare value is a hard error that names the
correct replacement when the live registry can resolve it.
See Task Types for how required plugins feed the committed
sase/task_types.json snapshot, and
Required Plugin Notification for the
human install offer.
Source: src/sase/plugins/required.py, src/sase/config/sase.schema.json
mentor_profiles¶
Defines mentor agents that run automated code reviews when a Patch's diff, changed files, or amend notes match configurable criteria. Each profile groups one or more mentors with shared matching rules. See docs/mentors.md for the full mentor system reference.
mentor_profiles:
- profile_name: python_review
file_globs:
- "*.py"
mentors:
- mentor_name: style_checker
role: "Python style expert"
focus_areas:
- focus_name: style
description: "PEP 8 compliance and code style"
- focus_name: naming
description: "Variable and function naming conventions"
- profile_name: first_commit_review
first_commit: true
mentors:
- mentor_name: architecture
role: "Software architect"
focus_areas:
- focus_name: design
description: "Overall design and architectural patterns"
Profile fields:
| Field | Type | Required | Description |
|---|---|---|---|
profile_name |
string | yes | Unique name identifying this profile. |
mentors |
list | yes | List of mentor definitions (see below). |
file_globs |
list[string] | no* | Glob patterns matched against changed file paths. |
diff_regexes |
list[string] | no* | Regex patterns matched against the diff content. |
amend_note_regexes |
list[string] | no* | Regex patterns matched against commit/amend notes. |
first_commit |
bool | no | If true, match only on the first commit of a Patch. |
projects |
list[string] | no | Only match Patches in these projects. Auto-set for local sase.yml profiles. |
*At least one of file_globs, diff_regexes, amend_note_regexes, or first_commit
must be provided per profile.
Mentor fields:
| Field | Type | Required | Description |
|---|---|---|---|
mentor_name |
string | yes | Unique name identifying this mentor within its profile. |
role |
string | yes | Role or persona for the mentor (e.g., "Security reviewer"). |
focus_areas |
list[object] | yes | List of review focus areas (see below). |
Focus area fields:
| Field | Type | Required | Description |
|---|---|---|---|
focus_name |
string | yes | Short name for this focus area (e.g., "correctness"). |
description |
string | yes | Description of what this focus area reviews. |
Mentors run automatically on Patches with Ready or Mailed status when their matching
criteria are met. Mentor comments are structured JSON with severity levels (error,
warning, suggestion) that can be reviewed and applied through the Mentor Review modal in
sase's TUI (,C).
Source: src/sase/config/mentor.py
metahooks¶
Metahooks intercept failing hooks before the summarize agent runs. They match based on the hook command (substring match) and the hook output (regex match). When a metahook matches, it can trigger specialized handling instead of the default summarization.
metahooks:
- name: scuba
hook_command: sase_hg_presubmit
output_regex: "SCUBA_ERROR.*timeout"
- name: flaky_test
hook_command: blaze test
output_regex: "FLAKY"
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Unique identifier for this metahook. |
hook_command |
string | yes | Substring matched against the executed hook command. |
output_regex |
string | yes | Regex pattern matched against hook output (multiline). |
Source: src/sase/config/metahook.py
macros¶
Defines reusable prompt snippets that can be referenced with #name syntax in any
prompt. Supports both simple string content and structured definitions with typed inputs
and Jinja2 templates.
macros:
# Simple string format
greeting: "Hello, please review this code."
# Structured format with inputs
review:
input:
language: word
strict: { type: bool, default: false }
content: "Review this {{ language }} code.{{ ' Be strict.' if strict }}"
# With tags for semantic role lookup
my_crs:
content: "Summarize the code review..."
tags: [crs]
Macros use the shared first-wins content-layout order:
- Project
sase/macros/, then legacy project.macros/andmacros/ - Home
~/sase/macros/, then legacy home~/.macros/and~/macros/ ~/sase/macros/{project}/, then legacy~/.config/sase/macros/{project}/- Project
sase/sase.yml(rootsase.ymlis an exclusive legacy fallback) - User
sase_*.ymloverlays, then~/.config/sase/sase.yml - Plugin config and package default config
- Plugin macro resources
<sase_package>/default_macros/*.md, then<sase_package>/macros/*.md
Earlier sources win on name conflicts. Project and home canonical directories are the only writable filesystem destinations; legacy directories remain read-compatible but are not offered for new saves. File-based macros use YAML front matter for metadata and the file body for content. The Macro discovery table lists every source separately.
Source: src/sase/macro/loader.py
macro_aliases¶
Defines raw text-level alias substitutions that are applied before any macro processing. This is useful for creating shorthand references where the alias must be present in the raw text for other processing logic (such as VCS directory-switching) to work correctly.
macro_aliases:
c: commit # #c → #commit
p: propose # #p → #propose
deploy_notes: "release-notes" # #deploy_notes → #release-notes
gh_foo: "gh:foo/bar" # #gh_foo → #gh:foo/bar
| Field | Type | Default | Description |
|---|---|---|---|
macro_aliases |
dict[string] | {c: commit, p: propose} |
Mapping of alias name → target. Applied as text substitution |
The built-in defaults provide #c as a shorthand for #commit and #p for #propose.
Additional aliases can be added in user config files.
Each entry maps an alias name to a target string. When the processor encounters
#alias_name in a prompt, it replaces it with #target before any other macro
resolution occurs. Only #-prefixed references are substituted; the alias name must
match [a-zA-Z_][a-zA-Z0-9_]*.
Source: src/sase/macro/processor.py
use_chezmoi¶
Enables chezmoi-aware home-file writes. When set to true, SASE writes generated home
instructions, memory, skills, and home-directory macro paths through the chezmoi source
tree under ~/.local/share/chezmoi/home/ instead of writing the live home files
directly. Canonical ~/sase/macros/ and ~/sase/memory/ map to source paths
home/sase/macros/ and home/sase/memory/. The unchanged global config still maps to
home/dot_config/sase/sase.yml.
This affects initialization workflow as well as macro editing. sase memory init
targets the chezmoi home source root when it needs to initialize home-level AGENTS.md,
writes home memory there, and may run the configured chezmoi deploy path;
sase skill init writes provider skill files there before optional commit, push, and
apply steps.
Home-level provider instruction files (CLAUDE.md, GEMINI.md, QWEN.md,
OPENCODE.md) in the chezmoi source are byte-for-byte copies of the root's generated
AGENTS.md. When no machine overlay declares memory.h1_title, those copies stay
static .md files. When a machine overlay does declare a title, sase memory init
writes AGENTS.md.tmpl and preferred *.md.tmpl shims whose H1 switches on
.chezmoi.hostname, and migrates leftover static copies away. Legacy *.md.tmpl shims
that imported @{{ .chezmoi.homeDir }}/AGENTS.md are still recognized and migrated to
full copies.
use_chezmoi: true # default: false
| Field | Type | Default | Description |
|---|---|---|---|
use_chezmoi |
bool | false |
Write home-managed SASE files through the chezmoi source directory. |
Source: src/sase/config/core.py
commit_hooks¶
Shell commands that bracket commit-producing VCS dispatches. before runs in the
repository root after bead and plan mutations but before diff capture and dispatch.
after runs in the repository root only after create_commit or create_pull_request
succeeds, including its push where applicable. Proposals run before but never run
after because they save a diff without creating a commit.
Both fields default to an empty string. Because the object is deep-merged, a global
before hook and project-local after hook compose without either configuration
repeating the other phase.
commit_hooks:
before: "just fix" # default: ""
after: "chezmoi update -a --force" # default: ""
| Field | Type | Default | Description |
|---|---|---|---|
commit_hooks.before |
string | "" |
Command before diff capture and VCS dispatch. Empty means disabled. |
commit_hooks.after |
string | "" |
Command after a commit/PR dispatch and push succeed. Empty means disabled. |
Hook output is captured and a bounded stdout/stderr tail is printed on failure; each run
also leaves per-run evidence files described in
Commit Hook Evidence. A failing before
hook aborts before dispatch. A failing after hook leaves the commit checkpoint in
place and returns failure even though the commit may already be pushed; fix the command
and run sase stitch create --resume. The completed after-hook step is checkpointed so
a normal resume does not rerun it. A crash after the external command succeeds but
before that checkpoint write can run it again, so after commands must be safe to
repeat.
Source: src/sase/default_config.yml, src/sase/workflows/commit/commit_hooks.py,
src/sase/workflows/commit/workflow.py
gate¶
Settings for durable, command-backed gates. The only current setting controls how the
hourly gate_turn_reclaim housekeeping job settles a
gate turn whose gate has already passed
its own deadline.
gate:
turn:
reclaim_grace_seconds: 3600
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
gate.turn.reclaim_grace_seconds |
int | 3600 |
0 |
Seconds after a turn gate's deadline before reclaim force-settles the pending turn as lost. |
Within the grace window, reclaim cancels an expired gate and settles its turn as a
normal timeout; once the window has passed, a still-pending turn settles as lost
instead. A missing, negative, or non-integer value falls back to 3600. While
legacy_sase_shell_syntax is enabled, the retired gate.shell .reclaim_grace_seconds
key is accepted as this same setting. Setting both keys is an error; use only
gate.turn.
Source: src/sase/default_config.yml, src/sase/gate_turn/reclaim.py
max_running_agents¶
The configured global runner-capacity budget across all projects. The key remains an
integer and the packaged default remains 10, but the value is interpreted as capacity
units. A normal launch claims 1.0; %queue(weight=...) / %q(w=...) can request a
non-negative finite weight (%q(w=0) adds no load but still takes its queue turn).
A standalone agent owns one claim of its effective weight. A live serial session shares
one claim across its agent, monitor, and serial successor turns; serial continuations
inherit the session weight when omitted, and must reacquire capacity after the session
has released its claim. Independently launched clan members and live parallel clan
members each hold their own claim. A processless gate turn deliberately releases runner
capacity while it owns a user decision, even when it retains the session's workspace
claim. Workflow Python/bash steps and axe Patch runners hold none of this capacity; axe
runners continue to use their separate axe.max_*_runners limits.
max_running_agents: 10
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
max_running_agents |
int | 10 |
1 |
Configured runner-capacity units available on this host. |
The effective cap is an active machine-wide temporary override first and this merged
configured value second. In Launch Control, fixed Ctrl+R opens Runner Capacity:
e previews and writes the user-base/chezmoi source, o chooses a relative, custom,
until-cleared, or exact-time override, and x clears it. Temporary state is stored as a
versioned record at ~/.sase/max_running_agents_override.json; a new set replaces the
previous value, expiry is enforced at its deadline, and a persistent edit leaves an
active override in force. Lowering the effective value is non-preemptive, so existing
agents continue and new launches wait for occupied capacity to drain. Parked waiters and
question continuations reread the effective cap on each normal poll. With the default-on
queue_capacity_budget sunset flag, an explicit %queue(capacity=N) (or
sase bead work -c/--capacity N for an epic) uses that positive-integer value as the
launch's own admission budget, replacing the global budget for that launch only. Once
admitted, the launch holds an ordinary weighted claim, so occupied capacity can honestly
exceed the global budget until work drains.
An authored %queue(capacity=<M>x) instead scales from this effective value, including
an active override: %q:1.5x resolves to 7.5 when the effective limit is 5. It is
resolved again while the launch is queued, rather than freezing the configured value at
launch time. Multipliers accept up to two decimal places and affect admission only while
queue_capacity_budget is enabled.
When upgrading from an unweighted scheduler build, restart sase's TUI and the service
host and let already running agent processes finish or relaunch them under the new
binary. Legacy records without queue_weight still read as 1.0, but mixed old and new
admission processes do not provide a safe weighted-capacity rollout because old binaries
do not enforce weighted claims.
max_agent_pipe_chain¶
The bound on how many times one agent session may hand its turn forward with
sase pipe. The originally launched agent is depth 0; each successful pipe records
pipe_depth on the successor and increments it by one. A pipe is refused when the next
link would exceed this value, and the refusal names the limit, this configuration key,
and the chain length already reached. The calling agent stays alive on a refusal, so it
can finish the work itself instead of handing it on.
max_agent_pipe_chain: 8
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
max_agent_pipe_chain |
int | 8 |
1 |
Maximum sase pipe hops in one agent session chain. |
This is a configuration field rather than a feature flag: the number is one users choose
permanently to stop a self-piping chain from running away. A missing or malformed value
falls back to the packaged default rather than allowing an unbounded chain. Only
sase pipe records pipe_depth, so a plan-approval, question, or monitor follow-up
member created in between starts the count over at 0. The bound is per session chain,
not per host, so it is unrelated to the concurrency cap in
max_running_agents.
Source: src/sase/default_config.yml, src/sase/config/core.py,
src/sase/main/pipe_handler.py
runner_slots¶
Bounded deference for deprioritized runner-slot waiters. Admission sorts eligible
waiters by lower numeric %queue(priority=N) first, but that sort only compares agents
already parked at the instant a slot frees. Dependency-chained work joins the queue
seconds after its predecessor exits, so a long-parked deprioritized waiter would
otherwise reliably win that race against exactly the normal-priority work it was meant
to yield to. A waiter whose priority is numerically worse than the 10 default
therefore holds back for a bounded window instead of claiming the moment it becomes
eligible, which gives a better-priority agent time to park and win through the existing
sort.
runner_slots:
deference_seconds_per_step: 3
deference_max_seconds: 60
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
runner_slots.deference_seconds_per_step |
int | 3 |
0 |
Seconds of deference added per priority step worse than the default. |
runner_slots.deference_max_seconds |
int | 60 |
0 |
Upper bound on the deference window regardless of priority. |
The window is min((priority - 10) * deference_seconds_per_step, deference_max_seconds)
seconds. With the defaults, priority=20 defers for up to 30s and any priority at or
beyond 30 clamps to the 60s cap. Priority 10 is the boundary and it is inclusive:
default-priority and better-priority waiters (priority <= 10) never defer and claim on
the first eligible poll exactly as before. The asymmetry is deliberate—you cannot defer
to work that may never arrive, so only an agent that explicitly volunteered to be
deprioritized pays a delay.
Deference measures continuous eligibility, not total time parked. The window starts
when the waiter first becomes eligible while a better-priority agent is pending, and it
resets whenever the waiter stops being eligible, so time spent parked behind a full cap
never counts toward it. It also exits early: on any poll where no live, unstarted,
not-yet-parked agent with a better priority remains, the waiter claims immediately
instead of serving out the rest of the window. The agent log prints one
Deferring for up to Ns (priority N) line on entry into the window.
Both fields are read fail-open. Deference is a politeness optimization, so a missing,
non-integer, negative, or unreadable value falls back to the built-in default rather
than propagating an error. This differs deliberately from max_running_agents, where
configuration errors do propagate: a bad value here must never strand a runner.
Bounded deference is not priority aging and not preemption. A running agent is never
stopped to make room, and a deferred waiter's own priority does not improve while it
waits. See Agent waiting for a runner slot for
diagnosis, and %queue(priority=N) for the directive
itself.
agent_scope_teardown¶
Lifetime of processes an agent starts. An agent runner's sase-agent-* scope bounds
every process the agent starts: leftovers are swept at runner exit and between
in-process successor turns, and long-lived work must escape via detach_scope into its
own scope. Members matching a spare pattern (plus their descendants) are never killed,
so shared user daemons survive.
agent_scope_teardown:
enabled: true
term_grace_seconds: 3
reaper_min_scope_age_seconds: 120
spare_process_patterns:
- "^ssh-agent$"
- "^gpg-agent$"
- "^tmux: server$"
- '^ssh: .*\[mux\]$'
| Field | Type | Default | Description |
|---|---|---|---|
agent_scope_teardown.enabled |
bool | true |
Sweep the runner's own scope at exit and between successor turns. |
agent_scope_teardown.term_grace_seconds |
number | 3 |
Grace between SIGTERM and SIGKILL when sweeping leaked processes. |
agent_scope_teardown.reaper_min_scope_age_seconds |
number | 120 |
Minimum scope age before the orphaned-scope reaper may sweep it. |
agent_scope_teardown.spare_process_patterns |
string[] | ssh-agent, gpg-agent, tmux server, ssh mux (see above) | Regex list (re.search over comm or cmdline) never killed by a sweep. |
This is a config field, not a feature flag: users may permanently disable it or extend the spare list, and the sweep is on by default. An invalid spare pattern is logged and ignored. The sweep is Linux/cgroup-v2 only and a no-op elsewhere.
Source: src/sase/default_config.yml, src/sase/config/_settings_system.py,
src/sase/agent/scope_sweep.py
agent hold limits¶
Three top-level keys bound agent holds armed by the sase agent hold create / run
commands and the %hold directive.
agent_hold_default_ttl: 2h
agent_hold_max_ttl: 12h
agent_hold_confirm_capture_threshold: 10
| Field | Type | Default | Description |
|---|---|---|---|
agent_hold_default_ttl |
string | 2h |
TTL for a hold armed without an explicit TTL. Bare seconds or a number suffixed with s, m, or h. |
agent_hold_max_ttl |
string | 12h |
Largest TTL a hold may request. sase agent hold create / run reject a larger -T/--ttl. |
agent_hold_confirm_capture_threshold |
int | 10 |
Frozen pending capture size above which a launch surface may ask for confirmation of the hold preview. |
The launch preview lists each %hold with its scope and a TTL resolved against these
values, showing both the default and the cap. sase's TUI and an interactive sase run
ask for confirmation when a preview combines future with scope=host, regardless of
the threshold. They can also ask when a pending capture exceeds the threshold, but the
capture check needs project context: the TUI supplies it, typed launch plans can resolve
it, and a plain project-scoped sase run prompt currently cannot. The Admin Center
Config tab's Holds child lists and releases active holds, and
sase doctor -C agent_holds.stale reports holds whose armer died or whose TTL passed.
A missing or unparsable TTL, or a negative or non-integer threshold, falls back to the default shown above rather than failing the command.
Source: src/sase/default_config.yml, src/sase/config/_settings_runner.py,
src/sase/agents/cli_hold.py, src/sase/agent/launch_hold_preview.py
procs¶
Durable proc records live in ~/.sase/procs/procs.jsonl, with combined output logs
under ~/.sase/procs/logs/. Retention keeps every pending or running proc plus the
newest configured number of finished procs. Lowering the limit trims the oldest finished
rows and their logs; active work is never pruned. Finished procs tagged command-line
(TUI Command Line submissions) keep a separate bucket of 50 beside this limit, so heavy
Command Line use never evicts operational proc history.
Each proc also has a runtime directory under ~/.sase/procs/runtime/. Retention deletes
a pruned row's runtime directory immediately, while the hourly proc_runtime_sweep
housekeeping job removes older rowless (orphaned) runtime directories within the horizon
and per-pass budget below. Fresh orphans are left alone so a launch that is still
reserving its row is never raced.
procs:
history_limit: 100
runtime_orphan_horizon_seconds: 259200
runtime_orphan_max_removals: 2000
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
procs.history_limit |
int | 100 |
1 |
Number of finished procs to preserve. |
procs.runtime_orphan_horizon_seconds |
int | 259200 |
0 |
Age before the hourly proc runtime owner may remove rowless runtime directories. |
procs.runtime_orphan_max_removals |
int | 2000 |
1 |
Maximum historical rowless proc runtime directories removed in one pass. |
tasks.history_limit |
int | 100 |
1 |
Deprecated alias for procs.history_limit; use procs.* for proc runtime retention settings. |
The top-level tasks block is still schema-valid, and sase config layers and
sase doctor flag it for migration. In practice the legacy key has no effect in a
normal merged configuration: the bundled defaults always supply procs.history_limit,
which takes precedence over tasks.history_limit. Move the value to
procs.history_limit to change retention.
service¶
The per-machine service host supervises configured daemon procs and transient oneshots.
Its catalog is deliberately machine-owned: builtin defaults, plugin defaults, user
config, and machine overlays compose field by field, while project-local sase/sase.yml
service: entries are ignored — the host resolves machine-level layers only, never a
project-local sase.yml. Lists replace earlier lists whole; env is the one map that
merges key by key. A section-level error fails closed, while an invalid individual entry
stays visible as unavailable and does not block valid peers.
service:
procs:
scheduler:
builtin: scheduler
description: "SASE's background automation: routines and their jobs."
gateway:
builtin: gateway
description: "Mobile gateway HTTP API for remote agent dispatch."
enabled: false
indexer:
command: [my-indexer, --watch]
cwd: ~/work
env:
LOG_LEVEL: info
restart: on-failure
after: [scheduler]
The shipped scheduler entry is enabled and owns routine/job automation. The shipped
gateway entry is disabled so a default host does not expose a mobile API on machines
that never paired a phone; it is enabled per machine in the overlay where the gateway is
wanted. Builtin launchers must use the entry's own reserved name. A custom entry instead
supplies exactly one command: a string runs through sh -c, while a string array runs
directly as argv.
| Field | Type | Default | Description |
|---|---|---|---|
service.procs.<name>.description |
string | - | Summary line, then a blank line and an optional body (grammar); shown in the Services-tab description panel and sase service proc show. A missing blank separator is tolerated: the body starts at line 2. |
service.procs.<name>.enabled |
bool | true |
Desired machine state. Plugin entries and the builtin gateway default to false; a saved machine override can replace it. |
service.procs.<name>.mode |
daemon |
daemon |
Config entries are daemons. Oneshots are transient submissions from sase service proc run. |
service.procs.<name>.command |
string or non-empty string array | - | Shell command or exact argv. Mutually exclusive with builtin. |
service.procs.<name>.builtin |
scheduler or gateway |
- | Reserved packaged launcher. Mutually exclusive with command and must equal the entry name. |
service.procs.<name>.cwd |
string | inherited | Working directory for the child. |
service.procs.<name>.env |
string map | {} |
Extra environment. ${NAME} expands a captured variable at launch; $$ is a literal dollar sign. |
service.procs.<name>.restart |
always, on-failure, or never |
on-failure |
Restart policy. |
service.procs.<name>.success_exit_codes |
integer array | [] |
Additional 0-255 exit codes treated as clean; zero is always clean. |
service.procs.<name>.stop_signal |
common signal name | SIGTERM |
Graceful stop signal, with or without the SIG prefix. |
service.procs.<name>.stop_timeout |
seconds, > 0 and <= 3600 |
10 |
Grace period before SIGKILL. |
service.procs.<name>.after |
name array | [] |
Start ordering only. Unknown names warn and are dropped; a cycle makes every entry in it unavailable. |
service.procs.<name>.log_max_bytes |
integer, at least 4096 |
2097152 |
Maximum retained bytes in the rotated proc output log. |
A bare command array's first element (for example sase_job_tg_inbound) is looked up
on PATH first and then in the bin directory of the interpreter running the host. The
second lookup is what makes a plugin's console scripts work under
uv tool install --with, which links only the primary package's entry points onto
PATH. A PATH hit always wins, an element that contains a path separator is used as
written, and the remaining arguments are never changed. The string (shell) form gets no
such fallback, because the shell resolves the name; prefer the array form for plugin
scripts. sase service init warns when an enabled entry's executable resolves nowhere.
sase service proc enable/disable writes machine-local effective enablement without
editing YAML. stop changes only the current boot: it records a marker that is cleared
on the next host boot. start and restart record a durable, numbered request that
also clears that marker; the host consumes it and the CLI waits for the confirmed pid.
Use sase service proc show NAME to see where an effective entry and enablement came
from, plus the current restart decision and any pending request.
Restart policy: always restarts after every exit and on-failure after a non-clean
one, each with a backoff that starts at 1 second and doubles up to 60 seconds. A clean
exit is exit code 0, a success_exit_codes code, or death by SIGTERM, SIGINT,
SIGHUP, or SIGPIPE; a stop the host itself requested is never restarted. Three
restart-triggering exits or spawn failures within 60 seconds mark the proc crash_loop
and raise one service notification per crash-loop episode; a run of at least 5 minutes
resets the backoff and ends the episode. When the policy decides not to restart — a
clean exit under on-failure, or any exit or spawn failure under never — the host
parks the proc instead of relaunching it on the next reconcile, and raises a service
notification when the proc is still desired running. A parked proc stays down until an
explicit start/restart request, a changed entry, a disable-then-enable cycle, or a
restart of the service host itself. If the service.procs configuration stops loading,
the host keeps supervising its last-known-good configuration and surfaces the load error
as the host error in sase service status; a host that has never loaded a valid
configuration launches nothing until it does.
sase service init --yes installs an idempotent user unit (sase.service under
systemd --user on Linux, sh.sase.service as a macOS LaunchAgent), captures only the
allow-listed provider credentials, PATH, SASE_TMPDIR, SASE_HOME, and configured
mobile credential environment variable into a mode-0600 file, retires legacy
gateway/AXE units, enables the unit, and starts it. SASE_FEATURE_FLAGS is never
captured: the host resolves flags from saved state. Planning and diffs redact every
captured value. Linux warns when user linger is off because the service may then stop at
logout. See sase service for lifecycle commands.
Source: src/sase/default_config.yml, src/sase/config/sase.schema.json,
src/sase/service/config.py, src/sase/service/platform.py
disk¶
Disk-pressure thresholds combine absolute byte floors with proportional free-space
floors, so large volumes warn before they are almost out of bytes. The doctor resource
check uses these values, and the hourly disk_pressure housekeeping job uses the warn
threshold to decide when to notify and run owner-safe cleanup early.
disk:
pressure:
warn_free_percent: 5.0
error_free_percent: 1.0
top_owner_min_bytes: 1073741824
| Field | Type | Default | Range | Description |
|---|---|---|---|---|
disk.pressure.warn_free_percent |
number | 5.0 |
0-100 |
Warn when filesystem free space falls below this percentage. |
disk.pressure.error_free_percent |
number | 1.0 |
0-100 |
Error when filesystem free space falls below this percentage. |
disk.pressure.top_owner_min_bytes |
int | 1073741824 |
>= 0 |
Minimum unowned row size named directly in pressure diagnostics. |
sase doctor still honors its absolute floors of 3 GiB for WARN and 1 GiB for ERROR;
the effective threshold is the larger of the absolute and proportional values. Use
sase disk list to see SASE disk usage by owner and sase disk reap to preview or run
the owners' cleanup passes.
Source: src/sase/default_config.yml, src/sase/core/disk_pressure.py
managed_tmp¶
Age horizons and pressure thresholds for the managed SASE temp root: $SASE_TMPDIR when
set, otherwise $SASE_HOME/tmp (~/.sase/tmp by default). SASE writes scratch there in
per-purpose bucket directories, and launched agents get TMPDIR/TMP/TEMP,
CARGO_TARGET_DIR, and CARGO_BUILD_BUILD_DIR pointed into managed buckets. Runners
remove their own launch-assigned scratch at exit when no live process still uses it;
sase disk reap and the hourly managed_tmp_reap housekeeping job clean up the rest
with these values.
Each bucket belongs to one horizon category. Editor, wrapper, per-agent agent-tmp, and
similar command scratch uses the command-scratch horizon; handoff, gh-diffs, and
muse-prompts use the handoff horizon; build-targets and cargo-targets use the
build-scratch horizon; and launch-prompts, screenshots, and workflow-artifacts,
which sase's TUI and screenshot tooling read back after a run ends, use the run-artifact
horizon. Unknown buckets and stray top-level entries are aged at the handoff horizon.
The bucket mapping is fixed; only the durations and pressure thresholds are
configurable.
Between the age horizons runs a dead-launch backstop over the launch-keyed buckets
(agent-tmp, cargo-targets, legacy build-targets) for launches that never ran
runner-exit cleanup — crashed, SIGKILLed, OOM-killed, or handed off to a monitor/gate
follow-up, which mints a fresh scratch key and leaves the old one quiet. Entries whose
newest descendant write predates dead_launch.grace_seconds and that no live process
holds (by environment or working directory, observed through procfs in one batch scan)
are removed largest-first within the removal budget. Held and incomplete-observation
entries are preserved and counted; hosts without readable procfs skip the pass and keep
the age horizons as the fallback.
Pressure pruning is a later pass that removes aged, large build scratch early when the
managed root grows past pressure.max_bytes or the filesystem's free space drops below
pressure.min_available_bytes. It never prunes generic agent scratch, handoff data,
unknown buckets, or build trees with fresh descendants. While the procfs observer is
available it is liveness-aware: a held entry is never removed, an incomplete observation
is preserved, and an unheld entry needs only the dead-launch grace rather than
pressure.min_age_seconds.
Every root get_sase_managed_tmpdir() writes into is recorded in a Rust-owned registry
at $SASE_HOME/managed_tmp/roots.json (schema version plus
{path, first_seen_epoch, last_seen_epoch} entries, written atomically under a lock
file). The hourly managed_tmp_reap job, the disk_pressure cleanup, and
sase disk reap each reap the effective root plus every registered root that still
exists, so scratch is bounded no matter which environment its writer resolved. A root
nested inside another covered root is folded into it, so it is not reaped or counted
twice. Registration is fail-open and never breaks a launch; only absolute, non-symlink
directories enroll, and broad roots such as /tmp or $HOME are refused.
In practice, the housekeeping managed_tmp_reap job, the disk_pressure job, and
sase disk reap all pass the shared disk.pressure warn threshold (the larger
of 3 GiB and disk.pressure.warn_free_percent) as both the free-space floor and the
recovery target, so pressure.min_available_bytes and
pressure.recovery_available_bytes currently take effect only for direct reaper calls
that leave those values unset.
Launched agents export CARGO_INCREMENTAL=0 by default, or CARGO_INCREMENTAL=1 when
agent_cargo_incremental is enabled. Only hosts with the splitting rustc wrapper
(athena) should opt in; elsewhere incremental test builds cost ~9 GB per run.
managed_tmp:
horizons:
command_scratch_seconds: 43200
handoff_seconds: 259200
build_scratch_seconds: 86400
run_artifact_seconds: 1209600
max_removals: 2000
dead_launch:
enabled: true
grace_seconds: 7200
pressure:
max_bytes: 17179869184
target_bytes: 8589934592
min_available_bytes: 34359738368
recovery_available_bytes: 51539607552
min_age_seconds: 43200
low_free_space_min_age_seconds: 3600
min_entry_bytes: 67108864
agent_cargo_incremental: false
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
managed_tmp.horizons.command_scratch_seconds |
int | 43200 |
0 |
Age horizon for scratch whose reader is the command that wrote it (editors, wrappers). |
managed_tmp.horizons.handoff_seconds |
int | 259200 |
0 |
Age horizon for files handed to a child process that may re-read them mid-run. |
managed_tmp.horizons.build_scratch_seconds |
int | 86400 |
0 |
Age horizon for Cargo and other build scratch created for one launched agent (non-procfs fallback; the dead-launch backstop reaps unheld launch scratch sooner). |
managed_tmp.horizons.run_artifact_seconds |
int | 1209600 |
0 |
Age horizon for run artifacts sase's TUI Agents tab reads back long after the run finished. |
managed_tmp.max_removals |
int | 2000 |
1 |
Removal budget for one reaper invocation, so a long-neglected root converges over passes. |
managed_tmp.dead_launch.enabled |
bool | true |
Run the dead-launch backstop pass over launch-keyed scratch no live process holds. | |
managed_tmp.dead_launch.grace_seconds |
int | 7200 |
0 |
Quiet age before scratch no live process holds may be reaped by the backstop (and by liveness-aware pressure). |
managed_tmp.pressure.max_bytes |
int | 17179869184 |
0 |
Managed-root size that triggers pressure pruning of aged build scratch. 0 disables it. |
managed_tmp.pressure.target_bytes |
int | 8589934592 |
0 |
Managed-root size the pressure pass tries to return to. |
managed_tmp.pressure.min_available_bytes |
int | 34359738368 |
0 |
Filesystem free-space floor that also triggers pressure pruning. 0 disables it. |
managed_tmp.pressure.recovery_available_bytes |
int | 51539607552 |
0 |
Filesystem free-space target used after crossing the low-space floor. |
managed_tmp.pressure.min_age_seconds |
int | 43200 |
0 |
Minimum age before pressure can prune a large scratch entry. |
managed_tmp.pressure.low_free_space_min_age_seconds |
int | 3600 |
0 |
Emergency minimum age used instead when the free-space floor is breached, if lower than base. |
managed_tmp.pressure.min_entry_bytes |
int | 67108864 |
0 |
Small entries below this size do not participate in pressure pruning. |
managed_tmp.agent_cargo_incremental |
bool | false |
Incremental Cargo check/clippy for launched agents. Only hosts with the splitting rustc wrapper (athena) should set this true. |
Source: src/sase/default_config.yml, src/sase/config/_settings_system.py,
src/sase/core/managed_tmp_reaper.py
markdown¶
The column width SASE wraps generated Markdown prose at. It governs every Markdown
surface SASE writes or renders itself: plan files, bead notes and pages, memory shims
and the generated AGENTS.md/provider instruction files, generated skills, prompt
archives, SDD documents, and the default --wrap for sase bead show and
sase plan show. The value is resolved on each call, so an edit takes effect on the
next command without restarting anything.
Values below the minimum, of the wrong type, or in a malformed config fall back to the
shipped default rather than raising: a broken sase.yml must never turn
sase plan propose into a traceback.
markdown:
print_width: 88
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
markdown.print_width |
int | 88 |
20 |
Column width SASE wraps generated Markdown prose at. |
The minimum of 20 is the floor below which SASE's display wrapper stops wrapping
entirely, so a smaller value would silently do nothing.
Sharp edge: the prettier CLI cannot read this field. prettier (via just fmt-md,
CI, or an editor integration) discovers its configuration from files on disk — a repo's
package.json, .prettierrc, and friends — and those declarations mirror SASE's
shipped default, not your effective configured value, so that a stock checkout is
self-consistent for a contributor with no SASE config at all. If you configure a
non-default markdown.print_width and then run sase init inside a repo whose prettier
config still declares the default, the regenerated AGENTS.md will be wrapped at your
width and that repo's fmt-md-check will fail on it. Change the repo's prettier config
to match, or leave the field at its default.
pager¶
File-aware syntax highlighting for the SASE pager. Eligible raw sources — files, Markdown documents, diffs, and stdin with a convincing diff prefix — receive a muted language-aware overlay after first paint. Formatted cards, bead detail, commit manifests, and producer-styled ANSI stay unhighlighted.
pager:
syntax: auto
| Field | Type | Default | Description |
|---|---|---|---|
pager.syntax |
auto or never |
auto |
auto detects language from filename, trusted Markdown/diff, shebang, or stdin diff. never keeps unhighlighted rendering. |
CLI --syntax overrides this field. --syntax none and --color never disable the
added layer for the rest of the session, including followed targets. Unknown values fail
schema validation (sase config / doctor diagnostics) and the runtime accessor falls
back to auto so a hand-edited sase.yml cannot crash the pager.
timezone¶
The timezone that governs all SASE wall-clock display and timestamp generation
(notifications, agent logs, artifact/agent-name timestamps, runtime durations, TUI
displays, CLI tables, and generated Markdown pages). Columns or labels that previously
carried a literal UTC suffix now render the configured zone abbreviation. When unset,
SASE uses the host system timezone, so machines that don't share our timezone
assumptions get sensible behavior out of the box.
timezone: "America/New_York" # default: system timezone
| Field | Type | Default | Description |
|---|---|---|---|
timezone |
string | system timezone | IANA timezone name governing all SASE wall-clock display and timestamp generation. |
chat_install¶
Configuration for chat-driven update workflows. External chat integrations can call
sase.integrations.chat_install.start_chat_install_worker() to run the built-in
sase update --json engine in a detached worker. The worker uses the same
managed-vs-dev routing as the TUI Updates tab and the sase update CLI, so no custom
update command is required.
chat_install:
timeout_seconds: 900
restart_attempts: 3
| Field | Type | Default | Description |
|---|---|---|---|
chat_install.timeout_seconds |
int | 900 |
Maximum runtime for sase update --json before returning exit code 124. |
chat_install.restart_attempts |
int | 3 |
Number of scheduler readiness polls after requesting its service proc start when the scheduler is not running after the update. |
Only one chat update worker may run at a time; a lock under
~/.sase/chat_install/install.lock rejects concurrent starts. Worker output is written
to timestamped logs under ~/.sase/chat_install/logs/. The configuration key and state
paths remain named chat_install for compatibility. The old chat_install.command and
chat_install.sync_workspace keys have been removed; delete them from user config if
schema validation reports them. See
docs/integrations.md for the integration-facing
Python API.
Source: src/sase/default_config.yml, src/sase/integrations/chat_install.py
telegram¶
Custom Telegram slash commands are keyed by the bot command name. Define them in user
configuration or an overlay; the Telegram integration deliberately ignores project-local
configuration so a repository cannot add commands to your bot. Core SASE validates the
definitions, and sase doctor checks that each command's executable resolves.
telegram:
commands:
projects:
description: List enabled SASE projects.
run: sase project list --state enabled
output: message
timeout: 60s
| Field | Type | Default | Description |
|---|---|---|---|
telegram.commands.<name>.description |
string | required | Slash-menu description, from 1 to 256 characters. |
telegram.commands.<name>.run |
string | required | Executable plus fixed arguments, parsed as an argument vector without a shell. |
telegram.commands.<name>.output |
string | message |
Deliver Markdown stdout as a message or rendered pdf. |
telegram.commands.<name>.timeout |
string | 60s |
Integer duration ending in s, m, or h. |
Command names must contain 1–32 lowercase letters, digits, or underscores. The built-in
names bead, beads, changes, fork, kill, list, update, and macros are
reserved. The integration parses run as an argument vector and never invokes a shell.
Text following /name is appended as one final argument, and the process runs from an
isolated temporary directory, so use absolute paths or commands available on PATH
rather than relying on a project working directory.
Run sase doctor -C integrations.telegram_commands after editing the map; unresolved
command heads produce a warning with the affected names.
Source: src/sase/default_config.yml, src/sase/doctor/checks_integrations.py
tmux_agent¶
Configuration for tmux Agent: Launch Control's t binding and
sase tmux-agent. Both surfaces share this block. A bad value is dropped with a warning
rather than making the tmux key binding fail.
tmux_agent:
# Base tmux window name. The first window is this name; later ones get a
# numeric suffix (ai, ai2, ai3, ...).
window_name: "ai"
# Pass each agent CLI's approval-bypass flags (see the provider's
# llm_interactive_cli descriptor). Per-provider overrides win.
bypass_permissions: true
# Reasoning effort applied to launches. "" follows llm_provider.default_effort;
# "off" passes no effort flags at all.
effort: ""
# Run `clear` in the new window before starting the CLI.
clear_screen: true
# Optional shell command run after an agent CLI window closes, alongside
# SASE's own window renumbering. Empty means nothing extra runs.
after_close_command: ""
# Per-provider overrides, keyed by registered provider name.
providers:
claude:
enabled: true # false hides the provider from both surfaces
key: "" # override the single-key menu shortcut
model: "" # pin a model, e.g. "gemini-3.7-flash-high"
effort: "" # per-provider effort; "" inherits, "off" disables
args: [] # extra CLI args appended verbatim
env: {} # environment variables for the new tmux window
bypass_permissions: true # omit to inherit the global default
| Field | Type | Default | Description |
|---|---|---|---|
tmux_agent.window_name |
string | "ai" |
Base tmux window name. First window is this name; later ones get a numeric suffix (ai2, ai3). |
tmux_agent.bypass_permissions |
bool | true |
Pass each agent CLI's approval-bypass flags. The resolved command always shows whether bypass is on. |
tmux_agent.effort |
string | "" |
Effort applied to launches. "" follows llm_provider.default_effort; "off" passes no effort flags. |
tmux_agent.clear_screen |
bool | true |
Run clear in the new window before starting the CLI. |
tmux_agent.after_close_command |
string | "" |
Extra shell command run after an agent CLI window closes, alongside SASE's own window renumbering. |
tmux_agent.providers.<name>.enabled |
bool | true |
false hides the provider from both the tmux menu and sase's TUI panel. |
tmux_agent.providers.<name>.key |
string | "" |
Override the single-key menu shortcut. Must be exactly one printable non-whitespace character. |
tmux_agent.providers.<name>.model |
string | "" |
Pin a model (substituted into the provider's model_args). |
tmux_agent.providers.<name>.effort |
string | "" |
Per-provider effort; "" inherits the global tmux_agent.effort; "off" disables effort flags. |
tmux_agent.providers.<name>.args |
list | [] |
Extra CLI args appended verbatim after the resolved launch argv. |
tmux_agent.providers.<name>.env |
mapping | {} |
Environment variables for the new tmux window; user values win over the provider descriptor. |
tmux_agent.providers.<name>.bypass_permissions |
bool | inherit | Omit to inherit tmux_agent.bypass_permissions. false launches without bypass args. |
effort accepts "", "off", "none", "minimal", "low", "medium", "high",
"xhigh", and "max". A config-default effort is best-effort: a provider that cannot
honor the level launches without the flag rather than failing. An explicit
sase tmux-agent -e <level> that the provider cannot honor is a usage error.
Three per-provider entries are load-bearing if you want the resolved argv to match the shell script this feature replaces:
tmux_agent:
effort: "max"
providers:
claude: { env: { EDITOR: nvim } }
codex: { effort: "xhigh" }
grok: { effort: "xhigh" }
opencode: { effort: "off" }
agy: { model: "gemini-3.7-flash-high" }
qwen: { model: "qwen3.6-plus" }
muse: { model: "muse-spark-1.3" }
codexandgrokcap out atxhigh. A config-default effort is best-effort, soeffort_cli_argslogs and skipsmaxrather than downgrading it — without the per-providerxhighthey would launch with no effort flag at all, unlike the script.opencodeaccepts every level as--variant <level>, so a globalmaxwould add a flag the script never passes;effort: "off"keeps it bare.agyandqwenneed no effort entry: both declare an empty supported-effort map, so the globalmaxis skipped for them automatically and their argv already matches. Theirmodelpins reproduce the script's hardcoded models.
EDITOR=nvim is a personal preference, not a provider requirement, which is why it
lives on tmux_agent.providers.claude.env rather than in the Claude plugin.
Source: src/sase/default_config.yml, src/sase/config/tmux_agent.py
mobile_gateway¶
Configuration for sase mobile gateway start, which launches the workstation-hosted
Rust gateway for paired mobile clients.
mobile_gateway:
bind_address: "127.0.0.1"
port: 7629
state_dir: ""
allow_non_loopback: false
command: ""
agent_bridge_command: ""
helper_bridge_command: ""
push_provider: "disabled"
fcm_project_id: ""
fcm_service_account_json: ""
fcm_credential_env: ""
fcm_dry_run: false
push_timeout_seconds: 5
push_retry_limit: 1
startup_timeout_seconds: 10
| Field | Type | Default | Description |
|---|---|---|---|
mobile_gateway.bind_address |
string | "127.0.0.1" |
Host address to bind. Non-loopback values require explicit opt-in. |
mobile_gateway.port |
int | 7629 |
Gateway HTTP port. |
mobile_gateway.state_dir |
string | "" |
SASE state root for gateway storage. Empty uses the Rust gateway default. |
mobile_gateway.allow_non_loopback |
bool | false |
Allow LAN or tailnet binds after explicit user opt-in. |
mobile_gateway.command |
string | "" |
Gateway binary command override, parsed without a shell. |
mobile_gateway.agent_bridge_command |
string | "" |
Agent bridge command override passed to the gateway; empty uses its default. |
mobile_gateway.helper_bridge_command |
string | "" |
Helper bridge command override passed to the gateway; empty uses its default. |
mobile_gateway.push_provider |
string | "disabled" |
Push provider: disabled, test, or fcm. |
mobile_gateway.fcm_project_id |
string | "" |
Firebase project ID for FCM HTTP v1. |
mobile_gateway.fcm_service_account_json |
string | "" |
Local service-account JSON path. Do not commit this file. |
mobile_gateway.fcm_credential_env |
string | "" |
Env var containing an FCM bearer token or service-account JSON. |
mobile_gateway.fcm_dry_run |
bool | false |
Ask FCM to validate messages without delivering them. |
mobile_gateway.push_timeout_seconds |
float | 5 |
Timeout per push provider HTTP attempt. |
mobile_gateway.push_retry_limit |
int | 1 |
Retry attempts for best-effort push delivery. |
mobile_gateway.startup_timeout_seconds |
float | 10 |
Seconds to wait for gateway readiness before exiting. |
Push payloads are hint-only and must not contain bearer tokens, pairing codes, prompt
bodies, response text, attachment contents, attachment tokens, or host paths. Only
credential paths or environment-variable names are placed on the gateway command line.
See docs/mobile_gateway.md for setup examples and
security notes.
Source: src/sase/default_config.yml, src/sase/integrations/mobile_gateway.py
sdd¶
Configuration for spec-driven development features, including prompt, tale, epic, research, and bead storage.
sdd:
bead_refresh:
mode: background
ttl_seconds: 120
repo:
name: "" # provider-specific sidecar repo override
push_after_commit: async
| Field | Type | Default | Description |
|---|---|---|---|
sdd.bead_refresh.mode |
string | background |
Sidecar bead-store freshness: background launches a TTL-gated managed sync after commands, blocking pulls before commands, and off disables remote refresh, including the live-bead-waiter hint the sidecar_auto_sync job marks and the equivalent hint in the runner's bead-wait fallback. Local dependency rechecks continue. |
sdd.bead_refresh.ttl_seconds |
float | 120 |
Minimum age of the last successful remote integration before another background worker is launched. |
sdd.repo.name |
string | "" |
Optional sidecar repo override for providers that support separate_repo; accepts name or owner/name. For GitHub, empty checks only <owner>/<repo>--sdd; set sdd.repo.name to use another repo such as sdd or owner/sdd. |
sdd.push_after_commit |
bool or str | async |
Controls git push after SDD commits in sidecar repositories: async, true, or false. Local commits are preserved. |
The workspace provider owns storage selection. Built-in bare-git projects store SDD
under sdd/. Managed GitHub projects use a --plans sidecar cloned at
sase/repos/plans; every configured document role resolves at sase/repos/<role>. The
default-seeded research role derives <owner>/<project>--research. Current managed
initialization also records a --beads sidecar at sase/repos/beads. Unmigrated GitHub
projects retain their provider-backed .sase/sdd/ clone. Materialized layouts record
metadata in the primary workspace's .sase/sdd-store.json. Providerless projects fall
back to a primary-workspace .sase/sdd/ store. The retired sdd.storage and
sdd.version_controlled keys are ignored, stripped before validation, and reported by
sase doctor for cleanup. See SDD Storage and Beads.
The default current layout has a schema-version 3 sidecar_repos record: every recorded
role resolves to its role-specific clone, and bead state lives at the root of --beads.
A record without a beads role remains schema version 2 and resolves bead state to
beads/ in --plans. Ordinary resolution preserves that compatibility shape. Running
managed sase repo init with the beads role enabled is the adoption step: it prepares
the dedicated sidecar and writes a schema-version 3 record. A project that disables or
otherwise omits the beads role stays on schema version 2. Initialization prepares
configured sidecars in its current workspace and re-records stale compatibility metadata
with the derived repository. Later workspaces clone lazy document roles on demand. The
legacy single-sidecar shape continues to resolve byte-for-byte as before.
Built-in bare-git projects also auto-create or refresh generated SDD guide files during
first-use #git:<project> initialization (target existing projects with their
+<project> project tag), existing bare-repo registration,
#git/workspace materialization, and the first in-tree SDD write. Setup/materialization
flows commit and push only those generated init paths with an Initialize SDD init
commit when needed.
For a repository whose own sase/sase.yml sets is_sase_managed: true, running
sase repo init or its sase init repo alias writes managed entries for plans, beads,
research, and agents, initializes configured sidecars, then refreshes generated guides
and the directory map. On GitHub it derives the remotes as <owner>/<repo>--<role> for
those four roles while honoring optional explicit repo pins. It initializes and pushes
every enabled entry, then maintains the split store record; the agents role is not part
of the SDD store record. Existing legacy --sdd files remain untouched locally and in
their remote, while normal SDD routing uses the configured sidecars. --check previews
provider and generated-file work without writing. Missing or false management markers
make both forms successful no-ops; invalid local marker configuration fails before
provider calls or writes.
Explicit initialization first performs authoritative provider discovery for every
enabled sidecar. Each missing GitHub repository triggers a separate prompt naming its
role and resolved repository; only y or yes authorizes that invocation to create it.
The prompts are default-no and unavailable on non-interactive stdin. Bare
sase init --yes cannot authorize repository creation: it reports a missing remote and
defers creation to an interactive sase repo init without failing automated onboarding.
--check remains network-free.
Source: src/sase/default_config.yml
bead¶
Configuration for the bead issue tracker.
bead:
big_epic_phase_threshold: 5 # minimum authored phase count for llm_provider.big_epic_lander_model
show:
images: auto # image previews for sase bead show: auto, cells, kitty, or never
task_triage:
min_plus_ones: 1 # +1 reports a ready untyped/undeclared task needs before it earns a TaskTriage gate
stale_after_days: 7 # age at which a still-sub-threshold ready task bead is stale
stale_cleanup_min_beads: 10 # stale beads required before bead_stale_cleanup gates
task_types: [] # optional catalog overrides and project-local types
push_after_commit: true # compatibility field; current bead-work launches do not consult it
| Field | Type | Default | Description |
| ---------------------------------------------- | ----------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- |
| bead.big_epic_phase_threshold | int | 5 | Minimum total authored phase count that selects llm_provider.big_epic_lander_model for an epic without an explicit land model. Must be at least 1; malformed runtime values defensively fall back to 5. |
| bead.attachments.sensitive_patterns | list[str] | [] | Extra glob patterns treated as sensitive by the bead note attachment authoring service, additive with the built-in policy. A note that references a matching path fails unless -S/--allow-sensitive is passed. Malformed values fail open to []. |
| bead.attachments.git_max_bytes | int | 52428800 | Largest single attachment the git tier accepts, in bytes (50 MiB). Missing, malformed, or above 95 MiB fails open to the default. |
| bead.attachments.public_max_bytes | int | 26214400 | Largest attachment the public tier accepts, in bytes (25 MiB). Anything larger stays on the private tiers. Missing, malformed, or above 95 MiB fails open to the default. |
| bead.attachments.require_upload | bool | false | When true, attachment bytes upload before the bead event is written; any upload failure aborts with nothing written and nothing queued. Malformed values fail open to false. |
| bead.attachments.auto_fetch_max_bytes | int | 26214400 | Largest single attachment fetched automatically by read/show, in bytes (25 MiB). Missing or malformed values fail open to the default. -d/--download lifts the cap for one invocation; attachment path always fetches. |
| bead.attachments.background_upload_min_bytes | int | 67108864 | Objects at or above this size upload in the background (64 MiB) through a detached drain worker. Missing or malformed values fail open to the default. require_upload: true stays synchronous instead. |
| bead.attachments.large_store | object/null | null | Optional rclone large-object tier for objects above git_max_bytes: {remote: "<rclone remote:path>", max_bytes: 2147483648} (default cap 2 GiB). Null disables the tier. Missing, malformed, or remote-less values fail open to null. | |
| bead.attachments.local_cache_max_bytes | int | 10737418240 | Local attachment cache budget in bytes (10 GiB). sase bead attachment prune evicts cached objects confirmed present in a shared store, oldest views first, to fit under this budget. Missing or malformed values fail open to the default. | |
| bead.show.images | string | auto | Image previews for sase bead show: auto, cells, kitty, or never. read, JSON, and piped show never draw. Malformed values fail open to auto. |
| bead.task_triage.min_plus_ones | int | 1 | Fallback +1 bar, applied only to untyped legacy beads and to types that declare no triage.min_plus_ones of their own. A typed bead uses its own spec bar instead, which is why this default does not describe most task beads: flake ships as 3, bug ships as 1, and ci, feature, and memory ship as 0. See Task Types for how a bead resolves its bar. Must be at least 0. Suppression withholds only the gate — a sub-threshold bead stays stored as ready, and a gate already raised for a bead that falls below the bar is canceled and its notification dismissed. |
| bead.task_types | list | [] | Project catalog entries. {use: <plugin>@<slug>, ...} deep-merges sibling keys onto an installed type; a full spec without use: defines a new slug and may not shadow a builtin. |
| bead.task_triage.stale_after_days | int | 7 | Days after creation at which a still-sub-threshold ready task bead is considered stale and eligible for the bead_stale_cleanup gate. Must be at least 1. |
| bead.task_triage.stale_cleanup_min_beads | int | 10 | Stale beads required across all enabled projects before bead_stale_cleanup raises its gate; below this count the job does nothing. Must be at least 1. |
| bead.push_after_commit | bool or str | true | Retained in the accepted configuration shape, but the current sase bead work path does not read it. Without --no-push, bead-ID launches synchronously run managed sync even for an in-tree Git store; a remote-backed detached store additionally requires an actual pre-spawn push. |
Below the threshold, an epic land agent uses llm_provider.epic_lander_model. At or
above it, it uses llm_provider.big_epic_lander_model instead. An explicit land model
remains authoritative over both.
See bead_task_triage for how the task_triage
fields gate TaskTriage notifications,
Task Triage Notification for the
post-upgrade dismissal of already-raised sub-threshold gates,
Discovered Follow-Up Capture and Triage
for the human-facing triage lifecycle.
In Launch Control (,m), the big epic starts at row shows this effective threshold
next to the two epic-lander rows. e or Enter opens a focused positive-integer editor,
and r previews an unset reset against bead.big_epic_phase_threshold in the writable
user-base config or its chezmoi source. There is no temporary override for this setting;
pressing o or x on the row only reports that Edit/Reset are available.
See docs/beads.md for the current pre-spawn
checkpoint and publication flow.
Source: src/sase/default_config.yml
external_mirror¶
Configuration for the external tracker mirror. See
External Issue Mirroring and the
external_mirror lane's
external_issue_mirror and external_pr_mirror jobs, the first production use of
for_each: {source: projects} fan-out.
One shared filter surface governs which tracker issues become beads
(external_mirror.issues.filters) and which remote pull requests become Patches
(external_mirror.pull_requests.filters). Each criterion is either a *_globs list
(accepting !-prefixed exclusions, matched like file_hooks's
path_globs) or a states enum list. A record matches a criterion if it matches any
positive glob (or the criterion has no positive globs) and matches no negative glob; a
record is mirrored only when every criterion accepts it, and matching is case-folded.
Setting a criterion replaces the shipped defaults for that criterion rather than
appending to them. Filters gate creation only: a record a filter now excludes keeps
whatever bead or Patch it already has, the mirror never deletes.
external_mirror:
issues:
filters:
author_globs: []
label_globs: []
title_globs: []
states: []
pull_requests:
filters:
author_globs: []
base_ref_globs: []
head_ref_globs:
- "!release-please--*"
- "!release-please/*"
- "!release-plz-*"
- "!release-plz/*"
title_globs: []
states: []
Pull requests ship with the four head_ref_globs exclusions above, so release-please
and release-plz PRs stop becoming Patches by default. Head ref is the key those defaults
filter on rather than author or title: release automation can author as a human account
on some repos, and PR titles are user-configurable, but the release branch name is
stable.
| Field | Type | Default | Description |
|---|---|---|---|
external_mirror.issues.filters.author_globs |
list of str | [] |
Issue author globs, including !-prefixed exclusions. |
external_mirror.issues.filters.label_globs |
list of str | [] |
Issue label globs, matched against every label on the issue. |
external_mirror.issues.filters.title_globs |
list of str | [] |
Issue title globs. |
external_mirror.issues.filters.states |
list of str | [] |
Issue states (open/closed) accepted for mirroring. |
external_mirror.pull_requests.filters.author_globs |
list of str | [] |
Remote PR author globs. |
external_mirror.pull_requests.filters.base_ref_globs |
list of str | [] |
Remote PR base-ref globs. |
external_mirror.pull_requests.filters.head_ref_globs |
list of str | ["!release-please--*", "!release-please/*", "!release-plz-*", "!release-plz/*"] |
Remote PR head-ref globs. |
external_mirror.pull_requests.filters.title_globs |
list of str | [] |
Remote PR title globs. |
external_mirror.pull_requests.filters.states |
list of str | [] |
Remote PR states (open/closed) accepted for adoption. |
external_mirror.exclude_labels and external_mirror.pr_authors are deprecated aliases
for external_mirror.issues.filters.label_globs (folded in as negated globs) and
external_mirror.pull_requests.filters.author_globs (folded in as plain globs),
respectively. The fold applies only when the modern criterion is empty — a non-empty
modern criterion always wins, and the legacy key's value is then ignored.
sase doctor -C config.external_mirror flags both a set legacy key and the "set
alongside a non-empty modern criterion" case.
Records a filter drops are visible rather than silently missing:
sase patch sync-external's table gets a Filtered column, sase bead sync-external's
per-project line gains filtered=<n> when non-zero, and the Patches pane's project
banners show a · M remote-only suffix when a filtered-PR count is known.
Source: src/sase/default_config.yml
feature_flags¶
feature_flags is the portable user and project override surface for code-owned SASE
feature flags. Registry defaults live in src/sase/feature_flags/registry.py; the
config block only overrides registered keys. It remains valid and lower-precedence than
a saved machine preference. The Config Flags pane and sase flag enable /
sase flag disable never edit this block, overlays, project-local sase.yml, or
chezmoi source.
feature_flags:
ref_sync_gesture: true
The generated JSON Schema exposes one boolean property per registered flag with its description and default. Unknown keys are tolerated by the schema so downgraded installs can still read a config written by a newer SASE, but the resolver warns and ignores unknown keys at runtime.
beta flags default off and gate work that is still landing; sunset flags default on
and keep a fallback path reachable until the flag is removed. The schema marks sunset
flags deprecated. The currently registered flags are the rows below, plus the sunset
flag for retired macro spellings. That flag defaults on. Its registry key uses the
retired spelling, so its behavior is documented in
the macro rename section instead of this table.
| Flag | Kind | Default | Controls |
|---|---|---|---|
ace_refresh_tokens |
sunset | true |
sase's TUI and proc refreshes are gated on per-surface, stat-only change tokens. |
admin_center_flags |
sunset | true |
The Admin Center Config catalog shows the Flags pane. |
agent_sudo_requests |
beta | false |
The typed sudo request workflow (sase sudo) and its review modal. |
agents_session_manifest_compat |
sunset | true |
When an owner manifest still lists files, accept the current set or the legacy set that drops sessions/ pages. See Strict v2 layout. |
agents_unified_query |
sunset | true |
The Agents tab filter uses the shared agents-live boolean query profile. |
autonomy_record_only |
sunset | true |
Only agent_meta.autonomy carries %auto state; off also writes the legacy meta keys. |
axe_routine_job_contract |
sunset | true |
AXE configuration projections and public JSON use routine/job names; see axe. |
bgcmd_legacy_slots |
sunset | true |
Legacy ~/.sase/axe/bgcmd slot directories stay readable in the Services tab oneshot section. |
claude_helper_channel |
sunset | true |
Claude receives the packaged helper instructions and a tool guard that prevents native helpers from submitting root final declarations. See Root and helper agents. |
grok_rules_delivery |
sunset | true |
Grok root runs receive the SASE single-turn directive plus the managed project root AGENTS.md through --rules. See Grok Build Integration. |
instruction_shadow_render |
sunset | true |
Root provider invocations record an intended bundle and manifest and set SASE_INSTRUCTIONS_FILE for the call. Current providers do not read it, so existing delivery stays the same. See Instruction Bundles. |
legacy_agent_family_syntax |
sunset | true |
Retired agent-family spellings still alias agent-session spellings. Off rejects new uses; stored records still read. See Agent sessions. |
legacy_sase_shell_syntax |
sunset | true |
Retired sase-shell spellings still alias sase-turn spellings. Off rejects new uses; stored records still read. See Gate turns. |
monitor_continuation_records |
sunset | true |
New monitors persist versioned continuation records, frozen outcome policy, and durable delivery state. |
muse_synchronous_shell |
sunset | true |
muse exec runs with --enable-shell-tool, so Muse runs commands synchronously; see Muse Code Integration. |
provider_drain |
beta | false |
A hard provider disable relaunches stranded agents through sase agent drain (see llm_provider.usage_limit and Launch Control's automatic provider drain in ace.md). |
queue_capacity_budget |
sunset | true |
%queue(capacity=N) is the launch's own admission budget; see max_running_agents. |
ref_sync_gesture |
sunset | true |
Typing a second : after an empty @<kind>: refreshes that kind's sidecar and reopens the payload menu. |
refresh_panel |
sunset | true |
r on Agents and R elsewhere open the Refresh panel, and ,y opens it on Full history. |
slim_agents_manifest |
sunset | true |
Agents-sidecar owner manifests omit each hood's per-hood file list. |
strict_macro_input_types |
sunset | true |
Unknown macro input types are load errors with suggestions. Off keeps the legacy fallback to the single-line line type. See Typed Inputs. |
typed_launch_units |
beta | false |
Typed launch units, %if:: script admission, and %proc (see below). |
agent_decks and agent_tabs are no longer registered. The Agents tab always shows
data decks and cards and
agent tabs. A saved preference for a flag that is no longer
registered is removed the next time an installing process reconciles
feature_flags.json.
legacy_agent_family_syntax accepts, while it stays on, the retired family keyword on
%id as session=, the family and kind agent-query values as session: and
kind:session, the --next-fork family value, a gate "fork": "family", and
SASE_AGENT_FAMILY_ATTACH. legacy_sase_shell_syntax accepts
sase gate create --shell, --shell-status, --shell-stop-status, and the
--next-fork shell value; a gate spec's "shell" block, the "fork" key set to
"shell", and "continuation_mode": "gate_shell"; --shell on sase proc list or
sase proc run (replaced by --name); and the retired gate.shell
.reclaim_grace_seconds key. Supplying both names for the same option or block is an
error in either flag state. Write the session and turn spellings in new prompts and
config, and use --name for proc commands.
The sunset flag for retired macro spellings accepts, while it stays on, the aliases in
the macro rename section. Turning it off rejects
those spellings in a new command, config key, frontmatter key, environment variable, or
sase path target, and the error names the replacement. The retired entry-point group
is not loaded, a plugin's packaged copy of the retired definition directory is not read,
and those retired directories are invisible to expansion, workflow loading, completion,
catalogs, and save choices. Config keys, frontmatter keys, and the plugin-disable
environment variable are an error when both the retired name and the macro name are
present, in either flag state. The LSP command environment variable is the exception: a
non-empty macro value wins and the retired value is ignored. See that section for which
names those are. The region alias there stays accepted either way. State files and agent
artifact filenames already written under the old names stay readable either way, and new
writes use only the macro spelling. A default sase doctor run includes the config
check for those retired authored surfaces in either flag state. It reports non-empty
retired definition directories and does not report those state files, agent artifact
filenames, or that region alias.
agents_session_manifest_compat applies when an owner-manifest hood entry still carries
an explicit files list. Readers accept a missing list either way. On, a present list
may be the current canonical set or the legacy set, which is that current set with every
sessions/ path removed and the legacy redirect stubs retained. Off, only the current
canonical set matches. See Strict v2 layout.
Run sase flag list for the live registry with effective and saved state.
The registered typed_launch_units beta flag defaults to false. Enabling it exposes
the experimental %if:: script-admission and %proc parser, completion, and
launch-plan contract. Static %if(should_run=true|false) segment omission is not
feature-gated and works before typed launch planning in either flag state.
User-initiated sase's TUI and sase run submissions execute typed directives through
durable typed admission without a LaunchApproval gate. The frozen plan keeps the
complete %id and %clan identity binding, and keyed {@<id>} markers resolve once at
batch creation. Agent-initiated launches still freeze the typed plan for LaunchApproval;
after approval, the same admission coordinator resolves waits, evaluates %if::
predicates, and dispatches eligible units — agent units through the established agent
launch path, and %proc units through native stand-alone named-proc dispatch; see
Experimental typed launch units.
Saved machine preferences¶
Persistent enable/disable choices live in a SASE-owned machine-state file
$SASE_HOME/feature_flags.json (normally ~/.sase/feature_flags.json), not in
configuration. The file is a bounded JSON object of exact booleans keyed by registered
flag names; equal-to-default values are still stored because enable and disable are
durable user choices. Inspect the path with sase flag show <key> or the Flags pane
detail card (STATE).
{
"version": 1,
"flags": {
"admin_center_flags": true,
"ref_sync_gesture": false
}
}
Missing state is an empty snapshot (every flag uses its registry/config default). A malformed, oversized, or unsupported whole file is not silently deleted or overwritten: reads return no preferences plus a diagnostic, mutations fail with the path and a recovery-oriented message, and registered flags still resolve from lower layers.
Valid unknown keys in the machine-state file are tolerated by registry-neutral reads and writes, but installing process-wide feature flags for sase's TUI, AXE, or an agent runner reconciles that file against the running SASE registry and removes saved entries that are no longer registered. sase's TUI defers the disk transaction until the initially visible surface is ready, then shows one cleanup toast; AXE and agent runners report the same cleanup on stderr. Clean starts do not rewrite the file or emit cleanup prose. If another process already removed the stale keys, sase's TUI reports that the previously unregistered saved keys are already absent. If cleanup fails or the file becomes unusable, SASE preserves the file, keeps the diagnostic, and retries on the next startup.
This means downgrading or misspelling a saved flag no longer preserves that saved key
past the next installing process. A later upgrade falls back to registry/config defaults
until the choice is saved again. Portable YAML feature_flags entries keep the older
tolerance: unknown config keys are diagnosed and ignored, not rewritten.
Resolution order¶
Effective value is resolved in this order, later sources winning:
registry default
< user config
< overlay config
< saved machine preference (SAVED)
< explicit in-process/test override
< SASE_FEATURE_FLAGS transport / legacy env
< root CLI -f/--enable-feature or -F/--disable-feature
Plugin config layers never flip first-party flag defaults. A local-config entry for any
feature flag is ignored with a scope_violation warning: a feature flag cannot be set
from the local config layer, because sase's TUI disables project-local config and
flags must resolve consistently across frontends.
The Flags pane and sase flag list / show report effective state and saved
state separately. Provenance SAVED means the machine-state file won. If environment or
root CLI overrides still shadow the saved choice, the UI shows a “forced for this
process” warning rather than pretending the toggle already controls this process. Saving
still restarts sase's TUI and the service host; the saved value takes effect once that
higher source is removed.
Root-level -f/--enable-feature and -F/--disable-feature force a registered flag on
or off for one sase invocation. They must appear before the subcommand
(sase -f ref_sync_gesture run "..."). They outrank every config layer, a saved machine
preference, and an inherited SASE_FEATURE_FLAGS value, and they merge into
SASE_FEATURE_FLAGS so launched agents and other child processes inherit the same
overrides.
SASE_FEATURE_FLAGS is a strict JSON object of booleans, for example
{"ref_sync_gesture":false}. Malformed JSON, a non-object payload, or a non-boolean
value is a startup error for that process. SASE-launched children inherit a resolved
snapshot through the same variable, so sase flag list marks env provenance
prominently. CLI overrides are marked the same way (CLI:--enable-feature /
CLI:--disable-feature). After a successful save, SASE merges the chosen key into
SASE_FEATURE_FLAGS so an sase's TUI execv restart does not inherit the old pinned
snapshot above the new saved value.
Enable, disable, and restart¶
sase flag enable <flag>
sase flag disable <flag>
Both commands persist the choice in the machine-state file and, on success, restart the
scheduler service proc when it is already running. A stopped scheduler is left
stopped. They never start a proc the user had stopped, and they never signal a sase's
TUI session in another terminal — restart any separately running sase's TUI yourself.
Repeating an already-saved enable or disable is idempotent for the store but still
retries that scheduler restart. A restart failure does not roll back the saved
preference: rich and JSON output distinguish mutation from restart so a partial
success is safe to retry. Unknown flags are usage errors (exit 2); store or restart
failures use exit 1. --json emits one versioned document with separate mutation
and restart objects.
From Config > Flags, a confirmed toggle uses the same mutation path, then waits for
TUI-local tasks and installation changes and performs one controlled sase's TUI and
service host restart. Independent commands keep running. Disabling admin_center_flags
from its own row is supported: the pane disappears after restart, and
sase flag enable admin_center_flags restores it. The CLI commands are not gated by
that flag.
Create temporary flags with sase flag new <key> rather than editing the registry by
hand. The command creates a task bead of type flag, prints the registry entry, and
gives the both-states test checklist. Kinds are beta (default off) and sunset
(default on); the registry default is derived from the kind. See
Beads for the removal lifecycle.
Source: src/sase/feature_flags/registry.py, src/sase/feature_flags/schema.py,
src/sase/feature_flags/snapshot.py, src/sase/feature_flags/state.py
workspace¶
Controls how SASE chooses the physical location of managed workspace checkouts. See
docs/workspace.md for the directory-layout
reference and CLI workflows.
workspace:
root: xdg-state # "xdg-state", "adjacent", or an absolute path
project_key: "" # explicit project-key override; empty = derive from git remote / primary path
share_git_objects: true # borrow primary Git objects for numbered managed checkouts
cleanup_ttl_days: 14 # age threshold for `sase workspace cleanup --stale`
held_claim_ttl_days: 14 # age threshold for releasing pinned dead workspace claims
| Field | Type | Default | Description |
|---|---|---|---|
workspace.root |
string | "xdg-state" |
Root policy. "xdg-state" uses the platform state dir; "adjacent" keeps the legacy <primary>_<num>/ layout as an explicit opt-in; an absolute path is used as the managed-root base. SASE_WORKSPACE_ROOT overrides this base directory. |
workspace.project_key |
string | "" |
Override the per-project namespace under managed roots. Empty derives a stable key from a single git remote slug or the primary-path basename plus a short hash. |
workspace.share_git_objects |
bool | true |
Borrow the primary checkout's Git object database for numbered managed clones. Disable for standalone clones; sase workspace repair dissociates existing SASE-managed borrowers while preserving non-SASE alternate entries. |
workspace.cleanup_ttl_days |
int | 14 |
Minimum age (in days) of an unclaimed managed checkout before sase workspace cleanup --stale will remove it. |
workspace.held_claim_ttl_days |
int | 14 |
Minimum age (in days) of a pinned dead-PID workspace claim before the stale sweep releases it. Artifacts are kept so the failed run stays dismissible in sase's TUI; only the workspace hold is dropped. 0 disables age-based release. |
Platform defaults for the xdg-state policy:
| Platform | Managed root |
|---|---|
| Linux | $XDG_STATE_HOME/sase/workspaces (falls back to ~/.local/state/sase/workspaces) |
| macOS | ~/Library/Application Support/sase/workspaces |
| Windows | %LOCALAPPDATA%\sase\workspaces |
Numeric identity is the same on every root policy: #0 is the primary checkout,
#1–#9 are reserved, and managed claim workspaces start at #10. See
docs/workspace.md for the full identity model and
backup/container/NFS caveats.
For non-adjacent policies, physical checkouts live under
<managed-root>/<project_key>/<project>_<num>/. For example,
workspace.root: /mnt/sase-workspaces with project key github.com_org_repo places
workspace #10 at /mnt/sase-workspaces/github.com_org_repo/<project>_10/. When
SASE_WORKSPACE_ROOT is set, it supplies the same <managed-root> base for the
process.
Existing adjacent checkouts are not moved automatically by the default. Run
sase workspace migrate --to xdg-state to carry legacy <primary>_<num>/ directories
into the managed root, or set workspace.root: adjacent explicitly to keep the old
sibling layout.
sase repo open <primary-repo> -w NUM -r "<reason>" is an explicit preparation command
for a checkout you plan to use outside a normal sase run launch. It uses the same root
policy when it materializes the checkout, backs up uncommitted local changes through the
active VCS provider, cleans the checkout, checks out and syncs the provider default
parent revision, and prints the resulting path. For manual scratch work, choose a
claim-range number such as 10; #0 is the primary checkout and #1 through #9 are
reserved compatibility numbers.
Source: src/sase/default_config.yml, src/sase/workspace_provider/store.py
telemetry¶
Configures local telemetry recording and retention. See docs/telemetry.md for the full telemetry reference, including the CLI, metric catalog, local store, and Admin Center tab.
telemetry:
enabled: true
flush_interval_seconds: 15
retention:
raw_seconds: 172800
rollup_5m_seconds: 2592000
rollup_1h_seconds: 31536000
health_thresholds:
error_rate_warn: 10.0
error_rate_critical: 25.0
retry_rate_warn: 10.0
retry_rate_critical: 25.0
p95_latency_warn: 300.0
p95_latency_critical: 600.0
| Field | Type | Default | Description |
|---|---|---|---|
telemetry.enabled |
bool | true |
Enable or disable local telemetry recording. |
telemetry.flush_interval_seconds |
float | 15 |
Flush interval for long-lived processes. |
telemetry.retention.raw_seconds |
int | 172800 |
Retain raw samples for 48 hours. |
telemetry.retention.rollup_5m_seconds |
int | 2592000 |
Retain five-minute rollups for 30 days. |
telemetry.retention.rollup_1h_seconds |
int | 31536000 |
Retain hourly rollups for one year. |
telemetry.health_thresholds.error_rate_warn |
float | 10.0 |
Error rate % threshold for WARN health status. |
telemetry.health_thresholds.error_rate_critical |
float | 25.0 |
Error rate % threshold for CRITICAL status. |
telemetry.health_thresholds.retry_rate_warn |
float | 10.0 |
Retry rate % threshold for WARN health status. |
telemetry.health_thresholds.retry_rate_critical |
float | 25.0 |
Retry rate % threshold for CRITICAL status. |
telemetry.health_thresholds.p95_latency_warn |
float | 300.0 |
P95 latency threshold (seconds) for WARN status. |
telemetry.health_thresholds.p95_latency_critical |
float | 600.0 |
P95 latency threshold (seconds) for CRITICAL. |
The legacy telemetry.prometheus block (url, pushgateway_url, exposition_port) is
still schema-valid for compatibility but is ignored.
Source: src/sase/default_config.yml, src/sase/telemetry/_config.py
tool_runs¶
Operational retention for the machine-local ToolRun ledger. This block uses ordinary
config-layer precedence (builtin, plugin, user, overlay, then project). It is
independent of the project-owned tools catalog: changing retention cannot
change argv.
tool_runs:
summary_days: 180
detail_days: 60
log_days: 14
log_max_bytes: 2147483648
run_log_max_bytes: 268435456
event_max_bytes: 16777216
soft_ceiling:
default: ""
providers: {}
| Field | Type | Default | Minimum | Description |
|---|---|---|---|---|
tool_runs.summary_days |
int | 180 |
1 |
Days to retain run/attempt/lifecycle summaries after settlement. |
tool_runs.detail_days |
int | 60 |
1 |
Days to retain stage/sample events and projections. Must be <= summary_days. |
tool_runs.log_days |
int | 14 |
1 |
Days to retain output and event files after settlement. Must be <= detail_days. |
tool_runs.log_max_bytes |
int | 2147483648 |
1 |
Aggregate retained-log target in bytes (2 GiB). |
tool_runs.run_log_max_bytes |
int | 268435456 |
1 |
Per-run retained-log cap in bytes (256 MiB). |
tool_runs.event_max_bytes |
int | 16777216 |
1 |
Per-run event-file cap in bytes (16 MiB). |
tool_runs.soft_ceiling.default |
string | "" |
— | Default soft-ceiling duration (90s, 20m, 1h); empty means none. |
tool_runs.soft_ceiling.providers.<name> |
string | "" |
— | Per-provider soft-ceiling duration keyed by registered provider name; empty means none. |
tool_runs.soft_ceiling is the most time an agent should block on one sase tool run
before escalating to a monitor; it kills nothing; the effective budget is the smaller of
this and the hard ceiling's budget. The provider entry wins, then default, then none.
A malformed or non-positive value is ignored with one warning and never fails a launch.
Unknown fields and non-positive or inconsistent horizons are rejected. JSON Schema
cannot express detail_days <= summary_days; runtime validation covers that.
Source: src/sase/default_config.yml, src/sase/config/sase.schema.json,
src/sase/config/tools.py
tools¶
Named tool catalogs are project-owned complete entries in sase/sase.yml. SASE does not
read argv from the recursively merged config: tools: in builtin, plugin, user, or
machine layers is diagnosed and ignored so list concatenation and deep merge cannot
change execution identity. Malformed project YAML or catalog fields are actionable
errors, not an empty catalog.
tools:
check:
argv: [just, check]
description: Run the repository scoped check.
stages: run_silent
inputs: [Justfile]
env: [SASE_PYTEST_WORKERS]
args: deny
fingerprint:
toolchain:
python: [python, --version]
| Field | Type | Default | Description |
|---|---|---|---|
tools.<name>.argv |
string array | required | Exact argv run at the project root. Not expanded for shell syntax or environment variables. |
tools.<name>.description |
string | "" |
Human-readable purpose shown by sase tool list. |
tools.<name>.stages |
run_silent | none |
none |
Whether run_silent stage producers attach to this tool. |
tools.<name>.inputs |
string array | [] |
Repository-relative glob patterns observed as fingerprint inputs. |
tools.<name>.env |
string array | [] |
Allow-listed environment variable names observed as fingerprint inputs. |
tools.<name>.args |
allow | deny |
deny |
Whether extra arguments after -- may be appended to argv. |
tools.<name>.duration_class |
short | long | unbounded |
short (when unset) |
Duration-class floor for inline-vs-monitor routing. Validated by the Rust core and excluded from the definition digest. |
tools.<name>.fingerprint.repos |
string array | [] |
Configured repository identities; empty means the current repository. |
tools.<name>.fingerprint.toolchain |
map of argv arrays | {} |
Bounded toolchain probes. |
sase tool list reports LAST (newest native result for this project and definition) and
TYPICAL (median duration of up to 30 normally exited native runs in 30 days). Missing
samples render as an em dash, never as zero or an ETA. It also reports CLASS (the
effective duration class, short when unset) and calibrates each declaration against
the corpus (see Duration classes).
Source: src/sase/config/sase.schema.json, src/sase/config/tools.py,
src/sase/main/tool_handler.py
update¶
Configures install-mode switching (see Install mode switching).
update:
dev_root: "~/projects/github"
| Field | Type | Default | Description |
|---|---|---|---|
update.dev_root |
str | ~/projects/github |
Base directory for dev-mode editable checkouts, materialized owner-nested as <dev_root>/<owner>/<repo>. |
Source: src/sase/default_config.yml, src/sase/mode_switch/repos.py
Environment Variables¶
LLM Provider¶
| Variable | Description |
|---|---|
SASE_MODEL_TIER_OVERRIDE |
Force all LLM invocations to a specific tier (large or small). |
SASE_MODEL_SIZE_OVERRIDE |
Legacy alias for SASE_MODEL_TIER_OVERRIDE (big or little). |
SASE_LLM_EXEC_PROVIDER |
Execute through this registered provider while preserving requested model metadata. |
SASE_LLM_LARGE_ARGS |
Extra CLI args appended for large tier invocations (any provider). |
SASE_LLM_SMALL_ARGS |
Extra CLI args appended for small tier invocations (any provider). |
SASE_CLAUDE_LARGE_ARGS |
Claude-specific extra args for large tier (fallback if generic unset). |
SASE_CLAUDE_SMALL_ARGS |
Claude-specific extra args for small tier (fallback if generic unset). |
SASE_CLAUDE_MAX_WAIT_CONTINUATIONS |
Claude single-turn wait guard cap (default: 2). |
SASE_CODEX_PATH |
Path to the Codex CLI binary (default: PATH lookup, then NVM_BIN/codex). |
SASE_CODEX_LARGE_ARGS |
Codex-specific extra args for large tier (fallback if generic unset). |
SASE_CODEX_SMALL_ARGS |
Codex-specific extra args for small tier (fallback if generic unset). |
SASE_CODEX_DISABLE_SHADOW_HOME |
Set to 1 to launch Codex with the inherited CODEX_HOME. |
SASE_QWEN_PATH |
Path to the Qwen Code CLI binary (default: qwen). |
SASE_QWEN_LARGE_ARGS |
Qwen-specific extra args for large tier (fallback if generic unset). |
SASE_QWEN_SMALL_ARGS |
Qwen-specific extra args for small tier (fallback if generic unset). |
SASE_OPENCODE_PATH |
Path to the OpenCode CLI binary (default: opencode). |
SASE_OPENCODE_LARGE_ARGS |
OpenCode-specific extra args for large tier (fallback if generic unset). |
SASE_OPENCODE_SMALL_ARGS |
OpenCode-specific extra args for small tier (fallback if generic unset). |
SASE_AGY_PATH |
Path to the Antigravity CLI binary (default: agy). |
SASE_AGY_PRINT_TIMEOUT |
Override the agy --print-timeout Go duration (default: 24h). |
SASE_AGY_MAX_NO_PROGRESS_CONTINUATIONS |
Override the no-progress continuation cap (default: 2). |
SASE_AGY_LARGE_ARGS |
Antigravity-specific extra args for large tier (fallback if generic unset). |
SASE_AGY_SMALL_ARGS |
Antigravity-specific extra args for small tier (fallback if generic unset). |
SASE_MUSE_PATH |
Path to the Muse Code CLI binary (default: muse). |
SASE_MUSE_LARGE_ARGS |
Muse-specific extra args for large tier (fallback if generic unset). |
SASE_MUSE_SMALL_ARGS |
Muse-specific extra args for small tier (fallback if generic unset). |
SASE_MUSE_SANDBOX |
Set to on to keep Muse's sandbox with --sandbox-network enabled. |
SASE_GROK_PATH |
Path to the Grok Build CLI binary (default: grok). |
SASE_GROK_LARGE_ARGS |
Grok-specific extra args for large tier (fallback if generic unset). |
SASE_GROK_SMALL_ARGS |
Grok-specific extra args for small tier (fallback if generic unset). |
SASE_CONTINUATION_BUDGET_ENFORCE |
Set to 1 to apply continuation-budget preflight outside monitor successors. |
SASE_CONTINUATION_CONTEXT_LIMIT_BYTES |
Positive context-limit override for continuation preflight (default: 800000). |
SASE_CONTINUATION_TRANSPORT_LIMIT_BYTES |
Optional positive transport-limit override for continuation preflight. |
SASE_CONTINUATION_CHECKPOINT_THRESHOLD_BYTES |
Optional positive checkpoint-threshold override. |
SASE_CONTINUATION_INSTRUCTION_RESERVE_BYTES |
Positive instruction-reserve override (default: 0). |
SASE_CONTINUATION_TOOL_RESERVE_BYTES |
Positive tool-context reserve override (default: 0). |
SASE_CONTINUATION_OUTPUT_RESERVE_BYTES |
Positive output reserve override (default: 0). |
SASE_CONTINUATION_REASONING_RESERVE_BYTES |
Positive reasoning reserve override (default: 0). |
For the per-provider args, the generic SASE_LLM_*_ARGS variables are checked first. If
unset, the provider-specific variable is used as a fallback. Values are split on
whitespace and appended to the CLI command.
Monitor successors always run the continuation-budget preflight; the explicit enforce
variable is for another invocation path that needs the same guard. The preflight
measures the final expanded prompt after replay. When essential content exceeds the
effective budget, SASE records the decision and refuses before calling the provider.
Each SASE_CONTINUATION_*_BYTES variable outranks the matching
llm_provider.continuation_budget setting,
including provider and model overrides. Invalid, zero, or negative numeric overrides
fall back to the corresponding default or remain unset.
SASE-launched Codex subprocesses use a disposable shadow CODEX_HOME by default. The
shadow home is created under ~/.cache/sase/codex_home/, receives a copy of the real
config.toml, symlinks other Codex home entries back to the real home, and is removed
when the subprocess exits. If the real Codex home does not provide AGENTS.override.md
or AGENTS.md, SASE also links ~/AGENTS.md into the shadow as Codex's
$CODEX_HOME/AGENTS.md fallback. This prevents Codex runtime config rewrites from
dirtying the user-managed Codex config while preserving auth, hooks, skills, logs, and
caches.
Qwen Code uses
qwen --input-format text --output-format stream-json --yolo --model <model> and
expects users to configure Qwen auth through Qwen's supported settings path. Qwen OAuth
free tier access ended on 2026-04-15; use API keys, Alibaba Cloud Coding Plan,
OpenRouter, Fireworks, or another Qwen-supported provider.
OpenCode uses
opencode run --format json --dangerously-skip-permissions --model <provider/model> --dir <cwd> <prompt>
and expects users to configure OpenCode auth/settings through its normal XDG paths.
OpenCode model names usually include a provider prefix; use opencode models to list
models in your configured environment.
Muse Code uses
muse exec --json --workspace <cwd> --model <model> --trust-workspace --disable-approval --disable-sandbox --user-input-auto-resolve --no-foreign-personal-context --session-id <uuid> --prompt-file <tempfile>
and expects users to authenticate with muse login or META_API_KEY. SASE always sets
MUSE_NO_AUTO_UPDATE=1 for agent runs so Muse's launcher cannot replace its binary
mid-run; update Muse with sase agent-cli update muse instead. Muse's sandbox makes
.git read-only inside the workspace, which would break in-run commits, so SASE
disables it by default; SASE_MUSE_SANDBOX=on keeps the sandbox with
--sandbox-network enabled at the documented cost of in-run commits failing.
Muse's Contributor models carry a model advisory: Meta uses their inputs and outputs
to train and improve Meta's AI models (see the generated
Built-in Model Catalog for the current names). SASE
keeps them fully reachable by name and never points a provider tier map at either.
Whichever shipped size aliases currently include a Contributor member route there
automatically whenever a muse executable is available (see the generated
shipped size-alias defaults); override
llm_provider.model_aliases.builtin.<size> to opt out. The advisory renders in sase's
TUI model picker, in %model completion detail, and in the resolved model label, and
sase doctor -C llm.model_advisory warns when a configured default or model alias
resolves to any advisory-flagged model. See
LLM Providers — Model advisories.
Grok Build uses
grok --prompt-file /dev/stdin --output-format streaming-messages-json --permission-mode bypassPermissions --model <model> --cwd <cwd> --session-id <uuid> --no-plan --no-ask-user --no-auto-update --no-leader
and expects users to authenticate with grok login or XAI_API_KEY. grok is a
generic executable name shared with a stale community CLI (grok-dev) and Homebrew's
deprecated regex tool. Like Muse, Grok never participates in default-provider
autodetection. It is reached by explicit selection (see
llm_provider.provider above) or by the separate model-alias router:
whichever shipped size aliases currently target Grok can select it whenever a grok
executable is available (see the generated
shipped size-alias defaults). Routing checks executable
presence, while sase doctor performs the Grok Build identity probe. Grok models accept
only low/medium/high/xhigh for --effort; see
LLM Providers — Reasoning Effort.
VCS Provider¶
| Variable | Description |
|---|---|
SASE_VCS_PROVIDER |
Override VCS provider selection (git, hg, or auto). |
SASE_WORKSPACE_ROOT |
Override the workspace-root base for this process. Use an absolute path; WorkspaceStore appends <project_key>/<project>_<num>/ for managed checkouts. |
SASE_BUG_ID |
Bug ID for PR workflows. When set and non-zero, injects SASE_BUG=<id> into PR tags and Patch. |
SASE_BEAD_ID |
Assigned bead ID for commit workflows. When set, sase stitch create adds a linked SASE_BEAD= footer tag, leaves the subject unchanged, and requires an explicit -B/--bead-action keep\|close (see Explicit Bead Action). |
SASE_GH_TIMEOUT |
Positive per-attempt timeout, in seconds, for SASE's retried gh CLI calls (default: 20). |
SASE_GH_MAX_RETRY_SLEEP |
Positive cap, in seconds, on one wait between gh retries, including a server-requested retry delay (default: 60). |
SASE_LINKED_REPOS_JSON |
Resolved linked-repo metadata passed to launched agents. |
SASE_LINKED_REPO_<ENV_NAME>_DIR |
Workspace-matched directory for one configured linked repo. |
SDD Git Operations¶
| Variable | Description |
|---|---|
SASE_SDD_STORE_WRITE_LOCK_TIMEOUT |
Non-negative seconds to wait for the cooperative SDD store lock. Overrides both the 10-second metadata-write default and 180-second worktree-mutation default. |
SASE_SDD_GIT_LOCK_RETRY_DELAYS |
Comma-separated non-negative delays, in seconds, for transient Git lock failures. Invalid or empty values use the built-in shared retry schedule. |
SASE_SDD_GIT_LOCAL_TIMEOUT |
Positive seconds allowed for one local SDD Git command (default: 30). |
SASE_SDD_GIT_NETWORK_TIMEOUT |
Positive seconds allowed for one network SDD Git attempt (default: 120). For progress-reporting clone, fetch, and push, it is the no-progress stall limit. |
SASE_SDD_GIT_NETWORK_TRANSFER_CEILING |
Positive wall-clock cap, in seconds, on one progress-reporting clone, fetch, or push even while it keeps making progress (default: 900). |
SASE_SDD_GIT_SLOW_MS |
Positive duration, in milliseconds, at which a successful SDD Git command is logged as slow (default: 1000). |
SASE_SDD_REMOTE_CLONE_CONCURRENCY |
Positive number of SDD sidecar remote clones allowed to run at once on this machine (default: 1). |
SASE_EPIC_PLAN_LAUNCH_LOCK_TIMEOUT |
Positive seconds an epic plan launch waits for another launch in the same project (default: 900). |
SASE_EPIC_APPROVAL_PREFLIGHT_LOCK_TIMEOUT |
Positive seconds an approval preflight waits for an in-flight epic launch before deferring its health check to the detached launch (default: 120). |
See SDD storage concurrency and recovery for the lock, recovery snapshot, and failed-integration cooldown behavior.
Plugin System¶
These switches affect plugin-provided resources and declarative artifact providers. The VCS, workspace, and LLM registries load provider entry points directly.
| Variable | Description |
|---|---|
SASE_DISABLE_PLUGINS |
Disable plugin resources and third-party artifact-provider entry points. |
SASE_DISABLE_PLUGIN_MACROS |
Disable plugin-provided macro and workflow files. |
SASE_DISABLE_PLUGIN_CONFIG |
Disable plugin-provided default_config.yml files and config macros. |
SASE_DISABLE_PLUGIN_ARTIFACT_REFS |
Disable plugin-provided artifact-reference specifications. |
SASE_DISABLE_PLUGIN_FILE_HOOKS |
Disable plugin-provided file-hook templates. |
SASE_DISABLE_PLUGIN_TASK_TYPES |
Disable plugin-provided task-type specifications. |
State Root¶
| Variable | Description |
|---|---|
SASE_HOME |
Override the SASE state root. Defaults to ~/.sase; project files, chats, artifacts, notifications, dismissed bundles, saved groups, and logs move under this root. |
SASE_PROC_LOG_MAX_BYTES |
Maximum active proc-log segment size in bytes (default: 2 MiB); 0 disables rotation. |
SASE_CUTOVER_BACKUP_DIR |
Root the local state cutover kit writes backups, restores, and run journals under. Defaults to ~/cutover-backups; it must not be nested beneath ~/.sase, ~/.local/state/sase, or ~/sase, and users of other runtime roots must keep it separate themselves. |
General¶
| Variable | Description |
|---|---|
SASE_TMPDIR |
Override SASE's managed temp root. When unset, the root is $SASE_HOME/tmp (~/.sase/tmp by default). Keep it off tmpfs and out of file-sync folders, because build scratch can be large. |
SASE_DETACH_SCOPE_DISABLE |
Set to 1, true, yes, or on to stop detached agent runners, procs, monitors, and scheduler/hook/bead-sync workers from escaping SASE's systemd cgroup. By default on Linux, when the launcher runs inside a SASE-owned systemd unit or scope, or anywhere under a reachable user systemd manager, and systemd-run exists, SASE starts that work in its own transient systemd-run --user --scope with OOMPolicy=continue. |
SASE_AXE_DISABLE_SYSTEMD_SCOPE |
Legacy alias for SASE_DETACH_SCOPE_DISABLE: set to a truthy value to stop the same detached work from escaping SASE's systemd cgroup. |
SASE_AGENT_AUTO_APPROVE |
Launch-time auto-approve snapshot exported only while autonomy_record_only is off for a %auto agent; nothing in SASE reads it. SASE never consults it when deciding a gate: only the live agent_meta.json counts, so the A toggle takes effect at the next gate without updating this variable. |
SASE_FEATURE_FLAGS |
Strict JSON object of booleans carrying the resolved feature-flag snapshot for this process and its children. Outranks config layers and a saved machine preference. Root -f/--enable-feature and -F/--disable-feature merge into this variable so launched processes inherit those CLI overrides. |
SASE_MACRO_LSP_CMD |
Override the command used by sase lsp to launch the macro language server. |
SASE_CORE_DIR |
Preferred sase-core source checkout for Justfile Rust build/install targets; overrides ../sase-core. |
SASE_RUST_DEV_PROFILE |
Cargo profile for the just Rust dev-install recipes and editable sase-core update prebuilds (default: dev-update); set release to force the published release profile. See Rust Backend. |
SASE_FORMAT_VENV_DIR |
Virtualenv the just formatting recipes install their narrow formatter tool set into (default: .venv-format). |
SASE_PYTEST_DIST |
xdist scheduler for the just pytest recipes: worksteal (default) or loadfile (fallback). Invalid values fail before worker-token acquisition; serial inline-snapshot modes ignore it. |
SASE_PYTEST_SANDBOX_DIR |
Pytest-published sandbox root inherited by test subprocesses; bead-store writes during pytest must target a path at or below this directory. |
SASE_PYTEST_WORKERS |
Request exactly this positive number of governed xdist workers for the just pytest recipes. The request must fit the active host pool unless accounting is deliberately disabled. |
SASE_PYTEST_WORKER_FLOOR |
Positive minimum token grant required to start an automatically sized just pytest run. Defaults to 4, clamped on smaller hosts, and cannot exceed the ceiling or host pool. |
SASE_PYTEST_WORKER_CEILING |
Positive maximum token grant for an automatically sized just pytest run. Defaults to at most 28 while reserving another floor-sized grant when capacity permits. |
SASE_ALLOW_UNSANDBOXED_BEAD_WRITES |
Test-only override; set to 1 to allow a pytest bead-store write outside SASE_PYTEST_SANDBOX_DIR for a deliberate exception. |
SASE_ACE_PAGE_GROUP_ISOLATION |
Test-only override; set to 1 to make AcePageGroup create a fresh AcePage for each checkout instead of sharing one app across related tests. just test-ace-page-group-isolated sets this for every module in tests/ace/tui/ace_page_group_files.txt. |
SASE_TEST_GATE_SLOTS |
Override the host-wide pytest capacity in worker tokens. Unlike the former whole-suite gate, one token now represents one xdist worker. |
SASE_TEST_GATE_DIR |
Override the shared pytest token-pool directory. Defaults to a UID-scoped sase-pytest-tokens-<uid> directory under /tmp. |
SASE_TEST_GATE_TIMEOUT |
Non-negative seconds to wait for a sufficient worker-token grant before failing with requested capacity and current-holder diagnostics. |
SASE_TEST_GATE_STALE |
Non-negative seconds without a progress heartbeat before a live holder is treated as wedged and reclaimed. Default 1800 (30 minutes). 0 disables stale-heartbeat reclaim. |
SASE_TEST_GATE_MAX_HOLD |
Non-negative seconds a live holder may keep its grant even while heartbeats continue. Default 14400 (4 hours). 0 disables the absolute age cap. |
SASE_TEST_GATE_WATCHDOG |
Non-negative seconds between a holder's self-checks of those bounds. Default 30. 0 disables the holder-side watchdog; waiters still reclaim. |
SASE_TEST_GATE_DISABLED |
Set to 1 to bypass the pytest worker-token pool deliberately. The bypass takes no tokens and never waits, but its width is still clamped to the host budget and announced on stderr; raise SASE_TEST_GATE_SLOTS to run wider. Every held lease also exports it to prevent nested pytest deadlocks. |
SASE_TEST_GATE_GOVERNED |
Internal marker exported by every held worker-token lease, meaning an ancestor already paid for this process's workers. It is what separates a corroborated exemption from a top-level bypass; inherited pytest configuration must not lease again. |
SASE_TEST_SHARD |
<1-based index>/<1-based count> (e.g. 3/8). Runs exactly one deterministic, balanced slice of the whole fast suite instead of all of it; just pytest recipes only accept this in fast mode and reject an explicit test selector alongside it. Set by .github/workflows/master-gate.yml, not intended for manual use. |
SASE_JUST_INVOCATION_DIR |
Internal value set by just so test selectors are normalized from the caller's directory. |
The pytest variables above describe one UID-scoped pool shared by just recipes and
direct parallel pytest controllers. The first active lease records the effective
capacity; later launchers honor that capacity until every holder exits, even if
MemAvailable changes in the meantime. Automatic launchers require their floor
atomically and then take currently free tokens up to the ceiling. Exact
SASE_PYTEST_WORKERS requests wait for the complete request, and an explicit
SASE_TEST_GATE_SLOTS value must match an already-active pool. The former whole-suite
slot gate is fully superseded: admission, diagnostics, and SIGKILL-safe release are all
expressed in worker tokens. A live holder also writes a progress heartbeat (collection
and completed test calls). Waiters and a holder-side watchdog reclaim a grant whose
heartbeat is older than SASE_TEST_GATE_STALE or whose age exceeds
SASE_TEST_GATE_MAX_HOLD: the watchdog releases its own tokens, and a waiter SIGTERMs
(then SIGKILLs) a still-held wedged process so flock can return the tokens to the
pool. Waiting and timeout messages print each holder's age, heartbeat age, and reclaim
reason.
Workspace Management (Internal)¶
These are set automatically by sase when launching agent subprocesses and are not
intended for manual use. Workspace plugins declare an env-var prefix, then SASE passes
<PREFIX>_PRE_ALLOCATED, <PREFIX>_WORKSPACE_NUM, and <PREFIX>_WORKSPACE_DIR into
the child process. Built-in prefixes include SASE_GIT for #git; plugin packages may
add prefixes such as SASE_GH for GitHub. The launcher clears inherited
SASE_*_PRE_ALLOCATED, SASE_*_WORKSPACE_NUM, and SASE_*_WORKSPACE_DIR variables
before applying the current launch's values so follow-up agents cannot inherit stale
workspace claims.
| Variable | Description |
|---|---|
SASE_SYNC_CWD |
Working directory override for sync operations. |
<PREFIX>_PRE_ALLOCATED |
Set to "1" when a workspace provider has pre-allocated a launch context. |
<PREFIX>_WORKSPACE_NUM |
Pre-allocated workspace number. |
<PREFIX>_WORKSPACE_DIR |
Pre-allocated workspace directory path. |
SASE_GIT_*, ... |
Concrete forms for built-in and plugin-provided workspace prefixes. |
CLI Flags¶
Command groups that default to a nested list command still parse flags at the
subcommand level. Use the explicit list form when passing list options, such as
sase notify list -j, sase agent hold list -j, or sase workspace list --json. See
the CLI Reference for which groups delegate to list.
sase (global)¶
These options are recognized only in the leading run of option tokens, before the first
subcommand. They do not steal -f/-F or -p from commands such as
sase bead list -f json or sase completion candidates -p PROJECT.
| Flag | Values | Default | Description |
|---|---|---|---|
-F, --disable-feature |
registered flag key | - | Force a registered feature flag off for this invocation and every process it launches. Repeatable. Outranks config layers, a saved machine preference, and an inherited SASE_FEATURE_FLAGS value. |
-f, --enable-feature |
registered flag key | - | Force a registered feature flag on for this invocation and every process it launches. Repeatable. Outranks config layers, a saved machine preference, and an inherited SASE_FEATURE_FLAGS value. |
-p, --print-command |
flag | - | Print a shell-quoted sase ... header, prefixed with ❯ (or > when stderr cannot encode it), to stderr before running the command. The header keeps feature-flag overrides but omits the root print switch, and it is never printed during tab completion. |
sase tui¶
| Flag | Values | Default | Description |
|---|---|---|---|
[query] |
string | last used, first saved, or !!! |
Query string for filtering Patches. |
-m, --model-tier |
large, small |
- | Override model tier for all LLM invocations. |
-M, --model-size |
big, little |
- | Deprecated alias for --model-tier. |
-p, --profile |
optional path | - | Profile the TUI session with pyinstrument. Without a path, write ace-profiles/ace_profile_<timestamp>.txt under SASE's managed temp root; after exit, print a shortened path and copy it to the system clipboard when possible. |
-r, --refresh-interval |
int (seconds) | 10 |
Auto-refresh interval (0 to disable). |
-R, --restart-service, --restart-axe |
flag | - | Restart the service host on startup; no-op if it is not running. |
-s, --sanity-refresh-interval |
int (seconds) | 300 |
Full sanity-refresh interval; missed watcher or change-token updates are still reconciled at least this often. |
-t, --tab |
artifacts, changespecs, patches, agents, services, axe |
agents |
Tab to focus on startup. services and legacy axe select Services; changespecs and patches select Artifacts. |
-T, --tmux |
flag | - | Launch sase's TUI in a new tmux window named sase_tmux_<N> and print the session/window target for external control. |
-x, --no-service, --no-axe |
flag | - | Disable auto-starting the service host on startup. |
-v, --vcs-provider |
git, hg, auto |
- | Override VCS provider. |
sase screenshot¶
Capture a PNG from a real sase tui running in tmux. The command launches in the
detached sase_ace_agents session unless --window names an existing tmux target. See
Agent screenshots.
| Flag | Values | Default | Description |
|---|---|---|---|
-- TUI_ARGS |
strings | - | Arguments forwarded to sase tui after a -- separator. |
-d, --settle-ms |
int (ms) | 0 |
Extra delay before capture. |
-H, --host |
alias / SSH | local machine | Run the SVG capture on a remote machine, then rasterize locally. |
-k, --keep |
flag | - | Leave a newly launched tmux window running after capture. |
-o, --output |
path | managed temp path | PNG output path, or SVG output path when --svg is used. |
-p, --press |
tmux key | - | Send one tmux key. Repeats interleave with --type and -w in argv order. |
-s, --size |
COLSxROWS |
120x40 |
Geometry for a newly launched capture window. |
-S, --svg |
flag | - | Stop after live-app SVG export and skip PNG rasterization. |
-t, --timeout |
seconds | 60 |
Overall capture deadline. |
-T, --type |
text | - | Send literal TUI text (tmux send-keys -l). Repeats interleave with -p and -w in argv order. |
-w, --wait-for |
regex | - | Wait for captured tmux screen text to match a regex. Repeats interleave with -p and --type in argv order. |
-W, --window |
tmux target | launch a new window | Capture an existing sase_tmux_* window and never kill it after capture. |
sase tmux-agent¶
Launch an interactive agent CLI in a new tmux window. There are no subcommands — in
particular no list child — so a bare sase tmux-agent paints the tmux menu instead of
delegating. See tmux Agent.
| Flag | Values | Default | Description |
|---|---|---|---|
[provider] |
registered provider name | paint the menu | Launch this provider directly. Omit to paint the tmux Agent menu. |
-c, --dir |
path | current pane path, else $PWD |
Launch directory. |
-e, --effort |
off, none, minimal, low, medium, high, xhigh, max |
inherit from config | Explicit effort for this launch; unsupported levels are a usage error. |
-j, --json |
flag | - | Versioned JSON envelope of the catalog or the dry-run plan. |
-l, --list |
flag | - | Print the catalog as a table; works outside tmux. Not a subcommand. |
-n, --dry-run |
flag | - | Print the window name, directory, env, and exact command; change nothing. |
-r, --refresh |
flag | - | Rebuild the catalog cache before doing anything else. |
-s, --safe |
flag | - | Launch without the provider's approval-bypass args. |
-v, --verbose |
flag | - | With --list, add resolved paths, full commands, and install hints. |
--renumber is an internal hook invoked when an agent CLI window exits and is omitted
from help. Outside tmux with no --list/--dry-run/--json, the command exits 2,
explains that a tmux session is required, and still prints the catalog.
sase service¶
Bare sase service defaults to status, and bare sase service proc defaults to
proc list.
| Command | Flags / arguments | Description |
|---|---|---|
sase service status |
-j, --json |
Show host/native-unit state and every configured proc; exits 0 for running/starting, otherwise 1. |
sase service start |
-j, --json |
Start through the installed native unit when present, otherwise start a detached host. |
sase service stop |
-j, --json |
Stop the host. |
sase service restart |
-j, --json |
Stop and start the host. |
sase service run |
- | Run the host in the foreground until SIGINT/SIGTERM. |
sase service logs |
-n, --lines N |
Print the bounded host log (default 200 lines). |
sase service init |
-a/--allow-agent-env, -c/--check, -d/--diff, -f/--force, -y/--yes |
Plan, check, diff, or install/update the native user unit and captured environment; --yes refuses a SASE agent process unless -a is given. |
sase service uninstall |
-c/--check, -d/--diff, -f/--force, -y/--yes |
Plan, check, diff, or remove the native user unit. |
sase service proc list |
-j, --json |
List effective enablement, desired state, runtime state, and summary. |
sase service proc show NAME |
-j, --json |
Show source, launcher, effective enablement, state, and log path. |
sase service proc logs NAME |
-n, --lines N |
Print one proc's bounded output log (default 200 lines). |
sase service proc start NAME |
-n/--no-wait, -t/--timeout SECONDS |
Record a start request the host confirms; prints the pid. |
sase service proc stop NAME |
- | Stop the proc until the next host boot. |
sase service proc restart NAME |
-n/--no-wait, -t/--timeout SECONDS |
Record a restart request the host confirms; prints pid OLD -> pid NEW. |
sase service proc enable/disable NAME |
- | Persist a machine-local enabled or disabled override. |
sase service proc run -- COMMAND... |
-c/--cwd, -j/--json, -l/--label, -p/--project, -w/--workspace |
Submit a transient durable oneshot that is never added to daemon desired state. |
Without --yes, init and uninstall only print the plan and the apply command. Their
--check forms are read-only and return 1 for drift. --force allows a non-default
SASE_HOME and uses a home-scoped unit identity; uninstalling that unit likewise
requires --force. Platform lifecycle installation supports Linux systemd user units
and macOS LaunchAgents. See service configuration for entry fields and
layering.
sase axe¶
sase axe start|stop|restart|status is an alias of the matching sase scheduler
command. -v/--vcs-provider below is a sase axe group flag only; it applies to every
axe subcommand and has no scheduler spelling.
| Flag | Values | Default | Description |
|---|---|---|---|
-v, --vcs-provider |
git, hg, auto |
- | Override VCS provider. |
sase axe status¶
Shows the scheduler service proc's status through the service host. Human output is
the default; JSON output is deterministic and never contains Rich markup or ANSI
escapes. -j, --json emits the proc record. See
Scheduler Status.
| Flag | Values | Default | Description |
|---|---|---|---|
-j, --json |
flag | - | Emit the proc record as JSON. |
sase axe start¶
Asks the service host to start the scheduler service proc and waits for the host to
confirm. -n/--no-wait returns once the request is recorded; -t/--timeout SECONDS
bounds the wait. The -q, -H, -A, and -z overrides live on sase scheduler run
(and sase axe routine run), not on start.
sase axe stop¶
Asks the service host to stop the scheduler service proc and returns once the request
is recorded. It takes no flags.
sase axe restart¶
Asks the service host to stop and start the scheduler service proc and waits for the
host to confirm the new pid. -n/--no-wait returns once the request is recorded;
-t/--timeout SECONDS bounds the wait.
sase axe maintenance¶
Maintenance mode pauses scheduled routine ticks without stopping the orchestrator.
| Command | Flags / exit code | Description |
|---|---|---|
sase axe maintenance enter |
-r, --reason required |
Write the maintenance marker with a reason. |
sase axe maintenance exit |
exits 0 | Remove the marker if present. |
sase axe maintenance status |
exits 0 when active, 1 when inactive | Print the active marker reason, PID, timestamp. |
See axe.md — Maintenance Mode for the runtime behavior.
sase axe job¶
With no subcommand, sase axe job defaults to sase axe job list. Use the explicit
list or doctor subcommand when passing diagnostic flags.
| Form | Flags | Description |
|---|---|---|
sase axe job list |
-a/--available, -j/--json, -v/--verbose |
List configured jobs with one summary line each; --available also shows discoverable executable job scripts, and --verbose adds a full-description panel. |
sase axe job doctor |
-j/--json, -v/--verbose |
Diagnose missing configured jobs, unconfigured scripts, and Telegram job prerequisites. |
sase axe job run <JOB> |
-L/--routine, -n/--dry-run, -f/--force, -V/--job-verbose |
Run a single job once in the foreground. -L names the routine when the job appears in several, -n runs the script but only previews its agent proposals, -f bypasses declarative guards (triggers are always bypassed), and -V enables verbose script diagnostics and the full structured result. |
sase axe job doctor exits 1 when any check is ERROR (a configured script job
cannot be resolved) and 0 otherwise. Unconfigured available scripts and Telegram
prerequisite gaps report WARN. The same job diagnostics are also surfaced by
sase doctor -C axe.jobs. The hidden legacy sase axe chop ... group accepts the same
subcommands (plus the hidden --chop-verbose and --lumberjack spellings) and keeps
its schema-version-1 JSON; see Compatibility aliases.
sase axe routine¶
With no subcommand, sase axe routine defaults to sase axe routine list.
| Form | Flags | Description |
|---|---|---|
sase axe routine list |
-v/--verbose |
List configured routines and their jobs; --verbose adds each description body under a details block. |
sase axe routine run |
-q, -H, -A, -z |
Run one routine once in the foreground with optional query and runner-limit overrides. |
sase axe routine status |
- | Show per-routine process status. |
Both listings print only the description summary line by default so the output stays
scannable; -v/--verbose renders the full description.
sase axe routine run takes the routine name positionally; its runner flags are the
ones the orchestrator forwards when it spawns routines. The hidden legacy
sase axe lumberjack ... group accepts the same subcommands.
sase stitch create¶
Dispatches a commit, proposal, or PR via the VCS provider layer. See commit_workflows.md for the full flow, payload, checkpoint, and resume semantics.
| Flag | Values | Default | Description |
|---|---|---|---|
-m, --message |
string | - | Commit message (mutually exclusive with -M). |
-M, --message-file |
path | - | File containing the commit message / PR description (mutually exclusive with -m). |
-x, --exclude |
path (repeatable) | - | Repo-relative file or directory to leave out of the commit. |
--only-file |
path (repeatable, hidden) | stage all | Internal: restrict the commit to these repo-relative paths. Mutually exclusive with -x. |
-n, --name |
string | - | Branch/PR name (required for create_pull_request). |
-b, --bug-id |
int | $SASE_BUG_ID |
Bug ID to associate with the commit. |
-B, --bead-action |
keep / close |
- | Explicit assigned-bead action; required when committing with an assigned bead. |
-c, --checkout-target |
string | HEAD~1 |
Branch point for PR creation. |
-p, --parent |
Patch name | auto | Parent Patch name (overrides branch-based auto-detection). Unresolvable values are dropped. |
-r, --resume |
flag | - | Resume a previously-checkpointed commit after manual conflict resolution. |
-s, --status |
wip / draft / ready |
ignored | Accepted by the parser but currently not forwarded to the commit workflow, so it has no effect. Use $SASE_PR_STATUS instead. |
-t, --type |
commit / propose / pr … |
$SASE_COMMIT_METHOD |
Commit method — full names (create_commit, etc.) and short aliases are both accepted. |
sase stitch¶
sase stitch defaults to sase stitch list, which shows a merged timeline for the
primary repo and configured linked repos. Add -S/--sdd to include sidecar repository
history. The legacy sase vcs spelling is still accepted as a deprecated alias.
| Subcommand | Flags | Description |
|---|---|---|
list |
-a/--all, -A/--author, -b/--branch/--ref, -c/--color, -o/--current-only, -F/--fetch, -f/--format pretty\|full\|oneline\|json, -n/--limit, -m/--merges hide\|show\|only, -N/--no-fetch, -T/--no-tags, --origin stitch\|auto\|manual, -r/--repo, -R/--reverse, -S/--sdd, -s/--since/--after, -u/--until/--before |
Show a merged commit timeline with local/remote presence markers. |
sase stitch list date filters accept relative offsets (Nh, Nd, Nw), today,
yesterday, YYYY-MM-DD, or YYYY-MM-DDTHH:MM. Day-granular --until / --before
values include the full named day; relative and minute-precise values remain instant
bounds. See VCS Providers for output examples and
provider notes. --all spans every registered enabled or disabled project and
deduplicates shared physical checkouts. Internal sibling backing checkouts remain
visible as linked repositories of their owning projects. Global scope can be combined
with repeatable --repo filters but not --current-only. Add --sdd to either scope
before selecting SDD history with --repo sdd; without the opt-in, that repo filter
does not expand the eligible set. --all --sdd includes materialized separate SDD
repositories across registered projects. The --limit is the cap on the final merged
timeline (default 40; 0 means unlimited). --origin has no short form; repeat it to
OR stitch, auto, and manual origins.
sase patch search¶
| Flag | Values | Default | Description |
|---|---|---|---|
query |
string | (required) | Query string for filtering Patches. |
-f, --format |
plain, rich, markdown |
rich |
Output format (markdown for agent-friendly output). |
Search uses the normal enabled-project discovery scope. Disabled projects and internal
sibling backing records are omitted from this CLI path; run
sase project list --state all or sase project show <project> to inspect them, then
run sase project enable <project> before using normal search and launch surfaces for
new work.
sase patch migrate-extension¶
One-time cleanup for older installs: renames legacy ProjectSpec files under
~/.sase/projects from .gp to .sase, including archive siblings. Current readers
still accept .gp as a fallback, so migration is not required before using SASE; it
just normalizes on-disk filenames to the canonical extension.
If a .sase sibling already exists with identical contents, the redundant .gp copy is
removed. If the sibling differs, the command reports a conflict and preserves both files
unless --force is set.
| Flag | Values | Default | Description |
|---|---|---|---|
--force |
flag | - | Replace an existing differing .sase sibling with the legacy .gp file. |
--projects-dir |
path | ~/.sase/projects/ |
Override the project root scanned for legacy .gp files. |
sase project¶
With no subcommand, sase project defaults to sase project list. Project lifecycle
state is stored as PROJECT_STATE metadata in the ProjectSpec header; missing state
means enabled.
| Form | Flags | Description |
|---|---|---|
sase project list |
-s, --state enabled\|disabled\|sibling\|all |
List records in one state; default is true enabled projects. |
sase project list |
-j, --json |
Emit machine-readable lifecycle and derived project/VCS fields. |
sase project current |
-j, --json |
Show the current project derived from the VCS macro MRU. |
sase project set-current <project> |
-j, --json |
Promote a project to the VCS macro MRU head without launching an agent. |
sase project show <project> |
-j, --json |
Show state, source, project/archive files, workspace, launchability, warnings. |
sase project set-state <project> <state> |
-f, --force |
Set enabled, disabled, or internal backing marker sibling. |
sase project enable <project> |
-f, --force |
Enable a project; --force has no effect when enabling. |
sase project disable <project> |
-f, --force |
Disable a project after live-work safety checks. |
sase project set-current promotes an enabled project to the VCS macro MRU head without
launching an agent. That is display and filter-seed context, not a lifecycle state; see
Current project.
Disabling refuses projects with live RUNNING claims or live artifact markers
(running.json, waiting.json, or pending_question.json) unless --force is passed.
Legacy active normalizes to enabled; inactive, archived, and closed normalize to
disabled. Deprecated activate, deactivate, archive, and close command aliases
remain accepted. The system-managed home project cannot be mutated. Normal launch and
discovery surfaces default to enabled projects. sibling remains an internal
backing-record marker for configured linked repos, not a third project state.
sase's TUI exposes the same lifecycle mutations through the Projects tab of the SASE
Admin Center (press #). That tab also supports marks for bulk lifecycle operations,
alias editing with A, ProjectSpec editing through $EDITOR, confirmed deletion of
whole SASE project directories, and the Repos/Workspaces inventory sub-tabs described
above.
sase repo¶
Bare sase repo defaults to sase repo list. The command family inventories primary,
sidecar, linked, and opened external repositories, prepares a selected repo inside one
workspace context, and exposes the durable audit history of successful opens.
sase repo list defaults to the current project and infers both the project and
workspace context from cwd. Primary repos come from ProjectSpecs, sidecars from
repos.sidecar plus SDD store records, linked repos from resolved repos.linked
(including compatibility aliases), and external repos from materialized workspace-local
clones; a sidecar wins when the same checkout is also auto-injected as linked. The Rich
table reports whether each repo is cloned in the selected workspace plus the number of
registered workspaces containing it. External rows use the canonical project name or
provider ref such as gh:pallets/click. Hidden agents rows remain visible and report
the same stable machine-level path for every registered workspace context.
| List flag | Description |
|---|---|
-a, --all |
Show all enabled and disabled projects at primary workspace context (#0). |
-j, --json |
Emit deterministic records with the full per-workspace clones matrix. |
-p, --project |
Select one enabled or disabled project instead of inferring from cwd. |
-w, --workspace |
Select a workspace number instead of inferring it from cwd. |
--all and --project are mutually exclusive. JSON records retain source, description,
auto_clone, environment, and SDD-storage metadata while making path and exists
describe the selected workspace context.
sase repo open REPO -r "<reason>" resolves REPO in three tiers: a host-project
inventory name, another registered SASE project name, then an external provider ref
(gh:owner/repo or owner/repo GitHub shorthand). It materializes and prepares the
repo, prints only its path to stdout, records the per-run artifact markers used by
sase's TUI and the commit finalizer, and appends an event to
~/.sase/projects/<project>/repo_opens.jsonl. Run it inside a managed checkout to infer
the host project and workspace. Reopening a valid external clone preserves its current
contents and records a new open event.
When a supported provider ref or another registered SASE project corresponds to a
configured linked repo in the host project, sase repo open opens the linked checkout
instead of cloning or reopening an external copy. Stdout remains exactly the linked
path; stderr explains the redirect and suggests the configured linked name for next
time. If a previous external checkout of the same repo exists, it is left untouched and
reported as a warning so any work already there remains visible.
| Open argument / flag | Description |
|---|---|
REPO |
Inventory name, registered project name, gh:owner/repo, owner/repo, or record path. |
-p, --project |
Select the host project instead of inferring it from cwd. |
-r, --reason |
Required non-empty audit reason. |
-w, --workspace |
Select the host workspace number instead of inferring it from cwd. |
When two inventory records share a name or slug, sase repo open refuses rather than
guessing and lists each candidate as <kind> '<name>' (<path>). To pick one, re-run the
command with that candidate's path as REPO, copied exactly as printed: a record path
is matched literally, ahead of any name or slug, so it selects that record even when its
name collides with another's. This is a disambiguator, not a general "open any
directory" mode — a path that matches no primary, sidecar, or linked record falls
through to the usual name and provider-ref tiers and fails there.
sase repo log renders a project-scoped summary and per-repo rollup of durable open
events. Repo, agent, or workspace filters add agent and event drill-down panels; an
event ID prefix shows one complete event. --json returns the same filtered data
deterministically.
| Log flag | Description |
|---|---|
-a, --agent |
Filter by agent name or interactive user. |
-i, --id |
Show one event by exact ID or unambiguous ID prefix. |
-j, --json |
Emit deterministic structured output. |
-p, --project |
Select the host project instead of inferring it from cwd. |
-r, --repo |
Filter by repository name. |
-w, --workspace |
Filter by host workspace number. |
sase revert¶
| Flag | Values | Default | Description |
|---|---|---|---|
name |
string | (required) | NAME of the Patch to revert. |
sase restore¶
| Flag | Values | Default | Description |
|---|---|---|---|
[name] |
string | - | NAME of the reverted Patch to restore. |
-l, --list |
flag | - | List all reverted Patches. |
If restoration reaches commit recreation, the current implementation calls
sase stitch create with an unsupported positional Patch name and fails after applying
the saved diff. See VCS restore for the resulting workspace state
and manual recovery command.
sase run¶
| Flag | Values | Default | Description |
|---|---|---|---|
[PROMPT] |
string | - | Prompt text, inline reference (#name), standalone workflow reference (#!name), or . for history picker. |
-Q, --request-path |
path | $SASE_PROC_REQUEST_PATH |
Private operation request sidecar used when sase's TUI runs the launch as a durable proc. |
-R, --result-path |
path | $SASE_PROC_RESULT_PATH |
Private typed result sidecar for the same durable-proc path. |
When invoked with no arguments, opens $EDITOR for composing a prompt interactively.
When invoked with ., opens a prompt history picker. All prompts launch as detached
background agents, and multi-prompt queries (containing --- separators) are launched
as sequential detached background agents.
From an interactive terminal outside an agent or durable proc, a prompt whose
%hold preview combines future with scope=host prints
the preview and asks Arm this hold? [y/N]. An over-threshold pending capture also
asks when the preview can resolve project context; typed launch plans can do so, while a
plain project-scoped sase run prompt currently cannot and skips this confirmation.
Declining prints Hold not armed; launch cancelled. and exits 1. Despite that prompt
text, accepting only permits the launch during the current beta; it does not arm the
hold.
sase repro¶
sase repro captures and replays debugging bundles for narrow, reproducible TUI bug
classes. The current target is the Agents-tab loader/apply sequence used to diagnose row
disappearance, reappearance, and duplicate workflow parents; see
Agents Tab Reproduction Bundles. All of its
options are long-only.
| Form | Flag | Values | Default | Description |
|---|---|---|---|---|
sase repro capture agents-tab |
--output |
path | required | Directory where agents_tab_repro.json and capture artifacts are written. |
sase repro capture agents-tab |
--commit-safe |
flag | enabled | Redact local names and paths for a shareable bundle. |
sase repro capture agents-tab |
--no-commit-safe |
flag | - | Keep unredacted local identifiers in the capture. |
sase repro capture agents-tab |
--size |
WxH |
120x40 |
Terminal size label stored with the bundle. |
sase repro capture agents-tab |
--json |
flag | - | Emit a machine-readable capture result. |
sase repro replay |
path |
path | required | Bundle JSON file or bundle directory to replay through the headless TUI. |
sase repro replay |
--assert-stable |
flag | - | Exit non-zero if replay invariants fail. |
sase repro replay |
--json |
flag | - | Emit a machine-readable replay verdict. |
sase repro replay |
--write-artifacts |
path | - | Directory for replay screen text and SVG artifacts. |
sase repro replay |
--size |
WxH |
120x40 |
Headless terminal size used for replay. |
Use the in-TUI ,B capture when a transient row-list bug has just happened in a live
sase's TUI session. The CLI capture path is out-of-band: it loads current filesystem
state and cannot reconstruct refreshes that already passed through the running TUI.
sase macro¶
With no subcommand, sase macro defaults to sase macro list.
sase macro expand¶
| Flag | Values | Default | Description |
|---|---|---|---|
[prompt] |
string | stdin | Prompt text to expand (reads from stdin if omitted). |
-t, --trace |
flag | - | Print expansion trace to stderr showing resolved references. |
sase macro explain¶
| Flag | Values | Default | Description |
|---|---|---|---|
workflow_name |
string | (required) | Workflow name to explain. |
[args] |
string | - | Positional arguments for the workflow. |
-a, --arg |
string | - | Named argument as KEY=VALUE (repeatable). |
sase macro list¶
No flags. Outputs a JSON array of all available macros with name, type, source, inputs,
tags, is_skill, and preview. Clients that insert references should prefer
kind/insertion metadata when present so standalone workflows are inserted as
#!name and inline-capable entries, including markdown macro swarms, are inserted as
#name. Slash skill completion clients should filter to entries where is_skill is
true.
sase macro show¶
| Flag | Values | Default | Description |
|---|---|---|---|
NAME |
string | (required) | Macro or workflow name; copied markers and argument suffixes are tolerated. |
-c, --color |
auto,always,never |
auto |
Color mode for rendered output. |
-f, --format |
full,json,raw |
full |
Rendered detail view, stable JSON record, or exact definition source bytes. |
-p, --project |
string | auto | Resolve within a specific project namespace instead of the detected project. |
sase macro graph¶
| Flag | Values | Default | Description |
|---|---|---|---|
[workflow_name] |
string | - | Workflow name to graph. Lists all workflows if omitted. |
-f, --format |
mermaid,text |
mermaid |
Output format for the DAG visualization. |
sase macro catalog¶
| Flag | Values | Default | Description |
|---|---|---|---|
-o, --out |
path | tempdir | Directory where the rendered PDF should be saved. |
sase macro types¶
| Flag | Values | Default | Description |
|---|---|---|---|
[NAME] |
string | - | Bare type (effort), alias, builtin@<name>, or <dist>@<id>. |
-j, --json |
flag | - | Print the Rust catalog projection as JSON. |
With no NAME, prints grouped tables of scalar, builtin, and plugin types. With NAME,
shows one type's detail card. Unknown names exit 1.
sase init¶
Bare sase init is the onboarding coordinator for SASE-managed resources. It runs
read-only planners for config (owner identity), machine enrollment, memory,
repositories, and skills, in that order, prints a grouped summary, and prompts once per
initializer that needs work when stdin is interactive. Non-interactive runs never
prompt; they print the drift summary and ask the caller to rerun with --yes. That flag
runs needed initializers but cannot authorize creation of a missing provider sidecar
repository, which always requires its own interactive y/yes response. The memory
planner (which owns agent-document initialization) only generates managed project
AGENTS.md from bare sase init when the current project's own sase/sase.yml sets
is_sase_managed: true. The repository planner uses that same local marker and skips
unmanaged repositories before provider work. Neither planner infers project ownership
from memory.h1_title, existing memory notes, lifecycle state, or merged configuration.
See Initialization for the full flow.
--all applies that coordinator to every registered enabled main project from its
recorded primary workspace, even when the command starts outside a project. It excludes
disabled projects, internal sibling backing records, home, and other system-managed
records, continues after per-project failures, and returns non-zero if any project has
drift, is unavailable, or fails. --all --check is read-only, while non-interactive
apply still requires --yes. --all is incompatible with --enable-project-memory and
with explicit compatibility subcommands. -p/--project names a subset of the same
enabled projects instead and has the same restrictions, plus it cannot be combined with
--all.
Advanced deploy controls stay on explicit subcommands such as
sase memory init --no-commit and sase skill init --no-push. The compatibility
subcommands sase init config, sase init machine, sase init memory,
sase init repo, and sase init skills accept the flags of sase config init,
sase machine init, sase memory init, sase repo init, and sase skill init.
| Flag | Values | Default | Description |
|---|---|---|---|
-a, --all |
flag | - | Attempt every known enabled main SASE project and report one aggregate status. |
-c, --check |
flag | - | Report initialization drift without writing; exits non-zero when changes are needed. |
-d, --diff |
flag | - | Show full file diffs for planned changes. |
-j, --json |
flag | - | With --check, emit one schema-versioned JSON plan (current, drift, or blocked) instead of human output. |
-M, --enable-project-memory |
flag | - | Mark the repository with is_sase_managed: true before initialization; cannot be combined with --check. |
-p, --project |
project name | - | Check or initialize one enabled project by name, display name, or alias; repeatable. |
-y, --yes |
flag | - | Run needed initializers without generic prompts; cannot approve creation of a missing provider sidecar repository. |
sase machine¶
With no subcommand, sase machine defaults to the offline sase machine list.
Bootstrap bundles contain live single-use secrets and are never accepted as command-line
values. init always needs an interactive stdin for candidate and alias selection; it
reads the bundle from -B/--bootstrap-file or a hidden prompt. add and repair also
accept a bundle on piped stdin when --bootstrap-file is omitted.
| Form | Flags | Description |
|---|---|---|
sase machine / machine list |
-j/--json |
List configured aliases without provider or gateway I/O. |
sase machine discover |
-j/--json, -p/--provider, -t/--timeout |
Query configured or repeatably selected discovery providers. |
sase machine bootstrap |
-e/--expires, -j/--json, -s/--scope |
On the target, issue a scoped single-use bundle to stdout. |
sase machine init |
-B/--bootstrap-file, -c/--check, -j, -t |
Interactively discover, enroll, reload, and verify; --check is offline. |
sase machine add ALIAS [ENDPOINT] |
-B, -c/--candidate, -j, -p/--provider, -S/--ssh-target, -t |
Enroll a named HTTPS endpoint or discovered candidate, then activate. |
sase machine show ALIAS |
-j/--json |
Show one local machine record, including its effective SSH target. |
sase machine status [ALIAS ...] |
-j/--json, -t/--timeout |
Run authenticated hello checks; no aliases means all configured aliases. |
sase machine repair ALIAS |
-B/--bootstrap-file, -j/--json, -t/--timeout |
Rotate a quarantined or mismatched enrollment and activate the replacement. |
sase machine rename OLD NEW |
-j/--json |
Rename the viewer-local alias without changing gateway identity or credentials. |
sase machine remove ALIAS |
-j/--json, -y/--yes |
Remove local config and its credential reference; interactive stdin prompts unless --yes. |
sase machine agent {stop,retry,fork} |
action-specific arguments, -j, -t |
Submit a journaled remote lifecycle mutation; sase's TUI supplies revision-safe durable sidecars. |
sase machine attention {answer,approve} |
action-specific arguments, -j, -t |
Answer a remote question or approve a gate through the same durable operation path. |
See the Remote Dispatch Runbook for gateway supervision, Tailscale Serve, enrollment, launch constraints, and sase's TUI machine-row operation.
sase instructions¶
With no subcommand, sase instructions defaults to sase instructions list.
| Form | Flags | Description |
|---|---|---|
sase instructions |
- | Show the same read-only agent-document inventory as sase instructions list. |
sase instructions list |
- | Inspect project, home, and chezmoi AGENTS.md files and provider shims. |
sase instructions verify |
-a/--agent, -c/--coverage, -H/--helpers, -j/--json, -n/--limit, -p/--provider, -s/--since, -u/--until |
Show observed instruction loads per provider; -c adds manifest coverage, -a adds the section diff, -j emits schema_version: 1 JSON. Reports only, never gates. |
sase instructions render |
-a/--agent, -f/--fact, -j/--json, -N/--no-cache, -p/--parity, -s/--sections |
Preview the memory-built bundle without delivering it; -j emits the preview manifest, -s the section table, -p checks legacy parity. |
sase memory¶
With no subcommand, sase memory defaults to sase memory list.
| Form | Flags | Description |
|---|---|---|
sase memory |
- | Show the same read-only memory context dashboard as sase memory list. |
sase memory list |
- | Show loaded, referenced, available, and missing memory files for the current launch context. |
sase memory read <selector> ... |
-r, --reason <reason> required; -d/--depth, -f/--format, -p/--project |
Agent-side read of one or more selectors (a flat note, a bare web, or web:keyword) with one attributable audit event. |
sase memory show <selector> ... |
-d/--depth, -f/--format, -p/--project |
Resolve and print the same selectors without recording a read. |
sase memory log |
--path, --agent, --id, --include, --json |
Summarize or inspect audited memory reads; --include glossary folds in the legacy glossary log. These options are long-only. |
sase memory web / web list |
-f/--format table\|names\|json, -p/--project |
List discovered memory webs with rendering, scope, and strand count. |
sase memory web show <web> [PATTERN] |
-b/--bodies, -f/--format table\|names\|json, -p/--project |
Print one web's strand index (keyword, slug, aliases, reference count, summary), filtered by PATTERN; -b extends matching into strand bodies. Never prints bodies. |
read and show resolve each selector from the project's sase/memory/ first and fall
back to ~/sase/memory/. The whole batch is resolved before anything is printed or
logged, so one unknown selector fails the request. -f/--format accepts markdown (the
default), rich, or json. -d/--depth N caps link and mention closure recursion
(unlimited by default); -d 0 prints only the requested selectors and lists every link
as a reference. See Memory for attribution and log details.
Examples:
# read requires SASE agent identity
sase memory read generated_skills.md --reason "Need generated skill context"
sase memory read glossary:stitch glossary:patch -r "Need the terms"
sase memory show generated_skills.md -f rich
sase memory web show glossary agent
sase memory log
sase memory log --path generated_skills.md
sase memory log --id <read-id>
sase memory init¶
Creates or refreshes home memory and memory for SASE-managed projects. Project ownership
requires is_sase_managed: true in the project's own sase/sase.yml; memory.h1_title
is optional title customization, with a stable derived title otherwise. The retired
memory.enabled key does not authorize management. It never creates or alters an
unmanaged project's root AGENTS.md. Independently, it overwrites each provider
instruction file (CLAUDE.md, GEMINI.md, QWEN.md, OPENCODE.md) with a
byte-for-byte copy of that root's AGENTS.md (legacy @AGENTS.md / *.md.tmpl import
shims are recognized and migrated to full copies). This copy applies to every existing
project-tree AGENTS.md; directories without one are untouched. For managed roots,
memory init synchronizes memory: core notes are inlined into the ## Core Memory block
of AGENTS.md, every heading in the generated document is numbered, reference notes are
rendered as numbered sections headed by the note path with the description as the body,
and missing reference-memory description frontmatter is inserted. By default it also
tries to commit, rebase-pull, and push generated project-side files. sase init memory
is a compatibility alias for this command. Generated repository memory requires agents
to use /sase_repo before reading or modifying any repo outside their own workspace
checkout. The rule covers linked repos, sidecars, different SASE projects, and unlinked
GitHub repos even when no linked repositories are configured. The same run also
refreshes each memory web's inline strand roster — for example the glossary web's
**GLOSSARY TERMS:** line in sase/memory/glossary.md — from its current strand files;
sase memory init --check reports drift if a web's rendered roster or its Memory Webs
inlining is stale. Unlike the generated notes above, sase/memory/glossary.md and every
other web descriptor are user-owned content: memory init rewrites only the roster it
renders, never the rest of the descriptor body.
| Flag | Values | Default | Description |
|---|---|---|---|
-c, --check |
flag | - | Report memory initialization drift without writing project or home files. |
-d, --diff |
flag | - | Show full file diffs for planned memory changes. |
-M, --enable-project-memory |
flag | - | Set is_sase_managed: true, enabling managed project memory; incompatible with --check. |
-m, --message |
string | - | Commit subject used when eligible memory source edits are folded into the generated-change commit; a docs(memory): tag is added if omitted. |
-C, --no-commit |
flag | - | Write files, but skip only the project git commit/pull/push path; home deployment still follows config. |
sase init repo¶
sase init repo is an alias for sase repo init. For targets marked
is_sase_managed: true in their own sase/sase.yml, it initializes configured
sidecars, creates or refreshes generated README files, ensures the managed plans and
research declarations, and maintains the root /sase/repos/ ignore rule. Missing or
false markers produce an informative successful no-op, while invalid local configuration
fails before provider or filesystem work. The command always targets the Git repository
that contains the current directory; it has no --path option. GitHub setup creates
missing sidecars with their configured public/private visibility. Bare-git projects
refresh generated files automatically during repository setup and first SDD writes; the
explicit command remains useful for refreshes and --check audits.
When the GitHub sidecar is missing, this alias uses the same default-no
repository-specific confirmation as sase repo init. EOF, interruption, and any answer
other than y/yes return nonzero before remote creation. Generic --yes approval
never authorizes repository creation; non-interactive bare onboarding instead reports
the missing remote and defers its creation.
| Flag | Values | Default | Description |
|---|---|---|---|
-c, --check |
flag | - | Report sidecar, config, and ignore-rule work without writing files. |
-d, --diff |
flag | - | Show full file diffs for planned repository changes. |
-C, --no-commit |
flag | - | Write project config and ignore rules without committing or pushing. |
sase skill¶
With no subcommand, sase skill defaults to the read-only sase skill list dashboard.
It reports loaded skill sources, provider targets, and deployed-file drift without
writing files. sase skill init generates and deploys agent skill files from macro
sources marked with the skill field. Generated skill files begin with a
sase skill use directive so agent-side skill use can be audited and later summarized
with sase skill log, unless the source sets log_skill_use: false. See
macros.md — Skill Field for the skill-source contract and
provider targets. Existing files are skipped in non-interactive runs unless --force is
passed; interactive runs prompt before overwriting. Commit and land macro template
changes before deploying: writing chezmoi deploys are refused from dirty or unmerged
sources, and refused when they would move the destination off the source commit recorded
in the provenance manifest — see
Commit Before Deploying. sase init skills is a
compatibility alias for sase skill init.
| Form | Flags | Description |
|---|---|---|
sase skill |
- | Show the same read-only dashboard as sase skill list. |
sase skill list |
- | Inspect generated skill sources, provider targets, and deployed-file drift. |
sase skill init |
-f, --force |
Overwrite deployed skill files without confirmation; bypass the provenance manifest guard. |
sase skill init |
-D, --allow-dirty |
Deploy from uncommitted or unmerged macro sources; can revert other agents' deployments. |
sase skill init |
-n, --dry-run |
Show what would be written without writing files. |
sase skill init |
-c, --check; -d, --diff |
Report or diff generated skill-file drift without writing files. |
sase skill init |
-p, --provider <name> |
Deploy only for one registered provider (claude, agy, codex, grok, muse, opencode, qwen). |
sase skill init |
-y, --yes |
Answer the overwrite confirmation for existing generated targets; unlike --force, it does not override source-integrity or provenance-manifest guards. |
sase skill init |
-A, --no-apply |
With use_chezmoi, skip chezmoi apply after generated files are committed and pushed. |
sase skill init |
-C, --no-commit |
With use_chezmoi, skip the entire git commit, push, and apply sequence. |
sase skill init |
-P, --no-push |
With use_chezmoi, commit generated files but skip pull/rebase, push, and chezmoi apply. |
sase skill log |
-a, --agent; -R, --runtime; -s, --skill; -i, --id; -j, --json |
Summarize or inspect audited generated skill-use events. |
sase skill use |
-r, --reason <reason> required |
Agent-side audit event recording that the current agent is using a generated skill. |
sase init skills |
same as sase skill init |
Compatibility alias for sase skill init. |
sase repo init¶
sase repo init declares the managed plans, beads, and default research document
sidecar, initializes every enabled configured sidecar, and ensures the project root
.gitignore contains /sase/repos/, protecting host-scoped repository clones durably.
-c, --check reports drift without writing, -d, --diff renders proposed full-file
diffs, and -C, --no-commit writes project config and ignore changes without the normal
project commit/pull/push sequence. sase init repo is an alias; bare sase init and
sase validate include the same check for Git projects.
sase tool¶
Named-tool catalog and foreground ToolRun commands. With no subcommand, sase tool
defaults to sase tool list.
| Command | Flag / argument | Values | Description |
|---|---|---|---|
sase tool list |
-j, --json |
flag | Emit a versioned JSON object with LAST, TYPICAL, argv, and definition digest. |
sase tool run TOOL [-- ARGS...] |
-q, --quiet; -T, --tail-lines; -v |
mixed | Run a named tool at the project root. Extra args append only when the definition allows them. |
sase tool run -H TOOL |
-H, --hand-off |
flag | Hand the run off to a durable proc and return at once with the run id (-q prints only the id; -v/-T are usage errors). Exits 0 accepted, 1 not started, 2 usage or refusal. |
sase tool run TOOL |
-k, --keep-going or -x, --fail-fast |
flag | Continue past, or stop at, the first failed stage (named run_silent tools only). |
sase tool run -- ARGV... |
same | mixed | Run an ad-hoc argv at the invocation cwd. The -- separator is required. |
sase tool runs |
-a -A -c -j -n -s -t |
mixed | List native ToolRuns (default: current project, limit 50, max 1000). |
sase tool show RUN |
-j, --json or -l, --logs; -F |
flag | Show one run by exact id; -F/--follow streams its output until it settles. -j and -l are mutually exclusive. Missing runs exit 2. |
sase tool wait RUN |
-T, --tail-lines; -t; -j |
mixed | Block until the run settles or the -t deadline passes (the run is never affected). Returns its exit code, 1 if settled without one, 124 on deadline, 130 on Ctrl-C. |
sase tool stop RUN |
-j, --json |
flag | Record a durable stop request and stop the run through its owner (proc, monitor, or inline wrapper). Exits 0 requested/stopped/settled, 2 unknown or refused, 1 owner failure. |
sase tool failures |
-a -c -d -j -n -t |
mixed | List grouped failure signatures from stored triage (default: current project, last 7 days, limit 50, max 1000). |
sase tool receipt TOOL |
-a, --accept; -j, --json |
mixed | Report the covering verdict receipt at the current fingerprint, or a typed refusal. Exits 0 covered, 1 refused, 2 usage. The query never claims a completion policy is met. |
sase tool receipts |
-d, --days; -j, --json |
mixed | List retained receipts and content-equivalent repeat opportunities over the last N days (default 7). Measurement only. |
sase tool stats |
-a -d -j -t |
mixed | Report ToolRun durations, waste, repeats, backtest, and pressure (default: current project, last 7 days; -d is 1..180). Read-only, machine-local. Exits 0 reported, 1 store failure, 2 usage. |
Humans default to exact stdout/stderr passthrough; wrapper metadata goes to stderr.
Direct agent execution (SASE_AGENT_NAME) defaults to compact output. -q forces
compact and -v forces streaming. Recording failures warn once and still execute the
child exactly once without inventing a durable id. Catalog, usage, and missing-run
errors exit 2. sase tool run returns the child's exit code, or 128+signal.
Each recorded run captures a bounded pre/post fingerprint of configured repositories,
input globs, allow-listed environment names, and toolchain probes. It also samples host
load at start, roughly every ten seconds, and finish. sase tool show reports evidence
completeness, whether complete fingerprints prove an input mutation, dirty-path counts,
toolchain summaries, and samples. A missing repository/input, timed-out probe,
unsupported host field, or observation budget exhaustion remains explicit incomplete
evidence; SASE never substitutes zero or claims mutated_input from incomplete
fingerprints.
For definitions with stages: run_silent, nested tools/run_silent calls append stage
events to the same run. Compact completion and sase tool show render that timeline,
including repeated or overlapping stages and the interval-union time not attributed to
any stage. -j exposes the complete stage, fingerprint, and sample records. -l is a
different mode: it replays retained stdout and stderr without claiming a total ordering
between those streams.
LAST is the newest native result for this project and definition. TYPICAL is the
observed median duration of at most 30 normally exited native runs within 30 days.
Missing samples render as an em dash, never as zero or an ETA.
sase disk¶
Disk commands inspect SASE-created bytes by owner and delegate cleanup back to those
owners. With no subcommand, sase disk defaults to sase disk list.
| Command | Flag / argument | Values | Description |
|---|---|---|---|
sase disk list |
-j, --json |
flag | Emit owner, horizon, size, path, and unowned-stray rows as JSON. |
sase disk reap |
-a, --apply |
flag | Run owner cleanup passes instead of previewing them. |
sase disk reap |
-j, --json |
flag | Emit the delegated cleanup plan or execution result as JSON. |
sase disk reap |
-p, --project |
project name | Limit project-scoped artifact and workspace cleanup to one project. |
Unowned Cargo-shaped strays are listed for human action and are never deleted by
sase disk reap.
The sase disk list table shows each row's owner, section, size, coverage, horizon, and
path. Managed-temp rows cover every registered root writers actually used, not only the
effective one. Workspace checkouts get one row per project plus sub-rows for
.git/objects, .pytest_cache (including sase-visual), sase/repos, .venv, and
in-tree target/, sized before the generic stray walk so budget exhaustion can only
clip that walk. Unowned rows are highlighted; a row whose coverage is partial or
unresolved means the owner could not account for every byte. When coverage is partial
or the filesystem is under pressure, an unattributed row reports filesystem used minus
attributed bytes on the filesystem holding SASE_HOME. When rows share physical storage
(for example workspaces that borrow primary Git objects), the size cell also shows the
exclusively counted bytes, and the footer reports the physical total, the logical row
total when it differs, the overall coverage status, and any partial-scan diagnostics.
sase disk reap runs, or previews, the owner passes in order: managed-temp reaping,
proc runtime cleanup, ACE-run artifact retention (the same owner as
sase artifact prune-runs), and workspace Git object compaction. It exits 1 when any
owner step is blocked, fails, or cannot report a trustworthy result, and 0 otherwise.
Because ACE-run deletion is currently preview-only, the artifact-run step reports
blocked under --apply.
sase workspace¶
Workspace commands inspect and maintain the managed checkout registry for the inferred
project, or for the project named by -p/--project. With no subcommand,
sase workspace defaults to sase workspace list with default options. Use
sase workspace list -p <project>, sase workspace list --all, or
sase workspace list --json when passing list flags.
| Command | Flag / argument | Values | Description |
|---|---|---|---|
sase workspace list |
-p, --project |
project name | Query a project other than the one inferred from the current directory. |
sase workspace list |
-a, --all |
flag | Inventory registered workspaces across every enabled and disabled project. |
sase workspace list |
-j, --json |
flag | Emit a machine-readable JSON object. |
sase workspace path |
workspace_num |
integer | Workspace number to resolve; 0 is the primary checkout and managed claims normally start at 10. |
sase workspace path |
-p, --project |
project name | Query a project other than the inferred one. |
sase workspace cleanup |
-p, --project |
project name | Clean a project other than the inferred one. |
sase workspace cleanup |
-s, --stale |
flag | Remove unclaimed managed checkouts older than workspace.cleanup_ttl_days. |
sase workspace cleanup |
-i, --include-shares |
flag | Also consider workflow-share managed checkouts for removal. |
sase workspace cleanup |
-n, --dry-run |
flag | Report planned removals without touching the filesystem. |
sase workspace compact |
-p, --project |
project name | Compact a project other than the inferred one. |
sase workspace compact |
-n, --dry-run |
flag | Report planned Git object compactions without touching config or objects. |
sase workspace compact |
-j, --json |
flag | Emit the compaction plan or result as a machine-readable JSON object. |
sase workspace compact |
workspace_nums |
integer list | Optional registered workspace numbers to compact; omit to scan all numbered checkouts. |
sase workspace repair |
-p, --project |
project name | Repair a project other than the inferred one. |
sase workspace repair |
-n, --dry-run |
flag | Report registry/filesystem and SASE-managed alternate reconciliation without writing. |
sase workspace migrate |
-p, --project |
project name | Migrate a project other than the inferred one. |
sase workspace migrate |
-t, --to |
xdg-state |
Target managed root policy for migration. |
sase workspace migrate |
-s, --symlink-transition |
flag | Leave <primary>_<num> symlinks pointing to migrated managed checkouts. |
sase workspace migrate |
-f, --finalize |
flag | Remove transition symlinks left behind by a prior migration. |
sase workspace migrate |
-n, --dry-run |
flag | Report planned migration or finalization actions without touching files or the registry. |
For built-in bare-git projects, sase repo open may initialize generated SDD guide
files in the primary checkout before materializing a numbered workspace.
sase workspace list and sase workspace path remain read-only and do not run SDD
initialization.
sase bead¶
With no subcommand, sase bead defaults to sase bead list. Commands that take
existing bead IDs route a full ID to the enabled project whose bead store owns it, while
shorthand suffixes stay in the current project; see
CLI Reference and
Bead ID Arguments. The tables below cover the most-used
subcommands; Beads documents every subcommand.
| Flag | Values | Default | Description |
|---|---|---|---|
| subcommand | +1, blocked, close, create, dep, doctor, epic-symbols, history, init, list, note, onboard, open, pages, ready, ref, resolve-conflicts, rm, search, show, snooze, stats, sync, sync-external, task-type, update, work |
list |
Bead subcommand |
sase bead create¶
| Flag | Values | Default | Description |
|---|---|---|---|
-t, --title |
string | (required) | Issue title |
-w, --reason |
string | (required) | Why the bead was filed. Trimmed, non-blank, at most 2000 characters. @<path> reads the text from a file. See Creation Reason. |
-T, --type |
string | (required) | plan(<file>), plan(<file>,<parent>), phase(<parent_id>), or task(<slug>). Feature flags use sase flag new |
-f, --field |
k=v |
- | Task-type field value; repeatable. @<path> reads the value from a file |
-d, --description |
string | - | Issue description |
-a, --assignee |
string | - | Assignee name |
-m, --model |
string | - | Epic land-agent, phase-worker, or task-worker model |
-R, --ref |
artifact reference | - | Artifact reference to attach; repeatable |
-z, --size |
xsmall, small, medium, large, xlarge |
- | Phase/task size; required for new task beads and rejected for plan beads. Phases use model and plan-first routing, tasks use model routing only |
-r, --tier |
plan, epic |
- | Plan-bead tier; invalid for phase and task beads |
-c, --patch, --changespec |
Patch name | - | Attach Patch metadata to a plan bead; --changespec is legacy-compatible |
-b, --bug-id |
string | - | Bug ID for the attached Patch; requires --patch or --changespec |
-x, --external-ref |
string | - | Project-qualified external issue identity, e.g. bug:sase#42 |
sase bead list¶
| Flag | Values | Default | Description |
|---|---|---|---|
-c, --color |
auto, always, never |
auto |
Color mode for compact output |
-f, --format |
compact, json, full |
compact |
Output format |
-n, --limit |
non-negative integer | (unlimited) | Maximum beads to print; closed listings default to 20, 0 means all |
-S, --since |
DATE | - | Only beads created at or after DATE |
-s, --status |
all, open, claimed, ready, snoozed, in_progress, closed |
open, claimed, ready, snoozed, in progress | Filter by status (repeatable); all selects every status. With no match and no --status, closed beads are listed instead |
-T, --task-type |
catalog slug or untyped |
- | Filter by task type (repeatable); untyped selects legacy beads |
-r, --tier |
plan, epic |
- | Filter by plan-bead tier (repeatable) |
-t, --type |
plan, phase, task |
- | Filter by issue type (repeatable). Flag beads are tasks; use -T flag |
-u, --until |
DATE | - | Only beads created at or before DATE |
sase bead search¶
| Flag | Values | Default | Description |
|---|---|---|---|
query |
string | (required) | Literal non-empty text to search for |
-c, --color |
auto, always, never |
auto |
Color mode for compact output |
-f, --format |
compact, json, full |
compact |
Output format |
-e, --regex |
flag | - | Treat the query as a case-insensitive regular expression unless it starts with (?-i) |
-n, --limit |
non-negative integer | (unlimited) | Maximum results to print; 0 also means unlimited |
-s, --status |
open, claimed, ready, snoozed, in_progress, closed |
- | Filter by status (repeatable); all statuses are searched by default |
-T, --task-type |
catalog slug or untyped |
- | Filter by task type (repeatable); untyped selects legacy beads |
-r, --tier |
plan, epic |
- | Filter by plan-bead tier (repeatable) |
-t, --type |
plan, phase, task |
- | Filter by issue type (repeatable). Flag beads are tasks; use -T flag |
sase bead task-type¶
With no subcommand, sase bead task-type defaults to sase bead task-type list.
| Flag / argument | Values | Default | Description |
|---|---|---|---|
list |
Colored catalog table; -a/--all includes uncreatable |
||
show <slug> |
Full spec, fields, template, triage, and provenance | ||
-j, --json |
flag | - | Machine-readable output on list and show |
sase bead epic-symbols¶
| Flag | Values | Default | Description |
|---|---|---|---|
id |
string | (all) | Optional bead ID; omit to list every entry |
-c, --color |
auto, always, never |
auto |
Color mode for compact output |
-f, --format |
compact, json |
compact |
Output format |
sase bead read¶
| Flag | Values | Default | Description |
|---|---|---|---|
ids |
string | (required) | One or more IDs; <epic-id>.. expands the epic plus direct children |
-c, --color |
auto, always, never |
auto |
Color mode; applies to --format full and compact |
-f, --format |
compact, json, full |
full |
Output format. Compact never expands artifact links |
-N, --no-links |
flag | off | Skip artifact-link neighborhood resolution and omit link sections / JSON |
-p, --pager |
auto, always, never |
auto |
Page long terminal output |
-P, --project |
project key, name, or alias | current project | Resolve every ID against one enabled project's bead store, including shorthand suffixes |
-r, --reason |
string | (required) | Non-empty reason for the audited bead read |
-s, --style |
auto, plain, rich |
auto |
Styling level for --format full |
-w, --wrap |
integer >= 20, auto, none, 0 |
88 |
Prose wrap width for the filing reason, description, notes, link reasons, and evidence |
sase bead touched¶
| Flag | Values | Default | Description |
|---|---|---|---|
agent |
string | (required) | Agent name, globalized or local |
-j, --json |
flag | off | Machine-readable rows with verbs, actors, and read_reasons |
-l, --limit |
integer >= 0 | unlimited | Maximum beads to print; 0 means unlimited |
-v, --verb |
verb | (all) | Only show beads touched with this verb (repeatable); unknown verbs exit 2 |
sase bead show¶
| Flag | Values | Default | Description |
|---|---|---|---|
ids |
string | (required) | One or more IDs; <epic-id>.. expands the epic plus direct children |
-c, --color |
auto, always, never |
auto |
Color mode; applies to --format full and compact |
-f, --format |
compact, json, full |
full |
Output format. Compact never expands artifact links |
-N, --no-links |
flag | off | Skip artifact-link neighborhood resolution and omit link sections / JSON |
-p, --pager |
auto, always, never |
auto |
Page long terminal output |
-P, --project |
project key, name, or alias | current project | Resolve every ID against one enabled project's bead store, including shorthand suffixes |
-s, --style |
auto, plain, rich |
auto |
Styling level for --format full |
-w, --wrap |
integer >= 20, auto, none, 0 |
88 |
Prose wrap width for the filing reason, description, notes, link reasons, and evidence |
sase bead open¶
| Flag | Values | Default | Description |
|---|---|---|---|
id |
string | (required) | Issue ID to reopen |
sase bead update¶
| Flag | Values | Default | Description |
|---|---|---|---|
ids |
string | (required) | One or more full or shorthand issue IDs |
-s, --status |
open, claimed, ready, in_progress, closed |
- | Change status; ready is task-only. Use sase bead snooze to snooze |
-t, --title |
string | - | Change title |
-d, --description |
string | - | Change description |
-n, --note |
string | - | Append an attributed note to each issue |
-D, --design |
path | - | Change design path; all types accepted |
-a, --assignee |
string | - | Change assignee |
-m, --model |
string | - | Change launch model; '' clears it |
-b, --remove-by |
YYYY-MM-DD/release |
- | Extend one flag task bead's thresholds |
-z, --size |
xsmall, small, medium, large, xlarge |
- | Change phase/task size |
-r, --tier |
plan, epic |
- | Change plan-bead tier |
-x, --external-ref |
string | - | Set the project-qualified external issue identity, e.g. bug:sase#42 |
-X, --clear-external-ref |
flag | - | Clear the external issue identity |
sase bead close¶
| Flag | Values | Default | Description |
|---|---|---|---|
ids |
string | (required) | One or more IDs; exactly one epic ID with --phases |
-f, --force |
flag | - | Sweep unfinished descendants; needs both below |
-P, --no-push |
flag | - | Commit the close locally but skip the post-commit push |
-n, --note |
string | - | Attributed note appended to each listed issue |
-p, --phases |
number/range list | - | Close numbered phase beads of the target epic |
-r, --reason |
string | - | Optional close reason text |
-R, --resolution |
canceled, done, superseded |
done |
How this bead was resolved |
sase bead rm¶
Atomically removes the requested issues and the recursive union of their descendants. Every requested ID must exist; overlapping or repeated selections remove each issue only once. Removal is irreversible.
| Flag | Values | Default | Description |
|---|---|---|---|
ids |
string | (required) | One or more issue IDs |
sase bead note¶
| Flag | Values | Default | Description |
|---|---|---|---|
id |
string | (required) | Full or shorthand issue ID |
text |
string | - | Note text; a single @<path> token reads it from a file. Required unless --remove is given |
-a, --author |
string | current agent, else owner | Author recorded on the entry |
-e, --edit |
note ordinal N |
- | Rewrite note N as numbered by sase bead read; ordinals shift after any edit or removal |
-x, --remove |
note ordinal N |
- | Retract note N; sase bead history keeps the retracted record |
sase bead dep¶
Bare sase bead dep delegates to sase bead dep list. Mutations reject dependency
edges that would cross bead stores before writing.
| Form | Flag / argument | Values | Default | Description |
|---|---|---|---|---|
dep list |
id |
string | store-wide | Optional issue whose edges to list |
dep list |
-d, --direction |
both, in, out |
both |
Edges to show |
dep list, dep tree |
-c, --color / -f, --format |
auto\|always\|never / compact\|full\|json |
auto / compact |
Color mode and output format |
dep list, dep tree |
-s, --status |
open, claimed, in_progress, closed |
all when scoped; open, claimed, ready, snoozed, in progress store-wide | Filter by status (repeatable); current --help text omits ready and snoozed from the runtime default |
dep list |
-n, --limit |
non-negative integer | - | Maximum beads to print; 0 means unlimited |
dep tree |
id |
string | store-wide | Optional issue to walk from |
dep tree |
-d, --direction |
both, in, out |
out |
Walk dependencies (out), blockers (in), or both |
dep tree |
-L, --levels |
non-negative integer | unlimited | Maximum levels to descend; 0 means unlimited |
dep add |
issue |
string | (required) | Issue that depends |
dep add |
depends_on |
string | (required) | Issue being depended upon |
dep rm |
issue depends_on... |
strings | (required) | Remove one or more dependencies from an issue |
sase bead sync¶
| Flag | Values | Default | Description |
|---|---|---|---|
-s, --status |
flag | - | Check sync status without committing |
sase bead work¶
| Flag | Values | Default | Description |
|---|---|---|---|
targets |
bead IDs or plan paths | (required) | One or more epic/task beads or validated epic plan files, processed in order until the first error |
-a, --artifacts-dir |
directory | - | Back-fill planner artifacts after each approved epic; plan-file targets only |
-c, --capacity |
positive integer | omitted | Epic-only per-launch capacity budget; 1 runs alone; a heavier macro weight floors that segment |
-C, --cl-name |
Patch name | - | Approved epic Patch name applied per plan-file target |
-n, --dry-run |
flag | - | Preview the epic wave plan or task prompt without mutating files, beads, or agents |
-j, --json |
flag | - | Print one result object per processed target as JSON Lines and imply --yes-to-all |
-P, --no-push |
flag | - | Commit checkpoint state locally but skip post-commit pushes |
-p, --parent |
bead ID or top-level |
- | Override a plan file's parent_bead, including forcing an unparented epic; plan-file targets only |
-w, --wait |
comma-separated names and bead=<id> |
- | Agent names and bead dependencies the launched phases wait for |
-y, --yes |
flag | - | Skip only the launch confirmation prompt |
-Y, --yes-to-all |
flag | - | Skip both destructive-cleanup and launch confirmation prompts |
Multiple sase bead work targets are non-atomic: earlier successes are not rolled back,
later targets are not prevalidated, and every command-wide flag is checked again for the
current target.
SDD repository and plan commands¶
SDD initialization/path resolution lives under sase repo; artifact browsing and
prompt/plan link maintenance live under sase plan. Link commands accept -p/--path,
which may point at an SDD root or a project root. Bare sase plan links defaults to its
list child.
| Command | Flags | Description |
|---|---|---|
sase repo init |
-c/--check, -d/--diff, -C/--no-commit |
Initialize configured sidecars and repository wiring for the current repository |
sase repo path REPO |
-e/--ensure, -p/--project, -w/--workspace |
Print a primary or sidecar path; optionally materialize it |
sase plan links [list] |
-p/--path, -j/--json |
List prompt/plan artifact links and bidirectional status |
sase plan links refresh |
-p/--path, -P/--plan, -j/--json, -w/--write |
Dry-run (or, with --write, commit) reconciliation of PARENT, BEAD, AGENTS, and COMMITS header sections |
sase plan links repair |
-p/--path, -w/--write |
Infer unambiguous prompt/plan pairs and optionally write fixes |
sase plan links validate |
-p/--path, -j/--json, -q/--quiet, -W/--show-warnings |
Validate a plan's own metadata and PROMPT bullet |
sase plan search |
-k/--kind, -o/--source, -f/--format, plus query/date/status filters |
Search or browse tale, epic, prompt, and document-sidecar artifacts |
sase validate¶
sase validate is the top-level portable SASE validation command. It takes no options
and runs, in order, sase doctor -C plugins.required (so a missing required plugin is
never reported as spurious generated-file drift), the explicit
sase init memory --check, sase init repo --check, and sase init skills --check
surfaces, sase doctor -C config.file_hooks, sase plan links validate, and
sase agent prompts validate. It prints one ok, skip, or fail line per check and
exits non-zero if any check fails; the prompt-archive check reports skip rather than
failing when no agents-sidecar prompt archive context is available. It deliberately
leaves the machine-local Config and machine planners to bare sase init --check and
sase doctor, so clean CI hosts do not need a synthetic machine identity. The command
can still fail on user/home memory or skill deployment drift even when repository-local
SDD validation passes.
A check can pass and still have something to say. When a check that exits 0 prints its
own Warnings: section — sase init skills --check deferring a chezmoi redeploy is the
common case — sase validate collects those lines and reprints them under a single
Warnings: block, after the per-check status lines and before any failure output. The
block is informational: it does not change the exit code, and it is separate from the
stdout dump that a failing or skipped check still produces.
sase doctor¶
Runs the read-only support diagnostics bundle for the active runtime, configuration, provider setup, project/workspace state, bead store, agent index, and telemetry when configured. Default mode is bounded and safe to run before asking for help; deep mode adds slower read-only checks.
| Flag | Values | Default | Description |
|---|---|---|---|
-j, --json |
flag | - | Emit the schema_version: 1 JSON support report. |
-v, --verbose |
flag | - | Show every check plus bounded details in human output. |
-D, --deep |
flag | - | Include slower read-only deep checks. |
-s, --strict |
flag | - | Exit non-zero for warnings as well as errors. |
-L, --list-checks |
flag | - | List registered default and deep check ids without running them. |
-C, --check |
id/group | repeat | Run only the selected check id or group; may be passed multiple times. |
-F, --fix-duplicate-blocks |
flag | - | Preview and, after confirmation or -y, keep one block per Patch name in every ProjectSpec. |
-R, --fix-primary-sidecar-links |
flag | - | Preview and, after confirmation or -y, restore stranded link-index deletions in primary-nested sidecars. |
-p, --project |
string | infer | Inspect a named project when doctor cannot infer one from the checkout. |
-y, --yes |
flag | - | Apply requested repairs without an interactive confirmation. |
Use sase doctor -L to list targeted check IDs. Useful focused checks include
runtime, llm.default, plugins.required, plugins.resources, beads.task_types,
project.junk_directories, project.primary_sidecar_link_dirt,
workspace.missing_checkouts, workspace.occupancy_conflicts, and
config.model_macros, config.macro_definitions, config.macro_input_types, and
config.macro_directives. The two inventory checks report telemetry-only directories
without ProjectSpecs and registered workspace paths missing from disk; both are
read-only and provide cleanup/repair guidance. workspace.occupancy_conflicts reports
RUNNING-field and occupant-record collisions and never auto-repairs.
config.macro_input_types lists unknown type names, the deprecated string alias, and
enum choice issues across macro sources. config.macro_directives locates definition
files that still use retired directive syntax. agent_holds.stale warns about
agent holds whose armer died or whose TTL passed; it
reconciles the hold store the same way sase agent hold list does, so the stale records
it reports are pruned as a side effect.
Default exit behavior is 0 for OK, WARN, and SKIP, and 1 for ERROR. Attach
sase doctor -v or sase doctor -j when asking for help.
sase flag¶
With no subcommand, sase flag defaults to sase flag list.
| Form | Flag or argument | Description |
|---|---|---|
sase flag disable |
<key>, -j/--json |
Persistently disable a registered flag in $SASE_HOME/feature_flags.json. Restarts the running scheduler service proc; a stopped scheduler is left stopped. Restart any separately running sase's TUI. Restart failure does not roll back the saved preference. |
sase flag enable |
<key>, -j/--json |
Persistently enable a registered flag in $SASE_HOME/feature_flags.json. Same scheduler-proc restart, sase's TUI notice, JSON envelope, and partial-success contract as disable. |
sase flag list |
-j, --json |
List registered flags, resolved values, provenance, saved vs effective state, beads, and due state. |
sase flag new |
<key>, --when-enabled, --when-disabled, --remove-when, -d/--description, -k/--kind (beta/sunset), -r/--remove-by, -z/--size |
Create a flag task bead and print the registry entry to paste. |
sase flag show |
<key>, -j/--json |
Show one flag's full decision, saved value, bead thresholds, diagnostics, and call sites. |
Unknown keys on enable/disable exit 2. Store or scheduler-restart failures exit
1; JSON then has "ok": false with the preference still recorded under mutation.
--json emits one versioned document with separate mutation and restart objects.
Neither command edits portable config files.
A non-empty human sase flag list ends with a compact statistics footer: how many flags
were rendered and which kinds they use, how many resolve on versus off, how many
decisions came from a layer above the registry default, and whether any loaded removal
bead is soon or due. Homogeneous kind or on/off values fold into the count head. For
example:
3 flags · 2 beta 1 sunset · 2 on 1 off · 1 overridden · ⧗ 1 soon ⧗ 1 due
--json keeps schema version 1 and does not include this footer.
new requires a SASE-managed checkout because the registry lives in this source tree.
--when-enabled, --when-disabled, and --remove-when are required and each accepts
@<path>. -k/--kind is beta (default; off) or sunset (on). There is no --scope.
sase file-hook¶
With no subcommand, sase file-hook delegates to sase file-hook list. The list shows
each valid effective hook's name, description, command,
project/sidecar/producer/glob/operation filters, timeout, and contributing config
source. Invalid and duplicate hooks are already excluded with configuration warnings, so
this is the runtime-effective view rather than a raw config dump.
| Form | Flag | Description |
|---|---|---|
sase file-hook list |
— | Render the effective hook table. |
sase file-hook list --json |
-j, --json |
Emit machine-readable hook records (schema_version: 4, including filters.producers). |
sase file-hook history |
-n, --limit |
Show recent producer audits (dispatched, unmatched, or failed). Default 20; 0 means all retained. |
sase file-hook history --json |
-j, --json |
Emit machine-readable producer audits. |
sase file-hook show AUDIT_ID |
-j, --json |
Show one producer audit by exact id or unique prefix. |
See file_hooks for matching, merge, execution, producer audits, and
notification behavior.
sase version¶
sase version reports the local runtime that the current sase process is using. It
does not query PyPI, GitHub, or latest available releases. The inventory always includes
the host sase package and the required sase-core-rs Rust core distribution, then
adds installed SASE plugin packages discovered through SASE entry points, SASE console
scripts, or sase-* distribution names.
The default human output is a compact runtime panel plus a package table with role,
effective version, and code directory. Development checkouts use PEP 440 local versions
such as 0.1.2+4.g26c39e004 or 0.1.2+0.g26c39e004.dirty. Editable installs prefer
source metadata over stale installed distribution metadata, while --verbose and
--json expose both values for auditability.
| Flag | Values | Default | Description |
|---|---|---|---|
-j, --json |
flag | - | Emit a stable JSON object with schema_version: 1, runtime, and packages. |
-v, --verbose |
flag | - | Include install type, dist/source versions, git metadata, and plugin signals. |
sase var¶
sase var inspects and publishes SASE agent output variables. Agents publish values
with sase var set, which merges named JSON-shaped values into the current run's
agent_meta.json["output_variables"]. sase artifact create also publishes the
SASE-managed artifacts list there. The stored values appear in sase's TUI Agents-tab
OUTPUT VARIABLES metadata panel, Telegram agent-completion messages, indexed agent
history, and downstream %wait prompt contexts. Later agents that wait on a producer
load that producer's stored values when they start and can render them through the
agents Jinja dictionary in prompts and macro workflows.
With no subcommand, sase var prints a delegation notice and runs sase var list.
| Form | Flags / arguments | Description |
|---|---|---|
sase var get |
-c, -f pretty\|json |
Show the current agent's output-variable snapshot from SASE_ARTIFACTS_DIR. |
sase var get '<AGENT_NAME>' |
-c, -f pretty\|json, -p, -H |
Show the newest exact-name historical snapshot. Quote the wrappers. |
sase var get SELECTOR [...] |
-c, -f pretty\|raw\|json\|jsonl |
Resolve precise values by exact, wildcard, hood, and JSON-path selectors. |
sase var list |
-a, -k, -p, dates, value flags |
Discover keys and distinct typed values across indexed agent history. |
sase var set KEY=VALUE [...] |
positional assignments | Store one or more strings, splitting each assignment at the first =. |
sase var set KEY --value TEXT |
-v, --value TEXT |
Store one string verbatim, including spaces or newlines. |
sase var set KEY --value-file PATH |
-f, --value-file PATH |
Read one string as UTF-8 text; use - to read standard input. |
sase var set ... --json |
-j, --json plus a form above |
Decode supplied values as JSON strings, scalars, lists, maps, or null. |
sase var get has three modes. With no target, it reads the current agent's artifact
directory directly so recent writes are visible before the agent completes; this form
requires SASE_ARTIFACTS_DIR. With exactly one quoted <AGENT_NAME>, it searches
indexed history and returns the newest exact-name artifact. Quote the wrappers so the
shell keeps them intact, for example sase var get '<build>' --format json. Use
--project PROJECT to disambiguate repeated names across projects, where PROJECT may
be a display name or alias and may be repeated. --hidden includes hidden indexed
agents. An agent with no variables is an empty success; an unknown name is an error.
--format pretty renders the same readable block form used in sase's TUI, while
--format json emits the variable map as compact machine-readable JSON. Snapshot mode
rejects selector-only --format raw / jsonl and an explicit --limit.
sase var list is historical discovery. It groups indexed output variables by key,
orders keys by most-recent occurrence, and shows each key's distinct typed values plus
the contributing agent names. Repeated filters in one dimension are ORed; different
dimensions are ANDed. Major filters are:
--agent GLOB/-a: filter by agent-name glob.hood.*includes the hood root.--key GLOB/-k: filter by case-sensitive variable-key glob.--project PROJECT/-p: filter by project display name or alias; repeatable.--hidden/-H: include hidden indexed agents; visible history is the default.--since DATE/-sand--until DATE/-u: filter by launch time using the same date grammar as bead history filters.--value TEXT/-v: case-insensitive substring match over scalar text and canonical JSON.--value-json JSON/-V: exact typed JSON value match after output-variable normalization. It is mutually exclusive with--value.--limit KEYS[:VALUES]/-n: cap returned keys and distinct values per key. The default is20:5;0means unlimited for that dimension; a single number changes only the key limit.--reverse/-r: invert the normal recent-first key and value order.
sase var list --format pretty prints readable grouped blocks. --format json emits a
stable envelope with schema_version, the normalized query, limit metadata, and grouped
values. --format jsonl emits one compact JSON object per returned distinct value.
With one or more ordinary targets, sase var get retrieves exact values from indexed
history with selectors:
[SCOPE.]KEY[PATH ...]
KEY is a variable name or *. SCOPE may be an exact agent name, * for every
agent, or HOOD.* for a hood. Unscoped keys choose the newest matching occurrence.
Exact-agent selectors choose that name's newest artifact. Global and hood wildcard
selectors collapse repeated runs to the newest value per agent name. JSON paths follow
the selected value with [INDEX] for lists or ["KEY"] for map keys; dotted map
traversal is not accepted. sase var get build.* remains selector mode and is distinct
from snapshot-mode sase var get '<build>'.
Examples:
sase var get
sase var get --format json
sase var get '<build>' --format json
sase var get status
sase var get build.status --format raw
sase var get '*.status' --format json
sase var get 'research.*.report["summary"]'
sase var get results[0]
Selector --format pretty prints each match with attribution. --format raw prints one
value only and fails unless the selector resolves to exactly one untruncated match;
strings print as text, and structured values print as compact JSON. --format json
emits a stable envelope with schema_version, query, limit metadata, and matches.
--format jsonl emits one compact JSON object per match. Wildcard expansion defaults to
20 matches; --limit 0 is unlimited. --project is repeatable, and --hidden includes
hidden indexed agents. --limit applies only to selector wildcard expansion.
sase var set is the mutation command. It is agent-scoped and requires SASE_AGENT=1
and SASE_ARTIFACTS_DIR. Successful writes print the current agent name when known, the
stored keys, and the artifact directory.
Keys must be valid Jinja attribute identifiers ([A-Za-z_][A-Za-z0-9_]*). Values may
contain spaces, blank lines, newlines, and additional equals signs. The KEY=VALUE form
splits only on the first = and preserves everything after it; quote the whole
assignment when the shell would otherwise split it. --value likewise preserves exactly
the text supplied by the shell, including any trailing newlines. --value-file reads a
file or stdin and removes at most one trailing newline after normalizing line endings,
which makes files, pipes, and heredocs convenient without discarding an intentional
trailing blank line. With --json, JSON whitespace is ignored by the decoder and no
trailing newline is removed before parsing:
sase var set 'suites=["unit","integration"]' --json
sase var set cfg --json --value '{"retries":3,"enabled":true}'
sase var set report --json --value-file report.json
sase var set findings --json --value-file - <<'JSON'
[{"file":"src/a.py","severity":"high"}]
JSON
A value may be any JSON string, number, boolean, null, list, or map. Nested map keys may
be any non-empty NUL-free string. Map keys are normalized into sorted order for
deterministic storage and display; list order is always preserved. Structured values
reach Jinja as real containers, so consumers can use attribute/subscript access and
loops. Rendering an entire container with {{ agents["build"].cfg }} yields compact
JSON, while | tojson remains available for explicit JSON formatting.
An agent may store at most 256 variables. Each string leaf and nested map key is limited to 8,192 UTF-8 bytes; each variable is limited to depth 8, 1,024 total container-plus-leaf nodes, and 65,536 compact encoded JSON bytes. Numbers must be finite and integers must fit the signed 64-bit range. Every string converts CRLF and lone CR line endings to LF and rejects NUL characters. Invalid or oversized values fail visibly instead of disappearing during publication.
Multiple calls merge into the same variable map; later writes for the same key replace
earlier values. The command does not update prompts that have already started rendering,
so write variables before the producing agent completes and before dependent agents
unblock. Downstream prompts read each producer's variables from the single agents
dictionary keyed by the producer's stable agent name, e.g.
{{ agents["build"].report_path }} (or {{ agents.build.report_path }} for
identifier-safe names). Do not store secrets; output variables are persisted in
agent_meta.json and shown in sase's TUI and Telegram completion messages.
STOP is a reserved output variable. sase var set stays generic and stores it like
any other key, but repeat orchestration interprets it: setting STOP (e.g.
sase var set STOP=1) inside a %repeat / %r iteration stops the remaining repeat
slots, which finalize as successful skipped slots. null, false, numeric zero, empty
strings, empty lists, and empty maps are not-stop; string values 0, false, no, and
off are also not-stop case-insensitively after trimming. Any other value stops the
chain. STOP affects only repeat-chain continuation; ordinary %wait consumers read it
as a normal variable. See Repeat Directive in the macro
reference for the full cascade semantics.
sase telemetry¶
With no subcommand, sase telemetry prints a delegation notice and runs
sase telemetry list.
| Flag | Values | Default | Description |
|---|---|---|---|
| subcommand | cleanup-test-data, health, list, snapshot, status |
list |
Telemetry subcommand |
See docs/telemetry.md for the full CLI reference including per-subcommand flags.
sase logs¶
| Flag | Values | Default | Description |
|---|---|---|---|
daterange |
string | (required) | Date range to collect (e.g., -7d, 260318, 260315..260318) |
Supported date range formats:
- Absolute:
YYmmddorYYmmddHHMMSS - Relative:
-Nd(days ago),-Nh(hours ago),-Nm(minutes ago),0d(today) - Ranges:
START..END(e.g.,-7d..0d); single point means "from that point to now"
The run and event inputs at ~/.sase/logs/runs.jsonl and events.jsonl rotate
independently before appending a record would make a non-empty file exceed 2 MiB.
Rotation keeps one .1 generation and replaces an older backup; set
SASE_RUN_LOG_MAX_BYTES to another byte limit, or 0 for no size rotation. The current
sase logs collector reads only the active .jsonl files and skips malformed lines
there, so copy the matching .1 files separately when a support bundle must include
records from the previous generation.
sase editor¶
sase editor exposes JSON-over-stdin helper operations for editor integrations. It is
intentionally a fixed-operation bridge rather than a generic shell or filesystem API.
| Form | Input | Description |
|---|---|---|
sase editor helper-bridge agent-catalog |
JSON object on stdin | Return active/recent agents and derived session, clan, and tribe prompt targets. |
sase editor helper-bridge finalizer-catalog |
JSON object on stdin | Return configured %final completion rows from effective finalizer config without loading providers. |
sase editor helper-bridge macro-catalog |
JSON object on stdin | Return the structured macro catalog; accepts the same schema as the mobile macro-catalog helper operation. |
sase editor helper-bridge snippet-catalog |
JSON object on stdin | Return the composed sase's TUI snippet registry used by sase lsp and editor completion clients. |
sase editor helper-bridge vcs-repo-catalog |
JSON object on stdin | Return repository completion candidates for a VCS workflow and namespace. |
The finalizer-catalog request is {"schema_version":1} with an optional project
hint; unknown fields are ignored. The response is a compact status/message/entries
envelope. Malformed finalizer configuration returns status: error and no rows, and the
builder never loads provider code.
The agent-catalog request is just {"schema_version":1}; it has no project filter and
reads the cross-project agent snapshot. Ordinary rows are de-duplicated by name and
include status and project, with kind: agent for agents and kind: monitor for
monitors. When group metadata is available, additive session, clan, and @tribe rows
include kind, member_count, and display-ready detail; clan rows also include
aggregate status. For the 20 most recently active sessions, SASE tries to enrich
detail with the associated plan or bead's kind, structure, and title, and to add
Markdown documentation carrying the goal, phase list, or parent/task context plus a
session status footer. Unresolved or older sessions keep the plain member-count detail,
and enrichment failure never removes ordinary rows; see
Editor Integration: Helper Bridge for the exact fallback
ladder. The structured macro catalog includes insertion metadata (insertion,
reference_prefix, kind), typed argument metadata, display/source fields, and
definition_path when SASE can resolve a real file to jump to.
The snippet catalog uses the same source ordering as sase's TUI: macros marked with
snippet front matter plus user-defined ace.snippets, with ace.snippets winning on
trigger collisions. It also includes the generated initial-capital aliases (foo →
Foo), so editor completion and the native fallback expose exactly the same
trigger/template pairs as the TUI.
sase file¶
With no subcommand, sase file defaults to sase file list.
| Form | Flags | Description |
|---|---|---|
sase file list |
-p/--path, -t/--token |
Emit JSON filesystem completion candidates rooted at --path and filtered by the cursor token. |
sase file-history¶
With no subcommand, sase file-history defaults to sase file-history list.
| Form | Flags | Description |
|---|---|---|
sase file-history list |
none | Emit the recency-ordered file-reference history as a JSON array. |
sase file-history delete |
-p/--path |
Remove one entry from the file-reference history. |
sase gate¶
Create, inspect, answer, and manage durable command-backed gates and their optional gate-turn session members.
| Form | Principal flags | Description |
|---|---|---|
sase gate act |
-i/--id, -k/--kind, -o/--operation, -I/--input, -j/--json |
Run one repeatable declared action without answering the gate |
sase gate answer |
-i/--id, -k/--kind, repeatable -o/--option, -s/--set, -O/--option-input, -I/--input, -f/--feedback, -d/--detach / -D/--no-detach, -r/--resume / -R/--restart, -j/--json |
Answer one branch; --resume continues a partial option run or an unfinished coder handoff |
sase gate cancel |
<turn> or -i/--id -k/--kind, -r/--reason, -j/--json |
Cancel a pending gate or turn; launches no follow-up |
sase gate create |
-G/--turn, -n/--next, -f/--next-fork, -m/--next-model, repeatable -N/--next-output, -o/--origin-agent, -g/--turn-status, -E/--turn-stop-status, -p/--panel, -P/--panel-icon, -s/--sender, repeatable -t/--tag |
Create a gate from JSON on stdin, optionally handing an agent to a gate turn |
sase gate list |
-a/--all, -l/--agent, -p/--project, repeatable -s/--state, -n/--limit, -f/--format, -j/--json |
List pending gate turns newest first; --all includes settled turns |
sase gate show |
[turn] or -i/--id -k/--kind, -j/--json |
Show branches, inputs, actions, runtime state, workspace claim, and follow-up disposition |
sase gate wait |
-i/--id, -j/--json, -k/--kind, -t/--timeout |
Wait for a gate; exits 0 answered, 3 cancelled, 4 timeout, 5 failed |
Gate creation accepts one option query, a required complete primary_branch, an
options list with configurable labels, icons, default selections, and feedback modes,
plus optional groups metadata for AND-branch submit controls. It returns a stable JSON
descriptor with the request identity, owned paths, continuation/auto state, and hashes.
sase gate wait -j emits status, selected_option_ids, feedback, and
response_path; failed execution also emits failure and failure_recovery with the
error report path and exact safe recovery commands. A CLI timeout can shorten but not
extend the request timeout.
--turn creates a processless session member that owns the pending decision and ends an
agent-side creator's turn. --next is the default answered-branch follow-up prompt;
branch-specific policy may override or suppress it, and non-answered terminal branches
need their own explicit follow-up. --next-fork session|turn|none chooses inherited
context, --next-model pins the successor model, and repeatable
--next-output none|results|tail|file chooses what gate-command evidence reaches it.
Use sase gate wait from non-agent automation; an agent that created a turn-backed gate
must hand off rather than hold a provider process open. -p/--panel places the gate's
notification in a named notification-panel tab and requires -P/--panel-icon. For
sase gate answer, per-option input (-s/--set, -O/--option-input) and the legacy
shared -I/--input value are mutually exclusive; a gate turn answers through a
supervised background proc unless -D/--no-detach is given.
sase sudo¶
sase sudo creates typed sudo request gates and answers them from a controlling
terminal through SASE's dedicated sudo runner; see Sudo Requests. Every
subcommand requires the agent_sudo_requests beta flag and fails with a
feature_disabled error otherwise. With no subcommand, sase sudo defaults to
sase sudo list.
| Form | Flags | Description |
|---|---|---|
sase sudo request |
-j/--json, -o/--origin-agent |
Read one JSON request object from stdin, create the gate turn, and print its descriptor; an agent caller then hands off. |
sase sudo list |
-a/--all, -j/--json, -l/--limit, -p/--project |
List pending sudo gate turns; --all includes settled ones. |
sase sudo show <ID> |
-j/--json |
Show one sudo gate by gate id or turn ref. |
sase sudo answer <ID> |
-u/--run or -a/--approve, -d/--deny, repeatable -c/--command, -f/--feedback, -r/--resume or -R/--restart, -j/--json |
Approve and run, or deny, one sudo gate. |
--run and --approve both authenticate and run the reviewed commands; -c/--command
limits the run to selected reviewed command ids. Without --run, --approve, or
--deny, an interactive terminal asks Approve sudo request? [y/N], and a
non-interactive caller gets a tty_required error. Approval always needs a controlling
terminal, so the gate stays pending when none is available; denial runs no privileged
command and works headlessly. For a machine-targeted request, approval runs the target's
sase sudo exec over ssh -t. -r/--resume and -R/--restart are accepted for parity
with sase gate answer, but both currently rerun the full selected command set; neither
skips commands that already completed. To avoid repeating one, explicitly select only
the remaining command ids with -c/--command. The hidden sase sudo exec subcommand is
the target-side runner entrypoint.
sase lsp¶
Starts the macro language server over stdio for editor integrations.
SASE_MACRO_LSP_CMD can override the server command during development. Without that
override, sase lsp uses the current Python environment's bin/sase-macro-lsp, then
sase-macro-lsp from PATH, then the newer debug/release binary from a sibling
../sase-core checkout, then falls back to cargo run from that sibling checkout when
Cargo is available. Full editable-install SASE updates reinstall the server into the
uv-tool venv when pulled sase-core commits change.
| Flag | Values | Default | Description |
|---|---|---|---|
-V, --version |
flag | - | Print the macro LSP version and exit |
sase path¶
| Flag | Values | Default | Description |
|---|---|---|---|
name |
macros-dir, macros-schema, macros-collection-schema, config-schema |
(required) | Which path to print |
sase usage¶
Bare sase usage defaults to sase usage list. Listing is strictly offline: it reads
the cached subscription-usage store and never starts AXE, logs in, or calls a provider
CLI/API. Output is Rich on an interactive color terminal and automatically becomes plain
for redirected output, NO_COLOR, or a dumb terminal.
| Form | Flags | Description |
|---|---|---|
sase usage list |
-j/--json, -P/--plain, -p/--provider, -v/--verbose |
Render cached observations; provider filters are repeatable. |
sase usage refresh |
-b/--background, -j, -P, -p/--provider, -v/--verbose |
Submit or join bounded durable refreshes; foreground waits and then renders the updated cache. |
--json and --plain are mutually exclusive. Background mode returns after submission
and renders the receipt instead of the cache. Unknown providers are usage errors.
Collection requires llm_provider.usage_metrics.enabled; see
Subscription Usage.
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.
| Form | Flags | Description |
|---|---|---|
sase notify |
-s/--sender, -t/--tag |
Shortcut for sase notify list with default compact output |
sase notify +1 [ID] NOTE |
-k/--dedup-key, -s/--sender |
Append corroboration by exact/unique ID prefix or sender-scoped dedup key |
sase notify create |
-k/--dedup-key, -p/--plus-one-note, -S/--supersedes, -s/--sender, -t/--tag |
Create a raw notification or atomically append to a matching deduplicated row |
sase notify list |
-j/--json, -l/--limit, -q/--query, -t/--tag, -s/--sender, -u/--unread, -a/--all |
List recent notifications; -j includes plus_ones, count, and dedup_key |
sase notify show |
-i/--id, -f/--format (markdown or json) |
Show one notification by id; defaults to markdown |
sase notify rules |
-e/--explain ID, -j/--json |
List merged delivery rules in evaluation order, or explain one row's toast and sound |
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.
sase plan¶
With no subcommand, sase plan defaults to the sase plan list dashboard.
| Form | Flags | Description |
|---|---|---|
sase plan approve [PLAN] |
-n/--dry-run, -k/--kind, -m/--model, -P/--project, -p/--prompt, -w/--wait |
Approve a pending proposal or a plan with no live approval gate. |
sase plan / sase plan list |
-j/--json, -n/--limit, -s/--status, -t/--tier |
List pending proposals, approvals, and inferred rejected rows. |
sase plan propose <plan_file> |
- | Submit a Markdown plan file for approval from the /sase_plan skill. |
sase plan reject [PLAN] |
- | Reject one pending proposal by name, <shard>/<name>, path, plan: ref, planner agent, or notification ID/prefix. |
sase plan search [query] |
-f/--format, -k/--kind, -s/--status, -o/--source, -r/--sort, -A/--since, -B/--until, -n/--limit, -c/--color |
Search SDD and machine-local Markdown plans. |
sase plan show [target] |
-c/--color, -f/--format full\|compact\|json\|raw, -t/--target, -w/--wrap |
Resolve one plan by path, plan: reference, pending selector, slug, or bead id and render it. |
sase plan validate <plan_file> |
-e/--explain, -j/--json, -q/--quiet |
Validate using the plan's authored tier: tale or tier: epic schema. |
sase plan links ... |
see SDD repository and plan commands | List, refresh, repair, or validate SDD prompt/plan links. |
sase plan list prints a Rich dashboard by default and emits a stable JSON projection
with summary, proposed, approved, and rejected keys when -j/--json is set.
Repeat -s/--status with approved, proposed, or rejected to render or serialize
only those sections; unrequested JSON section keys are omitted, while summary counts
continue to describe the full collected view. -n/--limit controls the maximum rows in
each Approved and Rejected history section (default 10, with 0 meaning unlimited).
Proposed rows are always shown in full, leading with the plan name (shortest unique
form, dim id_prefix below) and a ready-to-paste approve/reject hint line. -t/--tier
composes with both filters. Proposed --json rows carry the same name field alongside
id_prefix. The JSON summary includes status_filter, tier_filter, and a non-default
limit when applicable, plus approved_scan_truncated if a finite artifact scan may
have omitted older approvals.
Use the Proposed row's plan name as the selector for sase plan approve or
sase plan reject (the row's id_prefix still works); omitting the selector is valid
only when exactly one pending proposal exists. The Rejected rows are inferred from
archived proposal files that are not represented by current proposed or approved state,
so they are useful for history but are not actionable selectors. Omitting --kind uses
the plan's authored tier; explicit choices override it and tale/epic targets are
validated before the proposal is consumed. Approval kind approve runs the coder
without asking the runner to commit an SDD plan, tale commits the plan as an SDD tale
and then runs the coder, epic commits the matching SDD tier and launches the bead
follow-up, and commit records the approved plan in SDD without launching a coder. The
-m/--model flag applies to the follow-up agent; -p/--prompt adds extra coder
instructions only for the approve and tale paths. Use -w/--wait with
comma-separated agent names and bead=<id> entries to hold the approved coder or
launched epic phases until those dependencies finish. sase plan reject writes the
rejection response first, then attempts the same durable cleanup path as TUI no-feedback
rejection when the matching planner row is still discoverable.
sase plan search [query] scans plans in the resolved SDD store (the repo source) and
the machine-local ~/.sase/plans/ archive. The query is a literal case-insensitive
substring; omit it to browse and filter. --format accepts compact, full, json,
or markdown; --kind is repeatable and filters SDD-store artifacts to a plan tier
(tale or epic), prompt, or a configured document-sidecar role such as research
(an unknown kind is a usage error that lists the valid ones); --status is repeatable
and filters frontmatter status to wip or done; --source selects all, repo, or
local; --sort selects relevance, recent, or title (defaulting to relevance
with a query and recent without one); --since/--until accept YYYY-MM-DD,
YYYY-MM, YYYYMM, or relative durations such as 14d; and --limit defaults to
20, with 0 printing all matches.
sase plan validate <plan_file> infers the validation schema from the authored tier;
it no longer accepts -t/--tier. --explain prints tier-specific authoring guidance
before human results or adds it to the JSON envelope, while --quiet suppresses only
the successful human summary. See
Plan Frontmatter Schema and Validation
for diagnostics and exit codes.
sase artifact¶
sase artifact creates, discovers, inspects, resolves, opens, and repairs indexed
artifacts. Bare sase artifact delegates to sase artifact list, and
sase artifact-file remains a compatibility alias for the whole group.
sase artifact create is intended for code agents running with SASE_AGENT=1 and
SASE_ARTIFACTS_DIR set. It copies a generated file into persistent SASE artifact
storage and associates it with the current agent so the Agents tab can open it with A,
even after the agent has been dismissed and revived. -k/--kind accepts chat, plan,
image, markdown, pdf, or file and defaults to a kind inferred from the file
extension. -m/--move opts into removing the source after it is stored, -l/--label
sets the display label (default: the source file name), and -b/--bead [ID] attaches
the new file: reference to a bead (a bare -b uses the agent's SASE_BEAD_ID bead).
On success the command prints the artifact's id:, absolute source:, stored path:,
durable ref: (file:<id>), and var: artifacts[<index>] (bead: follows with
--bead). It also records the artifact in the agent's SASE-managed artifacts output
variable. Only create is agent-gated; every other artifact subcommand works outside an
agent run.
| Form | Flags | Description |
|---|---|---|
sase artifact create |
-b/--bead, -k/--kind, -l/--label, -m/--move, -p/--path |
Store one explicit artifact for the current agent |
sase artifact doctor |
-f/--fix, -v/--verify |
Report file-index and link-graph health; repair projections/renames and backfill enrichment with --fix, verify hashes with --verify |
sase artifact link add |
(positionals source_ref, relation, target_ref, why) |
Add or rewrite one typed artifact link |
sase artifact link import-indexes |
optional attestation, -a/--apply, -j/--json |
Preview or apply the attested, resumable legacy links/ to immutable-event cutover |
sase artifact link list |
-d/--direction, -j/--json, -l/--limit, -o/--origin, -R/--relation, -s/--source, optional ref |
List recent artifact links or one artifact's neighborhood |
sase artifact link migrate-notes |
-a/--apply, -j/--json |
Convert parseable historical RELATED: bead notes to typed links |
sase artifact link relation |
list / show <slug>, -j/--json |
Inspect the closed relation registry, direction, examples, and recommended endpoint kinds |
sase artifact link rm |
-R/--relation, (positionals source_ref, target_ref) |
Remove typed links between two artifacts |
sase artifact link suggest |
optional reference, -j/--json, -l/--limit |
Print write-free missing-link suggestions backed by deterministic evidence |
sase artifact list |
-a/--agent, -e/--explicit, -j/--json, -k/--kind, -l/--limit, -p/--project, -q/--query, -s/--since, -u/--unused |
List indexed artifacts newest-first |
sase artifact open |
(positional reference) |
Open a resolved reference with a kind-appropriate viewer |
sase artifact pane show |
-j/--json, (positional pane_id) |
Explain one configured Artifacts pane contract: every capability ON or OFF with its rule, declared fact, and reason; unknown pane ids fail |
sase artifact path |
(positional reference) |
Print the one absolute path a reference resolves to |
sase artifact prune |
-a/--apply, -b/--before, -g/--keep-generations, -j/--json, -k/--kind, -l/--limit, -m/--min-size, -p/--project |
Plan retention, then move selected automatic rows to restorable trash only with --apply |
sase artifact prune-runs |
-a/--apply, -i/--index-path, -j/--json, -l/--limit, -m/--keep-recent-months, -p/--project, -r/--projects-root |
Preview removal of old ace-run directories and empty out-of-range shards; --apply currently refuses and exits 1 because ACE-run deletion is preview-only |
sase artifact read |
-f/--format, -n/--lines, (positionals reference, reason) |
Print one artifact as audited context and optionally record a read link |
sase artifact reclaim |
-a/--apply, -d/--max-history-scan, -j/--json, -l/--limit, -p/--project |
Convert eligible stored automatic rows to verified VCS-backed rows only with --apply |
sase artifact show |
-j/--json, (positional reference) |
Show metadata, resolution, and consumption |
sase artifact stats |
-j/--json, -p/--project, -t/--top |
Report store economics, protection-source evidence, trash occupancy, and default-policy selection |
sase artifact trash / trash list |
-j/--json, -l/--limit |
List trash entries newest-first, flagging entries past the grace period |
sase artifact trash purge |
-a/--all, -j/--json |
Permanently delete entries past the grace period, or every entry with -a |
sase artifact trash restore |
-j/--json, (positional reference) |
Restore one entry's payload and complete index row by entry id or artifact ref |
list filters: -k/--kind is repeatable and accepts the artifact kinds above;
-l/--limit defaults to 50 and 0 means unlimited; -p/--project accepts a display
name, alias, or canonical key and exits 2 for an unknown project; -q/--query is a
case-insensitive substring match over label and paths; -s/--since accepts the same
DATE forms as sase plan search (YYYY-MM-DD, YYYY-MM, YYYYMM, or relative 14d /
3w / 2m); and -u/--unused shows only artifact files with no recorded file:<id>
consumption. Pretty output is a Rich panel with KIND, REF, LABEL, PROJECT (display
name), AGENT, SIZE, and CREATED columns; -j/--json emits every record field —
including sha256, size_bytes, and mime_type — plus the rendered ref.
show, path, and open accept canonical artifact references (file:, stitch:,
patch:, bead:, agent:, and document kinds such as plan:, research:, or
designs:). Historical commit:, chat:, bug:, and plans: aliases remain readable
for compatibility. Document kinds and file: preserve supported #L, #page=, and
#t= fragments; bead: and agent: reject fragments because they resolve to
regenerated pages whose anchors can drift. bead: resolves to the generated bead page
in the current project's beads sidecar, and agent: resolves to the generated
agents/<global-name>/README.md page in the current project's agents sidecar, so path
and open work for both after sase bead pages refresh or sase agent sync has
published the page. Bead and agent resolution is intentionally scoped to the reference
context's single project; foreign bead prefixes report unknown_project instead of
scanning every enabled project.
A bare default:<hash> or explicit:<hash> index id is accepted as sugar for
file:<id>. path exits 0 on success, 1 when the reference is malformed, missing, or
ambiguous (status and candidates go to stderr), and 2 for kinds with no filesystem
identity (stitch:, patch:, and historical commit: / bug:), pointing at show
instead. open treats historical commit: as non-viewable, opens historical bug: in
a browser, and currently opens a canonical stitch: at its resolved checkout; patch:
has no viewer path. show also reports consumption from the append-only
~/.sase/artifacts/consumption.jsonl ledger: consumption_count, consumed_by_agents,
consuming_agents, and last_consumed_at in pretty output, plus an additive
consumption object in -j/--json output. doctor exits 1 when it finds missing
enrichment fields, missing stored files, duplicate ids, unsupported schema versions,
malformed rows, digest mismatches, or an unhealthy artifact-link graph. Its link report
includes dangling refs, stale projections, durable-versus-aggregate counts, read-outbox
counts, derived coverage, and relation/origin totals; see
Artifact Links.
stats, prune, reclaim, and opt-in automatic retention use one shared protection
collector. It unions IDs found in persistent ProjectSpec and SDD text with canonical
fragment-free file: IDs recorded in the consumption ledger, then passes the
deduplicated set to the applicable planner. Stats keeps referenced, consumed, overlap,
and total protection counts distinct. Prune and reclaim are dry runs unless --apply is
passed and never select explicit or protected rows; automatic retention enforces the
same union after agent finalization when artifacts.retention.enabled is true. A
missing consumption ledger contributes no IDs; a present ledger that cannot be queried
appears in protection-source evidence and blocks destructive apply or skips automatic
enforcement.
Every removal prune, reclaim, and automatic retention perform routes through the
restorable trash under ~/.sase/artifacts/trash/: one directory per entry holding
entry.json (the complete original index row plus trashed_at, reason, and
size_bytes) and, for a byte-backed row, the moved payload file. Nothing else
hard-deletes. trash restore puts the payload back and re-inserts the index row; only
trash purge deletes permanently, and without -a/--all it deletes only entries older
than artifacts.retention.trash_grace_days. Because trashed bytes still occupy disk,
du does not drop until a purge runs; both apply summaries print the trash root, and
reclaim --apply says so outright. See
Store Lifecycle for the end-to-end workflow.
sase questions¶
| Flag | Values | Default | Description |
|---|---|---|---|
questions_json |
string | (required) | JSON string containing questions to ask |
sase agent¶
sase agent provides cross-project visibility into running agents and synchronizes
shared agent history. Subcommands:
| Subcommand | Flags | Description |
|---|---|---|
list |
-a/--all, -j/--json, -p/--project |
List running agents. -a includes DONE/FAILED agents (capped at 50 per project). -j emits a JSON array with a stable schema. -p limits output to a single project. |
search |
query words, -j/--json, -l/--limit, -p/--project |
Search the historical catalog with the Artifacts → Agent Boolean dialect. Bare search hides hidden and workflow-child rows and caps at 40; -l 0 or limit:all is unlimited. Explicit -l wins over a query limit: token. |
show |
<name> |
Render a full detail panel (prompt, reply, metadata) for a single agent by name. |
kill |
-n/--name |
SIGTERM a running agent by name. |
wait |
names or -a/--all, -i/--interval, -j/--json, -p/--project, -q/--quiet, -t/--timeout, -w/--wait-blocked |
Wait for agents, sessions, clans, or workflows to settle; exit status distinguishes failure, human blocking, timeout, and signals. |
hold |
create / list / release / run / show; selectors -n/--name, -t/--tribe, -H/--hood, -f/--future, -p/--pending; -s/--scope, -T/--ttl; list/show -j; release/show -k/--key |
Arm and manage durable admission holds that keep matching WAITING/QUEUED agents, later launches, and undispatched procs from starting. Bare hold defaults to list. See Agent holds. |
restart |
<name>, -j/--json, -m/--model, -n/--dry-run, -y/--yes |
Stop one agent and relaunch its stored prompt under the same name. Planning precedes mutation; dry-run previews and JSON skips confirmation. |
drain |
<provider>, -j/--json, -l/--limit, -m/--model, -n/--dry-run, -y/--yes |
Replan and relaunch agents stranded by a hard-disabled provider. Enabled and soft-disabled providers are refused. |
tribe |
set / unset / list |
Manage the user-defined tribe on an agent (used by the Agents tab tribe side panels). tribe set -n <agent> -t <tribe> replaces any prior tribe; tribe unset -n <agent> clears it; tribe list [-n <agent>] prints tribes as JSON (filtered when given). |
archive |
rebuild-index / verify |
Maintain the dismissed-agent bundle summary index under ~/.sase/dismissed_bundles/. verify exits non-zero if rows are stale or missing. |
artifacts |
layout status / migrate / verify / rollback, -P/--project, -p/--projects-root, -i/--index-path, -j/--json; -m/--manifest on migrate/verify/rollback; migrate also -d/--dry-run, -l/--limit |
Inspect and migrate the physical ace-run artifact directory layout. status reports flat and sharded directory counts, migrate moves flat timestamp directories into day shards (with --dry-run, it only writes or prints the manifest), verify checks current or manifest-backed state, and rollback reverses a manifest-backed migration. |
index |
status / rebuild / verify / gc / vacuum, -i/--index-path, -p/--projects-root, -j/--json; vacuum accepts -a/--apply; gc accepts --dry-run and -r/--purge-revived-bundles |
Maintain the persistent artifact index. status is a lightweight check, verify compares source artifacts, gc rebuilds the index and dismissed projection (--dry-run only reports its reconciliation counts, and -r first purges dismissed bundles for already-revived agents), and vacuum reports or reclaims SQLite freelist pages. |
names |
migrate-auto, purge-local-state, -f/--force, -j/--json; purge-local-state also takes -a/--apply |
Maintain the permanent agent-name registry. migrate-auto runs the historical generated-name namespace migration; --force reruns it after the completion marker exists. purge-local-state removes every locally materialized import closure regardless of transport or source machine (artifacts, chats, dismissed bundles, identities, historical import staging, incoming-cache directories, and receipts); a dry run unless -a/--apply is given. -j/--json emits a machine-readable summary for any subcommand. |
prompts |
list / migrate / show / validate, -p/--project, -m/--month, -j/--json; migrate -w/--write, validate -s/--show-warnings |
Inspect the canonical agents-sidecar prompt archive. list browses prompts/<YYYYMM>/; show prints the archived Markdown document; validate checks headers, artifact bytes, manifests, and plan links; and migrate moves historical plans-sidecar prompts only with --write. |
sync |
-c/--check, -d/--drop-retired, -g/--repair-digests, -j/--json, -m/--repair-manifest, repeatable -p/--project, -q/--retry-quarantined, -r/--refresh, -t/--retry-retired |
Pull enabled agents sidecars, publish locally commit-eligible hoods, restore deferred prompt archives, push, and drain Referenced By write-backs. Plain --check uses cached status without Git or artifact scans; --check --refresh fetches and recomputes status. Mutating sync can retry quarantined requests, revive a project's retired agent-publication requests (--retry-retired requires --project), or drop retired hood and back-reference requests. --repair-digests re-signs drifted locally owned hood-snapshot file references and --repair-manifest rebuilds missing owner-manifest entries, each instead of a normal sync. See Agent Hood Synchronization. |
Agent-index paths default to ~/.sase/agent_artifact_index.sqlite and
~/.sase/projects. sase agent index vacuum is a dry run unless -a/--apply is
supplied. It reports freelist pages left by normal SQLite deletes and dismissed-identity
row counts. Apply runs SQLite VACUUM to rebuild the index file and reclaim that free
space; it never removes or alters an index row. -i/--index-path selects another index
and -j/--json emits the machine-readable report.
sase agent persist-cleanup, sase agent persist-directive, and sase agent revert
are durable-operation entrypoints that sase's TUI runs as procs; they read their details
from the -Q/--request-path sidecar and are not meant for direct use.
sase agent-cli¶
sase agent-cli inventories the supported coding-agent CLIs, installs the ones whose
provider declares an install script, and updates the ones whose install method can be
identified safely. With no subcommand, it defaults to sase agent-cli list. See
Agent providers for the per-install-method
update behavior.
| Subcommand | Flags | Description |
|---|---|---|
list |
-j/--json, -o/--offline, -r/--refresh, -v/--verbose |
List every supported CLI with its binary, installed and latest versions, install method, and update marker. -v adds executable paths, docs URLs, and probe errors. |
update |
<name> ..., -a/--all, -j/--json, -n/--dry-run, -o/--offline, -r/--refresh |
Update named CLIs or, with -a, every installed one. -n prints exact commands and skip reasons without running them. Passing neither names nor -a is a usage error. |
install |
<name> ..., -f/--force, -j/--json, -n/--dry-run, -o/--offline, -r/--refresh, -y/--yes |
Install named CLIs from the install script their provider declares. Shows the URL, SHA-256 digest, exact shell-free command, env overlay, and target directory, then requires -y or an interactive confirmation. -n prints that plan and executes nothing; -f reinstalls an already-installed CLI. |
-o/--offline uses only cached latest-version data and never contacts the network;
-r/--refresh bypasses that cache.
sase chat¶
sase chat discovers and inspects saved agent chat transcripts. With no subcommand, it
defaults to sase chat list. Subcommands:
| Subcommand | Flags | Description |
|---|---|---|
list |
-j/--json, -l/--limit, -m/--machine, -P/--provenance, -q/--query |
List recent transcripts with sync provenance. -j emits the stable JSON shape consumed by the /sase_chats skill. |
show |
-n/--agent, -p/--path, -b/--basename, -f/--format |
Show one transcript by agent name, path, or basename. --format accepts raw, resume, or response. |
Directory Sharding¶
Older SASE layouts wrote many agent artifacts (chat logs, notifications, workflow state,
etc.) directly under ~/.sase/<kind>/. After a few months of heavy use those
directories can accumulate tens of thousands of files, which slows down filesystem walks
and makes ls-style inspection painful.
Current high-volume writers use a YYYYMM/ shard inside each managed artifact directory
(keyed by the current month). Readers transparently merge sharded and non-sharded files,
so the layout is backwards-compatible - existing unsharded files at the top level are
still found and the layout is fully read/write compatible across both forms.
Prompt history uses its own monthly JSON shard directory,
~/.sase/prompt_history/YYMM.json, because each shard stores a bounded JSON list rather
than one file per prompt. Entries whose last-used timestamp cannot be parsed are kept in
unknown.json. The legacy ~/.sase/prompt_history.json file is migrated into that
directory on first read or write when the shard directory does not already exist, then
preserved as a legacy-imported-<timestamp>.json.bak backup.
sase's TUI run artifacts also support a day-sharded physical layout under each project's
artifact root. Use sase agent artifacts layout status to inspect flat versus sharded
ace-run directories, migrate to move legacy flat timestamp directories into shards
while writing index aliases, verify to check the current or manifest-backed state, and
rollback -m <manifest> to reverse a migration when needed. Migration skips live
artifact directories with running.json, refuses existing targets, and can be previewed
with --dry-run.
Local State Cutover¶
sase migrate is a temporary, offline kit for the canonical-only local-state cutover.
It never runs automatically, no other SASE surface invokes it, and the whole command
group is deleted once the cutover completes. Reach for it only when you are deliberately
retiring legacy residue under ~/.sase and ~/.macros; nothing here is part of
day-to-day operation.
Every subcommand accepts -j/--json for a machine-readable object. Bare
sase migrate delegates to sase migrate list. Only backup, restore, run, and
resume accept -a/--apply. For those commands, --apply is the boundary for
changing the source or live root, not a promise that the default mode performs no
filesystem writes: plan persists control records, restore always creates a staging
copy, and path resolution may create the cutover control directories.
Where The Kit Writes¶
Backups, staged restores, and run journals live under the cutover backup root:
$SASE_CUTOVER_BACKUP_DIR when set, otherwise ~/cutover-backups. The root is created
with mode 0700 on first use. SASE rejects a root equal to or nested beneath ~/.sase,
~/.local/state/sase, or ~/sase. That check does not discover arbitrary custom
runtime roots, so if SASE_HOME or a checkout lives elsewhere, choose an unrelated
backup location yourself.
| Path | Contents |
|---|---|
backups/<backup-id>/payload/ |
The copied source tree, mirroring the backed-up root. |
backups/<backup-id>/ |
MANIFEST.json, SHA256SUMS, and provenance.json for that backup. |
restores/ |
Staged restores, before an apply swaps one into place. |
runs/<run-id>/manifest.json |
The planned operation, including any bound backup id. |
runs/<run-id>/journal.jsonl |
The append-only, fsynced record a resume replays. |
runs/<run-id>/run.lock |
The bounded lock run and resume take. |
runs/<run-id>/receipt.json |
The final receipt written once an applied run finishes. |
A backup id looks like <host>-<YYYYMMDDTHHMMSS>-<random>; a run is addressed by its
run id.
The Operation Catalog¶
The catalog is fixed — sase migrate list reports it along with whether each declared
root is present on this machine.
| Operation | What it does | Backup required | Apply supported | Declared roots |
|---|---|---|---|---|
import-purge |
Wraps sase agent names purge-local-state behind a verified backup, then re-runs the import-state preview to verify. |
yes | yes | ~/.sase/agents_sync, ~/.sase/artifacts, ~/.sase/chats, ~/.sase/dismissed_bundles, ~/.sase/projects |
lock-residue |
Classifies code-swap lock files and refuses to archive any lock the current code still writes. | no | no (read-only) | ~/.sase/locks |
procs-residue |
Parses residual ~/.sase/tasks rows, reconciles them with canonical procs, and archives only a fully matched legacy tree. |
yes | yes | ~/.sase/tasks, ~/.sase/procs |
state-residue |
Archives the legacy agent tribe file, user_question, plan_approval, and legacy ~/.macros residue once canonical counterparts exist. |
yes | yes | ~/.sase/agent_tags.json, ~/.sase/plan_approval, ~/.sase/user_question, ~/.macros |
Each operation declares its own preconditions and rollback unit; list --json prints
them verbatim. lock-residue reports a classification and never mutates anything, so it
has no apply mode.
Backing Up And Restoring¶
Treat the kit's “offline” label as an operator requirement. Before capturing a backup, stop sase's TUI, AXE, agents, procs, and any other process that can write the source tree; the backup command does not stop writers or acquire a tree-wide lock. A safe sequence is:
- Stop writers, preview the backup, and then repeat it with
--apply(preferably with--secondary). - Create the operation manifest with the verified backup id.
- Run the manifest without
--applyto preflight it. - Repeat
runwith--apply, then useverifyandstatus. - Use
resume --applyonly for an interrupted applied run.
sase migrate backup ~/.sase # report what would be captured
sase migrate backup ~/.sase --apply --secondary /mnt/backup # capture, plus a second durable copy
sase migrate restore <backup-id> # verify, create a staging copy, and diff
sase migrate restore <backup-id> --apply # swap the staged copy into place
An applied capture checksums its file members and copies SQLite databases through the
database backup API rather than as raw bytes. Ordinary files are copied while the tree
is being walked, which is why stopping writers matters. The capture refuses to start
unless free space covers the measured size with 15 percent headroom. Without --apply,
it reports member counts, byte totals, and whether the destination passes the three
fixed root exclusions above; resolving that destination can still create the cutover
root and backups/ directory.
A restore first hashes regular and SQLite payload files against the hashes stored in
MANIFEST.json. The backup also contains SHA256SUMS, but restore does not currently
read that file. After verification, restore always copies the payload to a new staging
path under restores/ and reports the diff and ownership deltas against the live root,
even without --apply. With --apply, an existing live root is renamed to a sibling
<name>.pre-restore-<YYYYMMDDTHHMMSS> — never deleted — and the staged copy is moved
into its place. -r/--root targets a different live root than the one that was backed
up. The backup itself is never modified or deleted by a restore.
Planning, Running, And Verifying¶
sase migrate plan state-residue --backup-id <backup-id> # write runs/<run-id>/manifest.json
sase migrate run <manifest> # preflight only: digests, conflicts, backup record
sase migrate run <manifest> --apply # execute, journaling every step
sase migrate status # every recorded run and its journal state
sase migrate verify <run-id> # re-check post-conditions and fingerprints
sase migrate resume <run-id> --apply # continue an interrupted run
plan mutates no source data: it writes a manifest plus an initial journal record and
prints the manifest path. run without --apply checks source digests, conflicts, and
the bound backup record; a refused preflight can append a refusal to the journal. With
--apply, run executes under a durable journal and writes a receipt. resume replays
the journal, re-checks source digests while the run has not started applying, and
continues from the next resumable step. run and resume accept
-l/--lock-timeout-ms; the bounded run lock is acquired only in apply mode.
plan and list accept -r/--root and -d/--home to resolve operation roots
against a SASE home other than the live one, which is how the kit is rehearsed against a
copy before it is pointed at real state.