Directive syntax and completion¶
This page documents directive syntax, completion, and model or dispatch settings.
Directives¶
Directives are in-prompt tags with a % prefix that modify agent runner behavior. They
are extracted and stripped from the prompt before further processing.
Moved to Directive syntax and completion. Moved to Directive syntax and completion. Moved to Directive syntax and completion. Moved to Directive syntax and completion. Moved to Directive syntax and completion.
Syntax¶
Directives use the same argument syntax as macro references:
%model:claude-sonnet # Colon syntax
%model(claude-sonnet) # Parenthesis syntax (single value only)
%model:`claude-sonnet-4` # Backtick syntax (for values with special chars)
%model:codex/o3 # Provider/model syntax — switches both provider and model
%m:agy/gemini-3.7-flash-high # Provider/model value with a stable Antigravity slug
%model:opencode/anthropic/claude-sonnet-4-5 # Nested provider/model syntax
%model:muse/muse-spark-1.3 # Meta Muse Code — never auto-detected from PATH
%model:grok/grok-4.7 # xAI Grok Build — never auto-detected; also in alias selectors
%model:@fast # Configured/implicit model alias; Model shows ← @fast
%model(opus, medium=codex/gpt-6.1-sol) # This agent uses opus; medium follow-ups use Codex
%model(medium=@large) # Leave this agent on its normal launch model; route medium follow-ups through @large
%effort:xhigh # Set the reasoning-effort level for this prompt
%e:xhigh # Same, using alias
%effort:%{medium | high | xhigh} # Fan out directive values
%model:opus@xhigh # Model + reasoning-effort suffix (alias: %m:opus@xhigh)
%dispatch:apollo # Launch on the enrolled machine alias "apollo"
%{%m:opus@xhigh | %m:sonnet@low} # Per-branch effort via fan-out
%id:reviewer # Short-form
%i:reviewer # Same, using alias
%i(reviewer, session=parent) # Attach parent--reviewer to parent's session
%i(@, session=parent) # Attach the next free feedback/Q&A suffix
%id(worker, clan=research) # Derive research.worker and join clan research
%id(!worker, clan=research) # Same derived name, with forced reuse
%id(reviewer, tribe=review) # Name reviewer and assign it to tribe @review
%id(tribe=review) # Auto-name the agent and assign tribe @review
#tribe:review # Built-in shorthand for %id(tribe=review)
%id # Bare — auto-generates a unique name
%id:!reviewer # Force reuse by wiping the previous owner
%clan:research.{@1} # Declare a keyed template clan; this member uses a full hood-qualified id
%c:research.{@1} # Same, using alias
%id:research.{@1}.cdx # Keyed template; same key resolves together across the dispatch
%id(image, clan=research.{@1}) # Join the same keyed clan and derive research.{@1}.image
%id:outer.{@shared!}.lead # Already-qualified key; share deliberately across nested swarms
%clan(research, tribe=review) # Declare a new clan in tribe @review
%clan(research, summary="Audit the authentication boundary") # Store a launch-time clan description
%clan(research, summary_script=./describe-clan) # Generate that description with an executable
%clan(research, summary_script=[[sase_clan_summary_plan "plans/research plan.md"]]) # Pass quoted script argv
%clan:research:: Audit the authentication boundary. # Text block; ends at the next top-level % or # line
%wait:agent1 # Wait for agent1
%w:agent2 # Wait for agent2 (alias)
%wait # Bare — waits for the most recently named agent
%wait:agent1,agent2 # Multi-value: equivalent to two separate %wait: lines
%wait(agent1, agent2) # Same, paren form
%wait(agent=agent1) # Named form of an ordinary agent wait
%wait:@review # Wait for the next completed @review agent or clan
%wait(bead=<open-bead-id>) # Wait for a bead in this project to close
%wait(hood=research) # Wait for every current member of one hood
%wait(agent1, bead=<open-bead-id>, hood=research) # Require every condition
%wait(planner, for_epic=true) # Same as %wait(planner); agent waits follow launched epics by default
%wait(planner, for_epic=false) # Release when planner finishes, even if it launched an epic
%wait(time=5m) # Wait for 5 minutes before starting
%wait(time=1h30m) # Wait for 1 hour 30 minutes
%wait(time=90s) # Wait for 90 seconds
%wait(time=1430) # Wait until 14:30 today (wraps to tomorrow if past)
%wait(time=260415/0900) # Wait until 2026-04-15 at 09:00
%wait(agent1, time=5m) # Wait for agent1, then a 5-minute floor
%queue:3 # Use a per-launch capacity budget of 3
%q:3 # Same, using alias
%queue(capacity=3) # Same, named form
%q:1.5x # Use 1.5 × the effective capacity budget
%q(1.5x, w=0.25) # Multiplier capacity with a quarter-unit claim
%queue(capacity=1) # Run-alone barrier for a default-weight launch
%queue(priority=1) # Join the runner queue ahead of larger priorities
%q(p=1) # Same, using the p= alias for priority=
%queue(weight=0.25) # Claim one quarter runner-capacity unit
%q(w=2) # Same field using the w= alias; this claims two units
%queue(capacity=5, priority=20, weight=2) # Capacity, priority, and weight together
%wait(agent1, time=5m) %queue(capacity=1) # Dependencies, then time floor, then capacity budget
#t:5m # Shorthand for %wait(time=5m)
%hold:planner # Name selector for an admission hold
%hold:planner,reviewer # Colon list of name selectors
%hold:@nightly # Tribe selector (positional @)
%hold(pending) # Freeze the WAITING/QUEUED agents in scope
%hold(future) # Fence launches submitted after the hold
%hold(hood=research) # Hood selector
%hold(tribe=nightly) # Tribe selector, keyword form
%hold(pending, future, ttl=90m, scope=host) # Widest form, explicit in the text
%repeat:3 # Run the prompt 3 times
%r:5 # Same, using alias
%{#review | #test} # Brace shorthand: branches split on top-level `|`
%alt(#review,#test) # Long form: same two variants, comma-separated
%(#review,#test) # Parenthesized shorthand for the same two branches
%{sec=#review | perf=#test} # Named branches become child name suffixes
%{extra instructions} # Single branch: split into with/without variants
%auto # Auto-resolve plan, epic, and question gates using the default
%a # Same, using alias
%auto:plan # Auto-approve tale plans only; epic plans wait for review
%auto:tale # Same as :plan: auto-approve tale plans only
%auto:epic # Auto-approve epic plans only; tale plans wait for review
%auto:manual # Manual: auto off, exactly as if no %auto were present
%auto:off # Same as :manual
Model names containing spaces or parentheses must use the quoted parenthesis form (for
example, %m("provider/Model Name (Variant)")); colon syntax cannot express those
values.
The %clan directive and the clan= keyword on %id request execution-neutral
membership in a rootless parallel agent clan. Adding membership does not change the
model, waits, fan-out, spawn order, VCS/project context, or workspace behavior; it only
adds clan metadata and strips the directive before model execution. The clan name is a
reserved container, never an agent. %clan is a create-only declaration, requires an
explicitly hood-qualified member name, and may appear for a resolved clan in only one
prompt per launch. It errors if that clan already exists. Use
%clan(<name>, tribe=<tribe>) to assign one authoritative tribe to the generation.
Every other member uses %id(<id>, clan=<clan>), which derives <clan>.<id> and joins
the newest generation or creates the clan implicitly without a tribe. The join form
cannot be combined with %clan; joining a clan also joins its tribe. A member segment
may fan out, and identical raw clan templates in one batch resolve to the same
generation. See Agent Clans, Sessions, and Tribes for the full
launch, wait, display, and cleanup contract.
The declaring %clan can also attach one launch-time description with summary=,
summary_script=, or the %clan...:: text-block shorthand. These forms are mutually
exclusive, and clan joiners cannot replace the description. The :: form requires a
following space or end of line and captures up to the next top-level line beginning with
a % directive or # reference. The captured text becomes metadata rather than member
instructions; use the explicit summary= form when the work prompt follows immediately.
Script-backed summaries may run synchronously during directive extraction and again
after the primary workspace, sidecars, and linked repositories are prepared. Both
attempts share the same 20-second, non-fatal contract and clan/epic environment; the
last successful non-empty output wins. Scripts must be read-only and idempotent because
runner re-exec can repeat them. summary_script= may contain shell-style quoted argv
(without invoking a shell), and sase_clan_summary_plan PLAN_REF renders a valid tale
or epic with the shared PLAN-lane layout; without PLAN_REF, it uses
SASE_EPIC_PLAN_REF. Scripts inherit the environment available to each attempt,
including epic SASE_EPIC_PLAN_REF, SASE_EPIC_PLAN_SNAPSHOT, SASE_EPIC_BEAD_ID,
SASE_PHASE_BEAD_ID, and SASE_EPIC_CLAN_TRIBE, while SASE overrides the clan identity
variables. The snapshot is an absolute project-scoped best-effort copy that the built-in
epic summary script uses as a guaranteed-local fallback after the normal checkout
candidates; the original reference remains authoritative for display and metadata. See
Launch-time clan summaries for the
complete ordering, execution, and persistence details. Re-creating a clan with a bare
%clan(<name>) or a generation-creating %id(<id>, clan=<name>) inherits the
remembered tribe and re-runs the remembered summary script (falling back to the
remembered text) when no explicit values are given.
The %model directive also supports automatic provider resolution: known model names
(e.g., opus, o3, qwen3.6-plus, muse-spark-1.3) are automatically mapped to their
provider. See Per-Prompt Provider Switching for
the full model-to-provider mapping. sase's TUI and the macro LSP complete %model: /
%m: values from the same model catalog used for provider resolution. The inserted
value is a canonical model name, a configured alias, or a qualified provider/model
value selected from a provider-scoped menu; provider short aliases are only
filter/display hints.
Model aliases are listed beneath the concrete model names. Each alias row shows its kind
(role or custom), the PROVIDER(model) target it currently resolves to — with an
@ <effort> suffix when the alias carries one — and its provenance (configured,
implicit → @fallback, override, plus a · pool 2/3 chip for round-robin selectors).
Typing @ right after the colon (%m:@) narrows the menu to aliases only; a bare
partial such as me still matches @medium through its bare name, but always after the
model rows. sase's TUI menu reflects active temporary alias overrides, while the LSP's
catalog is a launch-time snapshot that does not — restart the LSP to pick up config
changes, and use sase's TUI Launch Control (,m) to inspect
live override state.
Moved to Directive syntax and completion. Moved to Directive syntax and completion. Moved to Directive syntax and completion. Moved to Launch directives. Moved to Launch directives. Moved to Launch directives. Moved to Launch directives. Moved to Launch directives. Moved to Launch directives. Moved to Directive composition and fan-out. Moved to Directive composition and fan-out. Moved to Directive composition and fan-out. Moved to Directive composition and fan-out. Moved to Directive composition and fan-out. Moved to Directive composition and fan-out.
Supported Directives¶
See the generated directive catalog for supported names, aliases, syntax forms, argument summaries, and descriptions.
Agent identity uses %id or its %i alias. The retired %name and %n prompt
directives are not launch aliases. Using either as a top-level directive now raises a
migration error that points to %id / %i and, for clan membership, the
%id(<id>, clan=<clan>) form. %id's bead= keyword may name a closed bead; the
launch attaches without reopening it. See
Bead Claim Lifecycle.
The retired %tribe and %t directives also raise a migration error. Use
%id(<id>, tribe=<tribe>), %id(tribe=<tribe>) / #tribe:<tribe>, or
%clan(<clan>, tribe=<tribe>) according to the identity being tagged.
Tab Directive¶
%tab:<name> or %tab(<name>) places the launch's presentation root on a named agent
tab. There is no %tab alias and no keywords; the directive is single-valued, so a
second %tab fails with Duplicate directive '%tab' in prompt, even when both values
agree. Fan-out branches may use different tabs (%{%tab:a | %tab:b}) because each
branch is its own unit.
%tab:main is the default tab and is stored as absent. %tab:apollo is an ordinary
named tab (machine aliases are ordinary names). Reserved names fail with the core
messages:
local:machine tabs are derived; omit %tab to land on the local machine taball:there is no 'all' tab; use o in the grouping picker to see all tabs
%tab cannot be combined with a stand-alone %proc unit
(%tab cannot be used on a stand-alone %proc unit). It may be combined with
%dispatch; the dispatch prompt still carries the tab. Session follow-ups and clan
joiners inherit the root/generation tab; an explicit %tab that differs from the
inherited tab fails and names sase agent tab set. The directive is stripped from the
model prompt; the raw prompt keeps it.
Completion¶
The TUI and macro language server share the directive contract used by the parser. The generated directive catalog lists each directive's forms, aliases, arguments, and purpose. Completion adds context-sensitive values such as configured models, open beads, agent targets, and filesystem paths when those inventories are available.
Completion filters out non-repeatable keywords already present and keywords that
conflict with a selected value; manually typed input still goes through normal
validation. Structured %wait keywords are available in parenthesized form, while
queue-admission fields belong to %queue. If a dynamic inventory cannot be read,
static directive names and values remain available, while dynamic suggestions may be
missing or stale.
Static Conditional Segments¶
%if(should_run=true|false) is a static macro and multi-prompt inclusion directive. It
is not a typed-launch admission predicate and does not require a feature flag.
Always launch this segment.
---
%if(should_run=false)
This whole segment is omitted before nested macros, name allocation, waits, preview,
approval, queue capacity, workspace selection, or dispatch see it.
---
%if(should_run=true)
This segment is kept, and only the `%if(...)` line is removed from the model prompt.
The only supported parenthesized keyword is should_run=. After ordinary template
rendering, its value must be exactly true or false, case-insensitive; this accepts
Jinja-rendered True and False but rejects empty strings, numbers, null, misspelled
values, and still-unrendered template expressions. Unknown keywords, duplicate keywords,
duplicate active %if directives in one segment, malformed parentheses, and mixed
static/script forms are hard errors. The whole expanded batch is validated before any
surviving segment launches, so a bad condition in a later segment cannot partially
dispatch an earlier one.
Static %if acts like literal deletion of the disabled segment and its separator. A
false first, middle, last, sole, or all-disabled batch is valid; an all-disabled batch
launches nothing rather than falling back to a blank agent. Bare %wait binds to the
previous surviving unit exactly as if the omitted block had never been written, and
surviving named waits keep their normal resolution behavior. Static conditions inside
inline code, fenced code, directive-owned code bodies, or %macros_enabled:false
regions are literal text.
Inside a macro body, the condition is evaluated after that body's Jinja rendering, so a typed input can drive it:
Draft the report.
---
%if(should_run={{ include_images }})
Generate the supporting images.
A Jinja filter can also drive the condition:
%if(should_run={{ "grok" | provider_enabled }}) drops the segment whenever that
provider is disabled (see the provider_disabled / provider_enabled filters under
Jinja2 Integration).
provider_enabled defaults to the "any" mode, so the bare filter drops the segment on
either a soft or a hard disable. A segment that pins an explicit provider model should
normally gate on provider_enabled("hard") instead: a soft disable only steers alias
and pool routing away from the provider and never refuses an explicit launch, while only
a hard disable trips the launch guard. Use provider_disabled("soft") when a segment
wants to react to a soft disable specifically (for example, to adjust prose rather than
drop the segment):
Always launch this segment.
---
%if(should_run={{ "grok" | provider_enabled("hard") }})
This segment pins an explicit grok model, so it is dropped only on a hard disable.
In an macro swarm, a disabled segment is
dropped before any nested references in it expand. In an ordinary inline macro, a false
%if removes only that macro's own expansion; the prompt segment that references it
still launches with the rest of its text.
%if(should_run=...) cannot be combined with script admission syntax. For example,
%if(should_run=false):: and %if("test -f pyproject.toml", should_run=false) both
raise an error instead of dropping the segment silently. %if:value and %if+ are not
static forms and raise an error; use %if(should_run=...) for static omission.
Typed launch units¶
Script %if:: and %proc forms are always available; the directives are never
forwarded to the model. Static %if(should_run=true|false) works the same way and is
resolved before typed launch planning. Directive names used as prose are left alone:
stop un-admitted %if/%proc units and line-leading text such as %if is plain text are
literal. A token becomes directive-like only when the name is followed by (, :, or
+; the bare code form must be immediately followed by ::. Completion offers the
code-form rows and full Bash/Python snippet recipes, and the directive parser captures
these forms:
%if::
```bash
test -f pyproject.toml
```
The parenthesized process forms accept either a positional Bash body or one named
bash= / python= body:
%proc("just check")
%proc(python="print('ready')", timeout="20m", label="Preflight")
The fenced form puts options before :: and owns the fence that follows:
%proc(timeout="20m", idle_timeout="5m", cwd="docs", workspace="true")::
```bash
just docs-check
```
The script-admission form %if:: accepts exactly one closed bash or python fence
(intervening blank lines are allowed). %proc accepts one positional shell command, one
bash= or python= body, or the fenced :: form; only one script %if and one
%proc may appear in a launch unit. Code bodies are opaque: % directives, #
references, YAML frontmatter, Jinja, and $() inside them are preserved literally. The
parser strips each directive and its body from the model prompt. Each planned fanout
slot is one launch unit. A slot containing %proc becomes a process unit and cannot
also contain agent prompt prose; %id:<name> gives that process unit a proc name.
Execution depends on who initiated the launch:
- User-initiated submissions from
sase runand sase's TUI execute directly through durable typed admission. They freeze the same immutable typed plan and digest used after approval, then the admission coordinator waits for prerequisites, evaluates script%if::, and dispatches eligible units — agent units through the established agent launch path, and%procunits as nativenamed-procrecords with originprompt-proc. A direct user submission does not create a LaunchApproval notification. If a wait remains unresolved, thesase run/ sase's TUI launch proc can finish while a detached coordinator continues waiting; the coordinator writes a completion receipt and attempts a separate notification when admission settles. - Agent-initiated launches still require LaunchApproval. Approval freezes the typed plan and digest before the gate is shown. After approval, the same coordinator admits units.
For script %if::, exit 0 makes the unit eligible, exit 1 skips it, and any other
exit, signal, timeout, cancellation, or execution failure records a condition error. For
a selected managed project, admission waits first, then briefly claims and prepares a
numbered operational workspace, runs the predicate from that checkout, and releases the
claim before any dispatch. The launch request's source cwd is not used as a project
fallback after lease, materialization, or preparation failure; those failures record a
condition error. Home/unmanaged conditions have no claimable numbered workspace and
retain the explicit source-cwd behavior. A false or erroneous condition allocates no
runner, agent identity, proc identity, or model request. The default timeout is 10
seconds. The predicate receives a sanitized environment, a private HOME, and a
versioned JSON context at SASE_CONDITION_CONTEXT, but this isolation is not a security
boundary: the predicate runs with the SASE process's filesystem and network permissions.
%wait(agent=<name>) is also an alternate spelling of an ordinary agent wait on direct
launch paths. Logical-unit and proc-wait admission are active only in the LaunchApproval
coordinator. There, bare %wait targets the preceding unit, %wait(unit=unit-N)
targets an exact logical unit, and %wait(agent=<name>) or %wait(proc=<name-or-id>)
targets a matching unit in the same plan when possible, otherwise an existing agent or
proc. A typed dependency is satisfied when the target settles, even if it was skipped or
failed; that outcome is available in the %if:: context and does not automatically
cancel the dependent unit.
An eligible %proc unit dispatches natively once its waits and %if:: pass, no active
agent hold matches it, and any authored queue fields pass the
runner-capacity check described below: the admission coordinator reserves a named-proc
(lifecycle named-proc, origin prompt-proc; SASE also reads the proc-shell lifecycle
spelling) and starts its detached supervisor. The supervisor then acquires an operational
workspace lease when workspace is true, materializes the approved source as a private
0600 script, executes it by argv — /bin/bash --noprofile --norc <script> or the SASE
interpreter plus that script, never shell interpolation — and releases the lease through
the existing resumable settlement path on every terminal outcome. Execution and idle
timeouts begin when the child starts, not while waits, the condition, or the workspace
lease are pending. The child's environment inherits the detached supervisor's own
ordinary tool environment — PATH, HOME, locale, and toolchain configuration — rather
than a private hermetic one, so user-installed tools such as just, uv, and Cargo
resolve exactly as they do outside the proc. Parent agent, job, and artifact identity
are scrubbed from that inherited environment, along with any stale SASE_PROC_* sidecar
left over from an earlier proc, before the SASE interpreter directory is prefixed onto
PATH and only the current documented proc context (SASE_PROC_ID,
SASE_PROC_LOG_PATH, SASE_PROC_SESSION_ID, selected project, project file, and
workspace number) is added; the proc never sets SASE_AGENT or another agent-artifact
variable. The private script directory holds only the 0600 script and is not a
replacement user home, and this scrubbing is not a filesystem or network sandbox — the
child still runs with the supervisor's filesystem and network permissions. A stand-alone
%proc unit never allocates an agent runner slot, session, done.json, or finalizer
obligation.
A %proc unit may also carry %queue / %q fields:
%q(1, priority=4, weight=0.25)
%proc("just check")
Queue fields make dispatch wait for a runner-capacity check that uses the same capacity
budget, priority, FIFO, and deference rules as an agent launch (see the %queue rules
under Syntax). The check is not a claim: once dispatched, the proc holds no
runner capacity and never delays later launches. An omitted weight counts as 0 for the
check, so %q:1 on its own waits while running work fills that budget. A weight that
can never fit keeps the unit pending instead of failing it. A proc without queue fields
skips the check entirely. While it waits, the unit has no Agents-tab row and is not
counted as QUEUED; for a direct submission, inspect its state in
~/.sase/typed_launches/<request-id>/launch_admission/, where journal.jsonl records
the capacity message and receipt.json summarizes unit outcomes.
In project context workspace defaults to true and an optional relative cwd is
resolved beneath the leased checkout; workspace="false" opts out and requires an
ordinary cwd. Outside project context no lease is taken and an explicit cwd is
required — workspace="true" without a selected project is a hard error. %id:<name> /
%id(<name>) becomes the proc's optional bare proc_name (validated independently of
the agent-session -- naming convention); the canonical proc id is always allocated by
the proc store. Stop/kill is routed through the native proc-stop path and is responsive
in every phase — waiting, checking, acquiring the workspace, preparing the script,
running, and settling.
Typed admission preserves the complete identity binding for each agent unit, not only
the positional %id name: %id keywords (clan=, session=, tribe=, bead=), an
optional %clan declaration (tribe=, summary=, summary_script=), and force-reuse
! prefixes all survive planning. Dispatch reconstructs the equivalent %id and
%clan directives, then the existing model/effort/auto/final/hide/wait-runner
directives, and still omits admission-only %if:: and logical dependency waits. Each
unit also retains its own workspace reference and %dispatch machine target. An
approved or coordinator-replayed remote unit is therefore dispatched to that machine
from its resolved project workspace instead of silently falling back to a local launch;
mixed local and remote units route independently.
Keyed {@<id>} agent-name markers resolve once across the complete expanded typed batch
before it is split into durable logical units. The concrete tokens are stored on the
immutable plan, so a delayed or restarted coordinator never reallocates the same key
independently per unit. Literal markers inside fenced code and macro-disabled regions
stay unresolved, matching ordinary launch-time keyed-marker rules.
Coordinator restarts replay the journal: a persisted terminal condition result is not re-run, a reserved proc id is not duplicated, and agent/proc dispatch uses the stable request fingerprint written at approval. CLI and sase's TUI notifications report the same counts the receipt stores — total, eligible, launched, skipped, condition errors, and launch errors.
Stand-alone named procs created this way appear in sase's TUI Agents tab as their own
top-level rows (never nested under an agent session), each marked with a ▣ glyph
alongside its proc name or short proc id, an optional label, a Bash/Python language
badge, the current phase/status, elapsed time, and project. Panel titles report a
separate ▣<count> chip for stand-alone procs next to the ordinary agent-status chips;
a stand-alone proc never changes agent runner, unread, clan, or session counts.
Selecting a row opens a NAMED PROC detail with status/phase timeline,
project/workspace/cwd, language, code digest and safe preview, waits and condition
result, timeouts, and a bounded live-log tail — never the private script, the
SASE_CONDITION_CONTEXT file, or unbounded output. The same rows and details are
visible from the Procs pane.
Equals model shortcuts¶
sase's TUI and the macro LSP also complete leading =alias and ==model tokens into
canonical %m: expansions through shared Rust filters and edit plans. Typing = in an
macro-aware editor is an LSP completion trigger; manual completion works in the same
valid equals token. sase's TUI-only ace.prompt_completion.auto_directive_menu setting
does not change an editor client's trigger policy.
A marker is recognized only at prompt offset zero, at the start of a logical line, or
immediately after an ASCII space, with the caret after the equals marker. Escaped or
embedded equals signs, Markdown-style =text= / ==text== marker pairs, paths, inline
and fenced code, disabled prompt regions, frontmatter, Jinja, placeholders, and
directive-owned input are excluded, matching sase's TUI. The old *alias and **model
spellings are ordinary prompt text. The =alias shortcut lists only effective built-in,
plugin, and user aliases (implicit_alias and user_alias) from the same catalog
%model: uses, in catalog order, with case-insensitive prefix matching. The ==model
shortcut lists concrete model rows only, including provider-qualified matches such as
==claude/fa.
Accepting a row such as @large replaces the whole live =query token, including text
to the right of a mid-token caret, with %m:@large. Accepting a concrete model such as
gpt-6.1-sol replaces the whole live ==query token with %m:gpt-6.1-sol. At prompt
or line end the expansion appends one ASCII space; before a tab it appends none; before
an existing ASCII space it preserves the rest of that whitespace run and leaves the
caret after the first space. When the trigger's --- segment already holds a standalone
%model/%m directive, accepting instead deletes the shortcut token and rewrites the
earliest such directive in place with the selected value, removing any further
standalone model directives in the segment so exactly one remains; the caret lands at
the end of the rewritten directive. Directives inside alternation bodies (%alt(...),
%(...), %{...}), other --- segments, and literal/definition regions are never
targets, and a trigger inside an alternation expands at its own token without touching
sibling branches or outside text. A valid shortcut with no matching rows returns an
empty shortcut list rather than unrelated completion. Unaccepted equals text has no
launch-time meaning.
The LSP reads the launcher-materialized catalog snapshot, so restart the editor session
after alias or provider config changes. sase's TUI live prompt bar can additionally
overlay temporary alias overrides for =alias; see sase's TUI
model shortcut and the
editor integration notes.
Provider rows such as claude/ and opencode/ are listed after model and alias rows in
the broad %model: menu. Accepting one drills into that provider and reopens completion
for qualified values such as claude/opus; typing the slash directly behaves the same.
The provider scope splits only on the first slash, so %m:opencode/anthropic/ narrows
OpenCode's nested anthropic/... model names. Hidden providers stay out of completion
menus even though their explicit provider/model values remain routable.
When the final %model value was written as @<alias> — including a macro-expanded
reference such as %model:@#agy_flash — SASE records the expanded bare alias name in
agent_meta.json and renders it after the resolved model as ← @<alias>. Concrete
values such as %model:claude/opus and literal values such as model:`@text do not
get this chip. The chip is the alias named at launch, not the alias's current target.
Remote Dispatch¶
%dispatch:<alias> and %dispatch(<alias>) route one launch to an enrolled,
non-quarantined remote machine. The directive accepts exactly one machine alias, has no
short alias, and may appear only once. local is reserved: omit %dispatch for an
ordinary local launch.
The source strips only %dispatch; the target receives and processes the remaining
prompt and launch directives. Launch requests do not forward SASE_INSTRUCTIONS_FILE.
The target renders instruction bundles with its own installed code; no instruction bytes
cross the wire. V1 remote launch cannot be combined with %wait, %queue, %clan, or
an active %hold, and it rejects local-only run payloads such as resolved launch units,
collected inputs, attachments, files, and images. The remaining prompt must be
non-empty.
Project context must be reproducible on the target. A trusted launch integration can
provide a Patch reference or explicit revision in the durable request payload; writing a
Patch or macro reference in the prompt does not supply that source-side evidence.
Without payload evidence, including for an ordinary sase run, the source directory
must be a clean Git checkout whose current branch has an upstream and whose HEAD is
not ahead of it. Dispatch requests are durable and idempotent; if acceptance is
uncertain, retrying the same request does not intentionally create a second remote
launch.
sase's TUI and the macro LSP complete configured machine aliases after %dispatch:. See
the Remote Dispatch Runbook for gateway setup, enrollment, status,
and remote-agent controls.
Launch-Scoped Model Alias Overrides¶
The parenthesized %model form accepts keyword arguments that temporarily replace model
aliases for one launch lineage:
%model(opus, medium=codex/gpt-6.1-sol, small=@xsmall)
%model(xsmall=@small, medium=@large@high)
%model(large=@xlarge, xlarge=codex/gpt-6.1-sol@max)
The optional positional value selects the current agent's model. Each alias=value
entry changes how that bare alias resolves — including for the current agent itself:
without a positional value, the agent falls through to llm_provider.default_model
(shipped as @large), so a large=... entry in the map reaches that launch too, on top
of any phase or task route derived from the large size. %model(...) keys must be
alias names; it cannot key on default_model, epic_lander_model,
big_epic_lander_model, or any other retired role name — set those scalar fields under
llm_provider directly instead. The size-specific worker routes use the five built-in
size aliases directly:
Phase and task workers route through the size alias that matches their size. See
Role Aliases for Delegated Work
for the routing rules and
Implicit role aliases for shipped
defaults. Tasks without stored size metadata use the small route.
Keys must be known builtin or custom alias names without @; values may be concrete
models, provider/model targets, quoted targets, macro references, or another alias
with @. A trailing reasoning-effort suffix is supported on a single-target value,
including an alias reference such as @large@high.
"Launch-scoped" describes persistence, not every subprocess the agent starts. SASE
records the map in agent metadata and carries it through its plan/coder follow-up path.
An explicit %id(suffix, session=parent) attachment inherits the parent's map when its
prompt supplies no alias keywords; a prompt with its own keywords uses that new map.
Ordinary nested launches do not inherit the map. This lineage often overlaps an
Agent Session, but the terms are not interchangeable.
At each alias hop a launch-scoped value wins over a machine-wide temporary override and the configured or implicit alias value.
The launch preview shows the resulting map before approval. Invalid alias names, missing
values, duplicate keys, self-references, and ambiguous bare alias values fail with a
directive error. Use @medium, not medium, when the value should reference another
alias.
A %model value may carry a trailing @<effort> reasoning-effort suffix (e.g.
%model:opus@xhigh); the effort is split off the clean model and behaves exactly like a
standalone %effort directive. See the Effort Directive below.
Related¶
_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.