Command reference¶
Every asterism command, its options, and what it prints. Commands map directly
to operations — each one parses its arguments, performs the operation, and
formats the result.
asterism <command> [options]
| Global option | Effect |
|---|---|
-h, --help |
Show help. Works on any command: asterism <command> --help. |
-v, --version |
Print the installed version. |
Exit codes: 0 on success, 1 on any error (unknown agent, missing
argument, no workspace, bad value). Errors print to standard error; results
print to standard output.
An option a command does not take is an error, never something quietly ignored. The command names the option and shows what it does take, so a typo is never mistaken for a default you chose:
$ asterism new writer --trsut autonomous
asterism new does not take --trsut.
Usage: asterism new <agent> [--soul <name|path>] [--role "<text>"] [--trust <level>]
An option given no value is an error too — whether you left the value off
entirely, or gave one that is empty. --provider "" is what --provider "$VAR"
expands to when the variable is unset, and it names no provider:
$ asterism config set gpt-4o --provider ""
The --provider option needs a value.
Clearing something you have set is config unset or the command's own
--unset, which say so.
Text you write yourself is untouched by this. handoff,
artifact, summary, brief,
objective add, the notes verbs, and
secrets add take their tail exactly as typed, dashes and all, and
everything after -- on service install reaches the command the
service runs. run is the one exception — it reads its task as ordinary
arguments — so put a task that begins with a dash after --:
asterism run writer -- --draft the proposal.
Almost every command takes an agent by name and must be run inside an
initialized workspace (the .asterism/ home, discovered by walking up from the
current directory). If no workspace is found, the command tells you to run
asterism init first.
init¶
asterism init
Set up Asterism in the current directory. Creates a local .asterism/ home that
holds every agent's separate store and workspace. Safe to re-run — an
existing home is left untouched and reported as already set up.
$ asterism init
Initialized Asterism in /home/you/project/.asterism
Create your first agent: asterism new <name> --role "..." --trust propose
new¶
asterism new <agent> [--soul <name|path>] [--role "<text>"] [--trust <level>]
[--model <id>] [--provider <name>] [--base-url <url>] [--api <protocol>]
Create a new agent with its own memory, secrets, skills, and workspace, kept separate from every other agent.
| Option | Default | Description |
|---|---|---|
--soul <name\|path> |
casual-helper |
The agent's character. A built-in name (casual-helper, careful-consultant) or a path to your own Markdown file. |
--role "<text>" |
(none) | One line describing what the agent is responsible for. |
--trust <level> |
propose |
propose, notify, or autonomous. See trust levels. |
--model <id> |
(install default) | Pin this agent to a specific model. Combine with --provider/--base-url/--api for a non-default provider. Stored as a per-agent override in config; change it later there. No API key goes here. |
An agent name must be a single safe path segment: letters, digits, dot, dash, or
underscore — no spaces or slashes, up to 64 characters. A flag given with no
value (--trust with nothing after it) is an error rather than a silent fall
back to the default. If you point --soul at a file that does not exist yet,
the agent is still created and uses a default character until the file appears.
$ asterism new writer --soul careful-consultant --role "tightens blog drafts" --trust notify
Created agent "writer" (notify) — soul: careful-consultant
role: tightens blog drafts
workspace: /home/you/project/.asterism/agents/writer
list¶
asterism list
Show every agent in this workspace — its name, how much it may do on its own, the one line it is responsible for, and when it last ran. Reads the roster only; it never reaches into any agent's memory, secrets, or files.
$ asterism list
Agents (2):
• writer · autonomous
role: tightens blog drafts
last run 2026-06-10T12:01:00.000Z
• work · propose
role: client-facing consultant
never run
Before any agent exists it prints No agents yet. Create one with: asterism new
<name>.
trust¶
asterism trust <agent> <level>
asterism trust <agent> --review
asterism trust <agent> show
asterism trust <agent> revoke <capability>
asterism trust <agent> threshold [--clean <n>] [--targets <n>] · --unset
Set how much an agent may do on its own — both the overall level, and, capability by capability, the few destructive actions it has earned the right to take without pausing for you.
Set the level¶
$ asterism trust writer autonomous
Set writer to autonomous.
propose, notify, or autonomous — see trust levels.
Records an agent.trust_changed event. Remember: notify and autonomous both act
without asking first. Only the
destructive-action gate still pauses
them — unless the agent has earned standing
on that capability — and propose withholds the action rather than asking about it.
Earned autonomy — per-capability grants¶
The destructive-action gate pauses every destructive action by default. An agent can earn the standing to take one specific capability without that pause — by handling it cleanly, several times, across different targets, with nothing declined or failed in between. Earned standing is always proposed for your approval, never granted automatically, and lost the moment something goes wrong: a declined or failed action on a capability resets it, and it has to be re-earned. A grant only ever lets that one capability skip the pause — it never weakens the classification, never crosses to another capability, and never carries to another agent.
| Form | What it does |
|---|---|
trust <agent> --review |
Review the capabilities the agent has earned the right to act on without pausing. You grant or decline each; nothing is granted without your yes, and [q]uit (or Ctrl-D) stops the walk with the rest keeping for next time. The review — each capability, its evidence, and the question — is written to standard error, so redirecting the output leaves what you are deciding about on screen; only the closing count is on standard out. Needs a terminal to ask at: with nobody to ask, it names how many are waiting and stops, leaving them to be decided later. |
trust <agent> show |
Show the agent's level, which capabilities now act without pausing, and its earning bar. |
trust <agent> revoke <capability> |
Take a grant back — the capability pauses for your confirmation again. |
trust <agent> threshold [--clean <n>] [--targets <n>] |
Tune how much clean track record review asks for before it proposes a grant: how many confirmed executions (--clean), across how many different targets (--targets). Set either or both; leave the other as it is. |
trust <agent> threshold --unset |
Clear the custom bar — back to the built-in default. |
trust <agent> threshold |
Show this agent's current earning bar. |
A higher bar only asks for more evidence before proposing a grant — it never lets
anything act without your yes. Granting and revoking are recorded as
agent.standing_changed events; tuning the bar as agent.setting_changed.
capabilities¶
asterism capabilities show <agent>
asterism capabilities set <agent> <key>... · --none
asterism capabilities remove <agent> <key>...
asterism capabilities unset <agent>
Choose which tools an agent has at all. Every agent starts with the standard toolkit, and staying that way is perfectly ordinary — an agent is already kept to its own workspace and its own autonomy level. This narrows it further, for an agent you want kept to less.
$ asterism capabilities show work
work · holds all 9 in the catalog [not narrowed]
✓ fs.append
✓ fs.delete
✓ fs.find
✓ fs.list
✓ fs.mkdir
✓ fs.move
✓ fs.read
✓ fs.stat
✓ fs.write
Its own working notes (record_note / forget_note) are always available.
Narrow it with: asterism capabilities set work <key>…
Which of these may act without pausing: asterism trust work show
set declares exactly what the agent holds from then on — naming the whole list is the
point, because it is the only way to give something back and it says plainly where you
end up:
$ asterism capabilities set work fs.read fs.list fs.write
work now holds 3 capabilities: fs.list, fs.read, fs.write.
Its own working notes stay available. Stop narrowing it with: asterism capabilities unset work
$ asterism capabilities show work
work · holds 3 of 9 in the catalog [narrowed to 3]
· fs.append (withheld)
· fs.delete (withheld)
· fs.find (withheld)
✓ fs.list
· fs.mkdir (withheld)
· fs.move (withheld)
✓ fs.read
· fs.stat (withheld)
✓ fs.write
Its own working notes (record_note / forget_note) are always available.
Stop narrowing it with: asterism capabilities unset work
Which of these may act without pausing: asterism trust work show
| Form | What it does |
|---|---|
capabilities show <agent> |
What this agent holds, and what is being withheld. |
capabilities set <agent> <key>... |
Declare exactly what it holds from now on. |
capabilities set <agent> --none |
It gets no tools from this workspace at all. |
capabilities remove <agent> <key>... |
Take one away and leave the rest. |
capabilities unset <agent> |
Stop narrowing — back to everything on offer. |
An agent's own working notes are always available and are not listed here.
This is not the same as
trust.capabilitiesdecides which tools an agent has;trustdecides what it may do with them — its overall level, and the few destructive actions it has earned the right to take without pausing. They do not cascade: taking a tool away leaves any grant it earned intact and unused, and giving the tool back makes that grant apply again. Take a grant back withasterism trust <agent> revoke <capability>.
Endpoints you have bound with api add are listed separately in
capabilities show and are not changed by any verb here — a hand-typed api.* key is
refused, and names the verb that does own it:
$ asterism capabilities set work api.issues
api.issues: bound endpoints, which carry a credential — they are granted and withdrawn by their own verb, not declared here.
See them with: asterism api list work
Withdraw it with: asterism api remove work issues
secrets add¶
asterism secrets add <agent> <KEY> [value]
Give one agent a private credential. The value is stored for that agent alone and is never printed back, logged, or readable by any other agent.
The value is resolved in this order:
- the inline
[value]argument, if given; - otherwise the environment variable of the same name as
KEY; - otherwise standard input, if piped;
- otherwise you are asked for it, when there is a terminal to answer at.
The prompt comes last because it is the only one of the four that cannot run
unattended. It is the one to prefer when you are typing the command yourself: an
inline value is recorded in your shell history and is visible in ps output for as
long as the command runs, and an environment variable is readable by every child
process. A pipe avoids both, but only if whatever feeds it does — cat token.txt
leaves the value in a file, and echo … puts it back in your history, where
pass show … or another secret manager leaves it in neither. Nothing you type at the
prompt is echoed, and answering with a blank line stores nothing rather than storing
an empty credential.
The inline value is taken verbatim — a value beginning with a dash
(-----BEGIN…) is stored as given, not parsed as an option. So are the environment
and piped values. A value typed at the prompt is trimmed, because whitespace around a
line you cannot see is whitespace you cannot check; pipe a value whose leading or
trailing whitespace matters.
$ asterism secrets add work GITHUB_TOKEN ghp_xxx
Stored credential GITHUB_TOKEN for agent work.
# from the environment
$ GITHUB_TOKEN=ghp_xxx asterism secrets add work GITHUB_TOKEN
Stored credential GITHUB_TOKEN for agent work.
# piped (nothing echoed)
$ cat token.txt | asterism secrets add work GITHUB_TOKEN
Stored credential GITHUB_TOKEN for agent work.
# omitted at a terminal — asked for, and nothing echoed
$ asterism secrets add work GITHUB_TOKEN
Value for GITHUB_TOKEN (not echoed):
Stored credential GITHUB_TOKEN for agent work.
api¶
asterism api add <agent> <name> <https-url> --credential <KEY>
asterism api list <agent>
asterism api remove <agent> <name>
Let one agent call one web address, using one of its own stored credentials — without ever seeing that credential. You choose the address and which credential goes with it; the agent gets a tool that calls exactly that address and nothing else.
$ asterism api add work issues "https://api.github.com/repos/acme/site/issues?state=open" --credential GITHUB_TOKEN
Bound api.issues for work — it may now send credential GITHUB_TOKEN to api.github.com.
No call happens without you: at notify and autonomous it pauses and asks; a propose agent only ever plans it.
Three things are worth knowing before you bind one. They are the point of the command, not implementation detail:
- The agent supplies nothing. You give the whole address, query string and all, so the agent cannot steer the call or attach anything of its own to it. Nothing the agent wrote leaves your machine.
- No call happens without you, and it cannot earn its way out of asking. At
notifyandautonomousthe run pauses and asks; aproposeagent never calls at all, it only tells you it would. Unlike every other destructive capability, this one can never earn a standing grant — sending a credential somewhere is the one thing this product will not learn to do on its own. - The credential never reaches the agent. It is attached on the way out and stripped from anything that comes back.
Addresses must be https, and cannot carry a username or password in the URL. A
credential sent over cleartext http is a credential on the wire, so it is refused
rather than warned about:
$ asterism api add work bad "http://example.com/x" --credential GITHUB_TOKEN
invalid endpoint URL: only https is supported (a credential sent over http:// travels in cleartext)
See what an agent can call, and what each call sends:
$ asterism api list work
work · 1 bound endpoint:
api.issues
calls https://api.github.com/repos/acme/site/issues?state=open
sends GITHUB_TOKEN
No call happens without you: at notify and autonomous it pauses and asks; a propose agent only ever plans it.
Binding is what grants it — there is no second step, and the endpoint shows up in
capabilities show without being declared there. Withdrawing it
leaves the credential alone:
$ asterism api remove work issues
Removed api.issues from work — it can no longer send GITHUB_TOKEN anywhere.
The credential itself is untouched and still stored for work.
| Form | What it does |
|---|---|
api add <agent> <name> <url> --credential <KEY> |
Bind the credential to the address. The agent gains a tool named call_<name>. Re-run it to change either. |
api list <agent> |
What this agent can call, and what each call sends. |
api remove <agent> <name> |
Withdraw it. The credential itself is left alone. |
Binding a credential the agent does not have yet is allowed — asterism says so rather than failing, so you can bind and store in either order:
$ asterism api add work reports "https://api.example.com/reports" --credential REPORTS_TOKEN
Bound api.reports for work — it may now send credential REPORTS_TOKEN to api.example.com.
No call happens without you: at notify and autonomous it pauses and asks; a propose agent only ever plans it.
Note: no credential 'REPORTS_TOKEN' is stored for work yet, so calls will fail until you add one: asterism secrets add work REPORTS_TOKEN
skill add¶
asterism skill add <agent> <file.md>
Teach an agent a skill from a Markdown file. The file is copied into the
agent's own workspace; other agents cannot see it. The skill is named after
the file (without the .md). The source must be an existing .md file.
$ asterism skill add writer blog-style.md
Attached skill "blog-style" to agent writer.
objective¶
asterism objective add <agent> "<text>"
asterism objective list <agent>
asterism objective done <agent> <id>
asterism objective drop <agent> <id>
Give an agent a standing objective — what it should be working toward, ongoing. Unlike a memory (a lesson it learned and you reviewed), an objective is current purpose you set and manage. Every active objective frames the agent's runs as standing context, so the goal stays in view across many runs. See standing objectives.
| Verb | What it does |
|---|---|
objective add <agent> "<text>" |
Declare a new objective. It starts active and begins framing the agent's runs. |
objective list <agent> |
Show the agent's objectives — the active ones that frame runs first, then any proposed for review, then completed/dropped history. |
objective done <agent> <id> |
Mark an objective completed; it stops framing runs. |
objective drop <agent> <id> |
Abandon an objective; it stops framing runs. |
Identify an objective by the short id shown in objective list. An objective is the
named agent's own scoped state — only ever its own, never another agent's — and its
text is run through the same safety screen as memory
before it is saved (a write that trips the screen is refused, not stored). Declaring
or retiring one is not destructive: it touches only the agent's own purpose,
nothing external.
Reflection can also propose objectives it notices the agent working toward —
surfaced for your approval, never adopted on its own. A proposed objective does not
frame runs until you accept it; review proposals with
asterism reflect <agent> --review. Reflection can likewise suggest that
an active objective looks finished — but only your objective done (or an apply in
reflect --review) actually retires it; an agent never marks its own objective done.
$ asterism objective add writer "keep the launch blog current and on-brand"
Declared objective a1b2c3d4 for writer.
$ asterism objective list writer
Objectives for writer (2, 1 active):
• a1b2c3d4 · active
keep the launch blog current and on-brand
declared 2026-06-20T09:00:00.000Z
• e5f6a7b8 · done
ship the Q2 retrospective post
declared 2026-06-18T14:00:00.000Z
$ asterism objective done writer a1b2c3d4
Marked objective a1b2c3d4 done for writer.
An agent with no objectives prints writer has no objectives yet. alongside the
command to declare one. A done/drop whose id matches nothing — or matches more
than one — says so rather than guessing.
notes¶
asterism notes inspect <agent>
asterism notes set <agent> "<subject>" "<value>"
asterism notes clear <agent> "<subject>"
asterism notes accept <agent> "<subject>"
asterism notes reject <agent> "<subject>"
See and manage an agent's working notes — its own running record of the current
situation, kept as subject: value pairs. The agent writes these itself as it
works, to carry context from one run into the next, and they frame its later runs.
They are the agent's own unverified notes, distinct from memory: nothing here was
reviewed by you, so they are shown and framed plainly as the agent's own record,
not facts. See working notes.
| Verb | What it does |
|---|---|
notes inspect <agent> |
Show the agent's working notes — its own record, and how full it is. |
notes set <agent> "<subject>" "<value>" |
Set or correct a note yourself. Re-setting a subject replaces its value. |
notes clear <agent> "<subject>" |
Remove one note. |
notes accept <agent> "<subject>" |
Approve a note that is awaiting your review, so it starts framing runs. |
notes reject <agent> "<subject>" |
Decline a note awaiting review; it is discarded and never frames. |
The agent records and forgets its own notes mid-run through its tools; these operator
verbs are how you inspect and revert them. Notes the agent sets directly take
effect immediately. Some notes can instead arrive marked awaiting your review —
surfaced for you to accept or reject — and until you accept one it does not frame
the agent's runs (a subject can hold an accepted note and a proposed update to it
side by side; the review verbs act on the proposed one). A note is the agent's own
scoped state — writing, clearing, or reviewing one is never destructive, and never
crosses to another agent. An agent keeps a bounded number of notes; when they are
full, clear one before a new subject will save (tune the limit with
config world-fact-cap). A note you set is run through the same
safety screen as the agent's own (a write that trips the
screen is refused, not stored).
$ asterism notes inspect writer
Working notes for writer (2 of 32) — the agent's own unverified record, not facts:
• house style: sentence case in headings
updated 2026-06-20T09:02:00.000Z
• draft status: intro rewritten, closing still needs a pass
⟳ pending update → ready for review — awaiting your review (accept to apply, reject to keep the current value)
updated 2026-06-20T09:14:00.000Z
$ asterism notes set writer "house style" "sentence case in headings, serial comma"
Set working note "house style" for writer.
$ asterism notes clear writer "house style"
Cleared working note "house style" for writer.
$ asterism notes accept writer "draft status"
Accepted working note "draft status" for writer; it now frames runs.
accept and reject act on the pending update — the one marked ⟳ above, which the
agent proposed and you have not ruled on. A note you set yourself is already accepted, so
there is nothing to review; asterism says so rather than pretending to accept it again.
An agent with no working notes says so, with the command to set one. Clearing a subject that has no note, setting one when notes are full, or reviewing a note that is not awaiting review reports the problem plainly rather than failing silently.
run¶
asterism run <agent> "<task>"
Put an agent to work on a task, framed by its role, character, skills, and the memories it has accepted. What it may do on its own depends on its trust level; nothing destructive happens without you at any of them, under the destructive-action gate.
Requires a configured model. The task can be quoted or left as trailing words — both are preserved in full.
A run ends in one of three ways:
- done — the agent's output is printed.
- paused — a destructive action needs confirmation:
Run paused: a destructive action needs your confirmation before it can proceed.At a terminal you are prompted[y/N]to approve it right away. The question is put on standard error, alongside the run's activity and summary, so redirecting the agent's output still shows it to you. Otherwise the run stays paused — it also prints the exact command to resume it later (asterism confirm <agent> <id>; seeconfirm). A run with nobody to ask — standard input or standard error redirected, or no terminal at all — never auto-approves and never waits for an answer that cannot come. - failed — an error is printed and the command exits
1.
The run's activity streams as it happens, and an agent that can act on its own
(notify or autonomous) ends with a short summary of what it did, withheld, or
paused on — the notify level's promise to show you each action, kept. Both the
live activity and the summary go to standard error, so the agent's own output
on standard out stays clean to pipe or redirect:
$ asterism run writer "tighten the intro in posts/launch.md"
→ write_file
✓ write_file
Actions (1 executed):
✓ executed fs.write (write)
<the agent's response>
(The arrowed lines and the Actions summary are on stderr; only <the agent's
response> lands on stdout.)
Tools: the shipped CLI registers a default catalog of workspace-scoped file tools —
read_file,list_dir,stat, andfindto look around, pluswrite_file,append_file,mkdir, andmoveto change things anddelete_fileto remove them — behind the trust gate. The read-only tools and the write tools are ordinary read/write effects;delete_fileis destructive, so anotifyorautonomousrun stops and asks before it — unless you have allowed that capability for the agent — and aproposeagent never runs it at all, whilemoverefuses to overwrite an existing destination (so it never silently destroys anything). Each is confined to the agent's workspace (logical scoping, not an OS-enforced jail — see what isolation means today). The end-to-end behavior is shown in the walkthrough.
connect¶
asterism connect <from> <to> --mode <handoff|artifact-only|read-summary|shared-brief|delegated-tool>
Open an explicit channel from one agent to another. Agents are separate by default and cannot reach each other; a connection is the only thing that opens a path — and even then, only what the mode allows ever crosses. Nothing of the other agent's memory, secrets, or tools is ever shared.
$ asterism connect writer researcher --mode handoff
Connected writer → researcher (handoff). Use it with: asterism handoff writer researcher "<task>"
A connection is one-way: this lets writer hand off to researcher, not the other
way round. Open a second connection for the reverse. Re-running the same connect is
harmless — it will not make a duplicate.
--mode |
What may cross | Used by |
|---|---|---|
handoff (default) |
the receiving agent's final result, and nothing behind it | handoff |
artifact-only |
a list of the files it produced — not its words, not the contents | artifact |
read-summary |
a screened extract of what it has already learned; it does no work | summary |
shared-brief |
nothing back — this one carries standing context in | brief |
delegated-tool |
the answer from one tool the receiving agent owns — its credentials stay with it | delegate · call |
Each mode is its own permission, so opening one never quietly grants another. Opening a connection is a deliberate act you take; from then on either agent's record shows the channel exists and each time it is used — never the task text or any result.
The full picture, with a worked two-agent session, is in Working together.
disconnect¶
asterism disconnect <from> <to> [--mode <handoff|artifact-only|read-summary|shared-brief|delegated-tool>]
Withdraw a channel you opened. From then on the two agents are as separate as they were before you connected them.
$ asterism disconnect writer researcher --mode handoff
Disconnected writer → researcher (handoff). writer can no longer hand work to researcher.
Reconnecting opens a new channel — it does not bring the old one back.
What it takes away is more than the ability to ask for work. Withdrawing an
artifact-only channel also means files already handed over can no longer be
fetched — the list you were given stops resolving. Withdrawing a
read-summary channel stops it sharing what it knows, including anything learned while
the channel was open. Withdrawing a shared-brief channel un-frames the brief for
both agents, from their next run. Withdrawing a delegated-tool channel takes back
every tool handed over on it at once — to take back just one, use
undelegate.
--mode picks the channel when two agents have more than one open. With a single open
channel you can leave it out; with several, asterism asks you to name one rather than
guess.
Work already underway is not interrupted. If the other agent is paused waiting for you to confirm something, that confirmation still works and it still finishes its task in its own space — but nothing it produces afterwards comes back across the withdrawn channel.
This cannot be undone. Connecting the two agents again opens a new channel; it does not restore the old one, and files handed over on the old one stay unreachable. Both agents' records keep showing the channel and the moment it was withdrawn.
connections¶
asterism connections <agent>
Show the channels one agent is on — the agents it can hand work to (outbound, →) and
the agents that can hand work to it (inbound, ←), with each channel's mode.
$ asterism connections writer
Connections for writer (3):
• → researcher · artifact-only · active · 140f38bd
• → researcher · shared-brief · active · e2b6e22f
• → researcher · handoff · revoked · cdeb1026 (withdrawn — nothing crosses it)
→ outbound (this agent initiates over the channel) · ← inbound (the other agent initiates)
Only ever the named agent's own connections; it never reveals a channel between two other agents. Channels you have withdrawn stay listed, marked, after the open ones — so you can always see what was once open and that it is now closed.
handoff¶
asterism handoff <from> <to> "<task>"
Have one agent hand a task to another over a channel you have already opened.
$ asterism handoff writer researcher "summarize what the Q3 deck says about pricing"
The deck holds enterprise pricing flat and moves the team tier up 12%.
The receiving agent does the work in its own workspace, at its own autonomy level, framed by its own memory and skills — and hands back only its final result. The asking agent never sees the other's memory, secrets, files, or how it got there.
Because the work runs as the receiving agent, that agent's protections apply: a destructive action answers to the receiving agent's own gate, and to nothing the asking agent holds — no matter how much autonomy the asking agent was given. A handoff can never be a way around another agent's limits. If it pauses, confirm it on the receiving agent:
asterism confirm <to> <run>
Needs a configured model — the receiving agent runs the task.
artifact¶
asterism artifact <from> <to> "<task>"
Like handoff, but you get back only the files the other agent
produced — a list of what it made, with sizes — instead of its written answer.
$ asterism artifact writer researcher "draft the market section"
Actions (1 executed):
✓ executed fs.write (write)
researcher produced 1 artifact:
drafts/market-section.md 55 B
Only these references crossed — not researcher's words, memory, or the file contents.
The files are in researcher's own workspace.
Everything true of handoff is true here: the receiving agent works in its own space,
at its own autonomy level, and its protections apply. What comes back is narrower — each
file's path and size, and whether it still exists. You do not get the receiving
agent's words, its memory, or the contents of those files. The files sit in the
receiving agent's own workspace, on your machine, where you can read them directly.
To bring one across, use fetch. Needs a configured model.
A channel opened for handoff does not work here, and vice versa — each mode is its own
permission.
fetch¶
asterism fetch <from> <to> <path>
Copy one file the receiving agent produced into the asking agent's own workspace.
This is how a list of files becomes an actual file: run artifact first,
read the paths it hands back, and fetch the one you want.
$ asterism fetch writer researcher drafts/market-section.md
Fetching 'drafts/market-section.md' from researcher into writer's workspace.
Fetched 'drafts/market-section.md' from researcher into writer's workspace (55 B).
Nothing is copied without you, and this can never be earned away. At notify and
autonomous you are asked to confirm every single fetch — copying a file in is a
change to the asking agent's own workspace, and if a file is already there it says so
before replacing it. An agent set to propose writes nothing at all; it tells you what
it would copy and leaves it to you.
Only a file the other agent actually handed over can be fetched. A path it never produced is refused, whatever is on disk — this brings across what you were shown; it is not a way to read another agent's files.
summary¶
asterism summary <from> <to> ["<focus>"]
Read a short, screened extract of what another agent has learned. This is the one command where the other agent does no work at all — nothing runs, no model is called, and it works with no model configured.
$ asterism summary writer researcher
researcher knows 3 of 3 ratified notes:
semantic Enterprise buyers weigh seat price over total contract value.
semantic The Q3 deck prices the team tier 12% above Q2.
convention Draft sections in Markdown, never HTML.
Only this extract crossed — not researcher's memory records, its runs, or anything it has not accepted.
Add a focus in quotes to ask about one subject:
$ asterism summary writer researcher "pricing"
Only what you have accepted can cross. Anything still waiting for your review, anything you rejected, and anything archived stays where it is, at any size of extract. What comes back is the knowledge itself — never the underlying records, when they were learned, what run produced them, or how sure the agent is of them.
Anything that looks like a password or key is removed before it crosses, and a note whose wording could be an attempt to steer the reading agent is held back whole. The extract tells you how many notes were held back, and how many did not fit — ask again with a focus to reach those.
brief¶
asterism brief <from> <to> "<brief>"
Set the standing context two connected agents both run with. Unlike every other channel, this one carries something in rather than handing a result back.
$ asterism brief writer researcher "Q3 launch: enterprise buyers, ship by Friday"
Briefed writer → researcher.
Both agents now run with it as standing context, until: asterism unbrief writer researcher
From then on both agents run with that brief in front of them — not just the one it was written to, and not just when they work together. It shapes their ordinary runs too, until you replace it, end it, or withdraw the channel. That is what makes it standing context rather than a longer way of writing a task.
An agent has only one brief per channel. Writing a new one replaces the old one, which stops shaping runs immediately.
What you write is screened before it goes anywhere. Wording that reads as an attempt to steer or manipulate the other agent is refused outright — nothing is saved and nothing crosses, and asterism tells you which rule it tripped without repeating what you wrote. Each agent sees the brief clearly marked as coming from the channel, not as its own purpose, so an agent is never fooled into treating another agent's words as its own.
Nothing of the other agent comes back to you here — no result, no files, no memory.
unbrief¶
asterism unbrief <from> <to>
End the standing brief on a channel. From their next run onward, neither agent sees it.
$ asterism unbrief writer researcher
Ended the brief on writer → researcher. Neither agent sees it from their next run.
The channel is still open — set a new brief with: asterism brief writer researcher "<brief>"
The channel itself stays open, so you can set a new brief whenever you like. To close
the channel too, use disconnect — withdrawing the channel also ends the
brief. A brief that has ended does not come back; set a new one instead.
briefs¶
asterism briefs <agent>
Show the briefs an agent is running with, and the ones it used to.
$ asterism briefs writer
Briefs for writer (1):
• → researcher · 1e572c21 (framing every run of both agents)
Q3 launch: enterprise buyers, ship by Friday
A brief frames BOTH agents' runs while it is live — the arrow shows whose channel carries it.
Each line says which channel carries the brief and whether it is shaping runs right now.
A brief can be listed but no longer shaping runs for two reasons: you replaced or ended
it, or you withdrew the channel it lived on. Both are marked, so you never have to
cross-check connections to work out why a brief stopped applying.
Only ever the named agent's own briefs — it never reveals a brief between two other agents.
delegate¶
asterism delegate <from> <to> <endpoint>
Let one agent ask another to use one of its tools — and only that one. The tool is an endpoint the other agent has bound; what crosses is the answer, never the credential.
Opening the channel is not enough on its own, and that is deliberate:
$ asterism connect writer helper --mode delegated-tool
Connected writer → helper (delegated-tool). Use it with: asterism delegate writer helper <endpoint>
$ asterism delegate writer helper issues
writer may now ask helper to call 'issues' — and only that. helper's credential stays with helper.
Every call stops for you first: helper asks before it sends anything, at any trust level.
Use it with: asterism call writer helper issues
The channel says writer may ask for tool results at all; this says which tool. So an
endpoint you set up for helper next week is not something writer can reach through a
channel you opened today — you always name it. That is the whole reason the two are
separate.
Only an endpoint the other agent already has can be handed over. A grant left waiting for a tool to appear is exactly what this design avoids, so it is refused:
$ asterism delegate writer helper payroll
helper has no endpoint 'payroll' to hand over.
See what it has: asterism api list helper
Two things are worth knowing before you grant one, and they are properties of the channel rather than of your setup:
- Every call stops for you. The owning agent asks before it sends anything, at any autonomy level, and it can never earn its way out of asking — sending a credential somewhere is the one thing this product will not learn to do on its own.
- A
proposeagent never calls at all. It only tells you what it would have done. If the agent that owns the endpoint is atpropose, the channel works and the grant holds, and nothing is ever sent.
See what a channel reaches with connections. Take one grant back with
undelegate; withdrawing the channel with disconnect
takes back everything on it at once.
undelegate¶
asterism undelegate <from> <to> <endpoint>
Take back one tool you handed over. The channel stays open, the other tools on it are untouched, and the endpoint itself is left alone — the agent that owns it can still use it, and its credential is not touched.
$ asterism undelegate writer helper issues
writer can no longer ask helper to call 'issues'.
The channel is still open and helper still has the endpoint — hand it back with: asterism delegate writer helper issues
Changing the endpoint does this for you. If you re-point it at a different address or
credential with api add, or remove it with api remove, anyone who could ask
for it stops being able to: what they were handed is no longer what would be sent, so
asterism stops sending it rather than sending something else.
$ asterism api add helper issues "https://api.example.com/repos/acme/site/pulls" --credential GITHUB_TOKEN
Bound api.issues for helper — it may now send credential GITHUB_TOKEN to api.example.com.
No call happens without you: at notify and autonomous it pauses and asks; a propose agent only ever plans it.
This changed what the call sends, so writer can no longer ask helper to make it.
Grant it again with: asterism delegate writer helper issues
Re-running api add with the same address and credential changes nothing and takes
nothing away.
call¶
asterism call <from> <to> <endpoint>
Ask one agent to use a tool the other one has, and show what came back.
$ asterism call writer helper issues
{"open":3,"titles":["ship the pricing page"]}
The asking agent chooses nothing but which tool. The address, the credential and the request all belong to the agent that owns the endpoint; the call runs as that agent, under its rules; and what you see is the answer with anything credential-shaped stripped out. Nothing else of the owning agent crosses — not its memory, not its other tools, not its words.
You are asked before anything is sent, every time:
$ asterism call writer helper issues
writer is asking helper to call 'issues' with helper's credential.
Approve this destructive action? [y/N]
If the owning agent is at propose, nothing is sent at all:
$ asterism call writer helper issues
[proposed] would ask helper to call 'issues'. Nothing was sent.
helper is at trust level propose, so it does not act on its own.
Three things turn this down, and they read differently on purpose: there is no
delegated-tool channel between the two agents, the channel is open but this tool was
never handed over, or what was handed over has changed since. A tool that was never handed
over reads exactly like one that does not exist — the refusal tells you nothing about the
other agent's tools.
Both agents' records show that the channel was used and how it ended — never the answer itself, and never the credential.
confirm¶
asterism confirm [<agent>] <run>
Confirm the destructive action a run paused on, and let the run finish. This is how you clear a pause from anywhere — including a run that stopped with nobody at the prompt, like one started over the HTTP endpoint or from a pipe.
Identify the run by the short id shown when it paused (or by runs). Name
the agent too when the same short id could mean different runs:
$ asterism run writer "delete the generated files in dist/"
Run paused: a destructive action needs your confirmation before it can proceed.
Confirm it to continue: asterism confirm writer e5f6a7b8
$ asterism confirm writer e5f6a7b8
→ delete_file
✓ delete_file
Actions (1 executed):
✓ executed fs.delete (destructive)
Deleted 'dist'.
A bare asterism confirm e5f6a7b8 works too when the id is unambiguous across your
agents.
Confirming approves only the action the run paused on — nothing else is
unlocked, and the grant applies to this run alone. The approval is bounded, not a
blanket on the capability: confirming a delete of dist clears that delete, but a
later delete of cache (the same kind of action, a new target) pauses again for
its own confirm, as does any other destructive step. If a run stopped on several
destructive actions at once, you clear them one confirm at a time — a single yes
never approves several distinct actions together. Resuming re-runs the task from the
start, so any ordinary writes the agent had already made happen again; but a
destructive action you already confirmed is not repeated — the run recognizes it
as done and moves on — so confirming step by step never double-charges or
double-deletes. Requires a configured model
— the same one the run started with.
runs¶
asterism runs <agent>
Review one agent's run history — each run's short id, status, what it was asked to do, and when it started and finished. Shows only the named agent's runs, oldest first.
$ asterism runs writer
Runs for writer (2):
• a1b2c3d4 · done
tighten the intro in posts/launch.md
started 2026-06-10T12:01:00.000Z · finished 2026-06-10T12:01:04.000Z
• e5f6a7b8 · awaiting_confirmation
delete the generated files in dist/
started 2026-06-10T12:05:00.000Z
A run shown as awaiting_confirmation is waiting for you — resume it with
confirm. An agent with no runs prints writer has no runs yet.
memory inspect¶
asterism memory inspect <agent> [--type <type>] [--review-state <state>] [--run <run>]
Show what one agent remembers — what it has accepted, what is still proposed for review, and where each memory came from. Only ever shows the named agent's memory.
| Option | Description |
|---|---|
--type <type> |
One memory type: semantic, procedural, convention, negative, episodic. |
--review-state <state> |
One review state: proposed, accepted, rejected. |
--run <run> |
Only what was learned from one run (its short id, e.g. a1b2c3d4). |
Filters AND-combine and stay scoped to this agent — a --run value can only name
one of its own runs, never reach another agent's. An unknown --type or
--review-state is a clear error, not a silent empty list.
$ asterism memory inspect writer --review-state proposed
Memory for writer (1 matching review-state=proposed):
• convention · proposed · confidence 0.86
This blog uses sentence case in headings.
recorded 2026-06-10T12:00:00.000Z · from run a1b2c3d4
An agent with no memories prints writer has no memories yet. A filter that
matches nothing names what was filtered, e.g.
writer has no memories matching type=negative.
events tail¶
asterism events tail <agent> [--limit <n>] [--type <type>] [--run <run>] [--since <id>] [--follow]
Review what an agent has done — an append-only record of its consequential actions (event types). The log holds references only, never secret values. Only ever shows the named agent's activity.
| Option | Description |
|---|---|
--limit <n> |
Cap the number of events shown. |
--type <type> |
Show only events of one type, e.g. --type action.executed. |
--run <run> |
Show only one run's activity (its short id). |
--since <id> |
Page forward — show events after the given event id. |
--follow |
Keep watching and print new events as they happen. Ctrl+C to stop. |
$ asterism events tail writer --limit 3
Activity for writer (3):
2026-06-10T12:00:00.000Z agent.created
{"name":"writer","role":"tightens blog drafts","trustLevel":"notify"}
2026-06-10T12:01:00.000Z run.started run=a1b2c3d4
{"runId":"a1b2c3d4-…","status":"pending"}
2026-06-10T12:01:02.000Z action.executed run=a1b2c3d4
{"capability":"fs.write","effect":"write"}
--follow prints the current backlog (after any --limit/--type/--run
filter), then streams each new event as it lands until you stop it. Like every
other read, a live tail is scoped to the one agent.
trace¶
asterism trace <agent>
Review an agent's recorded cognition trace — an auditable, tool-by-tool record of what each of its runs did, rendered as a report. Only the named agent's own trace; it is never a way to see another agent's.
A trace exists only for an agent you opt in with
config cognition-provider <agent> lodestar. It is observe-only:
recording a trace never changes what an agent may do — the destructive-action gate
reaches the same verdict with a trace running as without one. By default it records
references only (which tool ran, whether it succeeded, how much it returned) —
never the contents of a tool's input
or output. You can additionally record the redacted content each tool returned with
config cognition-capture <agent> content; even then, input arguments are
never kept, common secret shapes (keys, tokens, passwords, private keys) are scrubbed,
and the captured text is bounded and stripped of terminal-control characters.
The scrub is best-effort, not a guarantee. Leave content capture off for an agent that routinely handles secrets you cannot risk appearing in an audit record.
The trace is kept in the install's own storage, outside the agent's workspace, so the agent cannot reach or tamper with its own record. An agent you haven't opted in is told how to start a trace; one opted in but not yet run reports it has no trace yet.
$ asterism config cognition-provider writer lodestar
Set writer's cognition provider to lodestar. Its runs now record an auditable trace — read it with `asterism trace writer`.
$ asterism trace writer
# ... per-run report (one section per run, oldest first), each ending with: ...
Recorded tool calls (3):
1. read_file [ok] 1240 bytes
2. write_file [ok] —
3. delete_file [paused] —
(Illustrative — the exact layout reflects the run that was recorded.) Each run is
rendered as its own report, oldest first, ending with the tool calls it recorded — which
tool ran, whether it succeeded, and how much it returned. Before any run, trace reports
the agent has no trace yet; an agent you never opted in is shown the command to start one.
This pairs with Lodestar. It is opt-in and off by default — the default install records no trace and pulls in nothing for it.
reflect¶
asterism reflect <agent> --review
asterism reflect <agent> --propose
Turn an agent's work into things it might keep — typed memories, and the
standing objectives it could take on. Reflection only ever proposes; you
stay the one who decides what an agent remembers and what it works toward. There are
two halves: one fills a review pile, the other lets you go through it. One of
--review / --propose is required; reflection never runs in a silent auto mode.
--review — go through the pile¶
Review the proposals waiting for an agent and accept, edit, or reject
each one — memory proposals first, then any standing-objective proposals, in one
pass. Anything that looks unsafe to remember is flagged. If proposals are already
waiting (see --propose), it reviews those — no model needed, since they are already
drafted. Otherwise it looks over the agent's latest completed run and drafts new
proposals on the spot (which needs a
configured model).
At a terminal each proposal prompts [a]ccept / [e]dit / [r]eject / [q]uit. The
whole review — each proposal, its warnings, and the question about it — goes to
standard error, so redirecting the command's output does not take the thing
you are deciding about with it; only the closing count is on standard out.
Quitting is not a decision: the proposal in front of you and everything after
it are left exactly as they were, so you can stop part-way through a queue without
settling the rest. That includes the parts of the review still to come — proposed
objectives, and any objective that looks finished — so leaving stops the command
rather than the list you were on. Ctrl-D does the same. With nobody to ask — piped, launched from a scheduler, or with standard
error redirected somewhere you would not see the question — it does not decide on
your behalf: a pile already queued by --propose is left untouched (since
rejecting a queued proposal is a durable choice), and there is no live pass either,
so no model is called for proposals nobody can answer for. Both say to come back at
a terminal, or to use --propose, which is the mode built to run unattended.
Nothing is ever written without you.
$ asterism reflect writer --review
Reviewing 2 proposed memories for writer (from run a1b2c3d4).
Nothing is saved unless you accept it.
(1/2) convention · confidence 0.86
This blog uses sentence case in headings.
Keep this memory? [a]ccept / [e]dit / [r]eject / [q]uit (default: reject): a
✓ saved
(2/2) procedural · confidence 0.78
Run a spell pass before saving.
Keep this memory? [a]ccept / [e]dit / [r]eject / [q]uit (default: reject): r
✗ rejected
Done — 1 saved, 1 rejected.
If a proposal trips the memory firewall it is flagged with a ⚠; if you accept
a flagged one anyway, the firewall still refuses to save it (⛔ blocked).
Finally, if the latest run looks to have finished one of the agent's standing
objectives, --review suggests marking that objective done (or dropped) — you
apply, skip, or quit. The suggestion is advisory: only your apply changes
the objective's status, and an agent never finishes its own objective. With nobody to
ask, this part does not run at all — no suggestion is computed, and no model is called
for one.
--propose — fill the pile, unattended¶
Look over an agent's new work and set aside what might be worth keeping — a memory
to remember, or a standing objective to take on — for you to review later. It
saves nothing as active and asks nothing; it just adds to the review pile, which
you drain with --review when you're ready. Anything that looks unsafe is held back
rather than added. Safe to re-run: it only looks at work it hasn't already looked
over, and never queues the same thing twice.
Because --propose is unattended, it's the form you can put on a schedule (below).
Drafting proposals needs a configured model.
$ asterism reflect writer --propose
Queued 2 proposed memories for writer from 1 run.
Queued 1 proposed objective.
Review them with: asterism reflect writer --review
Schedule it yourself¶
Asterism ships no clock of its own. Nothing reflects on a schedule unless you
set that up — wire reflect --propose to your operating system's scheduler, the
same way you'd schedule any command. It fills the review pile in the background;
you still review and accept everything yourself, on your own time.
cron (Linux/macOS) — propose for writer every night at 2am, via crontab -e.
flock -n keeps a slow run from overlapping the next one (Asterism also guards against
double-queueing internally, but skipping the overlap entirely is cleaner):
0 2 * * * flock -n ~/.asterism/reflect-writer.lock asterism reflect writer --propose >> ~/.asterism/reflect.log 2>&1
launchd (macOS) — ~/Library/LaunchAgents/com.asterism.reflect.writer.plist,
then launchctl load it:
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict>
<key>Label</key><string>com.asterism.reflect.writer</string>
<key>ProgramArguments</key>
<array>
<string>asterism</string><string>reflect</string>
<string>writer</string><string>--propose</string>
</array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>2</integer><key>Minute</key><integer>0</integer></dict>
</dict></plist>
systemd timer (Linux) — a templated asterism-reflect@.service plus its
.timer, then systemctl --user enable --now asterism-reflect@writer.timer:
# asterism-reflect@.service
[Service]
Type=oneshot
ExecStart=asterism reflect %i --propose
# asterism-reflect@writer.timer
[Timer]
OnCalendar=*-*-* 02:00:00
[Install]
WantedBy=timers.target
Whatever the cadence, the deal is the same: a scheduled run only ever adds to
the pile. An agent never starts remembering — or taking on a new objective — on
its own; every memory and objective still waits for your --review.
config¶
asterism config
asterism config set <model-id> [--provider <name>] [--base-url <url>] [--api <protocol>] [--agent <name>]
asterism config unset [--agent <name>]
asterism config recall-budget <agent> <n> · --unset · --default <n>
asterism config world-fact-cap <agent> <n> · --unset · --default <n>
asterism config recall-provider <agent> local · --unset
asterism config cognition-provider <agent> lodestar · --unset
asterism config cognition-capture <agent> content · references
Choose the model your agents run on, and tune how each agent recalls its memory, what
it remembers, and whether it keeps an auditable trace of its runs. Set one install-wide
default model, and give any single agent its own model, its own recall or working-note
limits, its own recall provider, or a cognition trace when you want it to
differ. The model settings live in .asterism/config.json and hold only which
model to use — never an API key (keys stay in the
environment); the per-agent settings are kept in
Asterism's local store, scoped to each agent.
| Form | What it does |
|---|---|
config (or config show) |
Show the saved settings and the model, recall budget, and recall provider each agent resolves to. |
config set <id> |
Set the install-wide default model. |
config set <id> --agent <name> |
Pin one agent to its own model. |
config unset |
Clear the install default. |
config unset --agent <name> |
Clear one agent's override. |
config recall-budget <agent> <n> |
Cap how many memories this agent recalls into a run. --unset returns it to the default; with no value, shows the current setting. --default <n> sets one install-wide default for every agent without its own (precedence: per-agent → install-wide → built-in). |
config world-fact-cap <agent> <n> |
Cap how many distinct working notes this agent may keep. At the cap a new note is refused, never silently dropped. --unset returns it to the default; with no value, shows the current setting. --default <n> sets one install-wide default for every agent without its own (same precedence as the recall budget). |
config recall-provider <agent> local |
Rank this agent's memory by meaning using a local embeddings endpoint (opt-in; see Tuning recall). --unset returns it to the built-in keyword ranker; with no value, shows the current setting. |
config cognition-provider <agent> lodestar |
Record an auditable cognition trace of this agent's runs (opt-in, off by default). --unset stops recording; with no value, shows the current setting. |
config cognition-capture <agent> content |
Also record the redacted content each tool returned, not just references — only meaningful with a trace turned on. references (the default) records references only; with no value, shows the current setting. |
Option (for set) |
Description |
|---|---|
--provider <name> |
Provider name. Default: openai. Naming a built-in provider is enough — the endpoint and wire protocol come with it. |
--base-url <url> |
The provider's API base URL (needed for providers other than the built-ins). |
--api <protocol> |
The wire protocol, when it differs from the provider default. |
--agent <name> |
Apply to this one agent instead of the install default. The agent must already exist. |
Where a model comes from, most specific first: an agent's own model →
ASTERISM_MODEL_* environment variables → the install default → built-in
provider settings. So an environment variable overrides the saved default, and an
agent's own model overrides everything.
A value and --unset are two different instructions, so giving both is an
error rather than a silent choice between them — on every setting above, and on
trust <agent> threshold:
$ asterism config recall-budget writer 30 --unset
Give either a recall budget or --unset, not both.
(config cognition-capture <agent> references --unset is the one exception:
references names exactly what --unset does, so the two agree.)
$ asterism config set gpt-4o-mini --provider openai
Set the model for the install default: gpt-4o-mini (provider: openai).
API keys are never stored here — keep them in the environment (e.g. OPENAI_API_KEY).
$ asterism config set claude-opus-4-8 --provider anthropic --agent work
Set the model for agent "work": claude-opus-4-8 (provider: anthropic).
$ asterism config
Configuration (/home/you/project/.asterism/config.json)
Install default model: gpt-4o-mini (provider: openai)
Per-agent model:
work → claude-opus-4-8 (provider: anthropic) [agent override]
personal → gpt-4o-mini (provider: openai) [install default]
Install-wide recall budget: (none — built-in default 20)
Per-agent recall budget:
work → 20 [default]
personal → 20 [default]
Install-wide world-fact cap: (none — built-in default 32)
Per-agent world-fact cap:
work → 32 [default]
personal → 32 [default]
Per-agent capabilities:
work → all 9 in the catalog [not narrowed]
personal → all 9 in the catalog [not narrowed]
Per-agent recall provider:
work → keyword (built-in) [default]
personal → keyword (built-in) [default]
Per-agent cognition provider:
work → none (no trace) [default]
personal → none (no trace) [default]
API keys are never stored here — set them in the environment (e.g. OPENAI_API_KEY).
Tuning recall¶
Before each run, an agent recalls the most relevant of its saved memories to frame the task. Two per-agent knobs tune that — each scoped to the one agent, never shared:
Recall budget — how many memories a run may frame. The most relevant are kept under the cap; leave it unset to use the built-in default.
$ asterism config recall-budget work 40
Set work's recall budget to 40 memories.
Recall provider — how that relevance is ranked. The default is a built-in
keyword ranker that needs nothing and makes no network call. You can opt a single
agent into local, which ranks its memory by meaning using a local embeddings
endpoint that you run — for example Ollama.
Opt-in, and off by default. Nothing here sends your memory anywhere unless you turn it on and point it at your own endpoint. The default install pulls no ML and makes no network call for recall. The embeddings stay between Asterism and your own local endpoint.
Point Asterism at the endpoint with environment variables, then opt the agent in:
| Variable | What it is |
|---|---|
ASTERISM_RECALL_EMBED_URL |
The embeddings endpoint — an OpenAI-compatible /embeddings route, e.g. http://localhost:11434/v1/embeddings for Ollama. |
ASTERISM_RECALL_EMBED_MODEL |
The embedding model to use, e.g. nomic-embed-text. |
ASTERISM_RECALL_EMBED_KEY |
(optional) A bearer token, for endpoints that need one. A purely-local endpoint usually needs none. |
$ export ASTERISM_RECALL_EMBED_URL=http://localhost:11434/v1/embeddings
$ export ASTERISM_RECALL_EMBED_MODEL=nomic-embed-text
$ asterism config recall-provider work local
Set work's recall provider to local. Configure the endpoint with ASTERISM_RECALL_EMBED_URL and ASTERISM_RECALL_EMBED_MODEL.
If an opted-in agent has no endpoint configured, its runs stop with a clear message rather than quietly falling back — so the misconfiguration is visible. If the endpoint is configured but unreachable during a run, recall degrades to the keyword ranker (it still frames correct memories) and says so, rather than failing the run.
Working notes cap¶
An agent keeps working notes — short, current facts about its world (see
notes). The world-fact cap bounds how many distinct notes it may hold at
once. At the cap, a new note is refused (loudly) rather than evicting an existing
one — notes are never silently dropped; clear one to make room.
Set it per agent, or set one install-wide default with --default. The precedence is
the same as the recall budget: a per-agent cap wins over the install-wide default, which
wins over the built-in.
$ asterism config world-fact-cap work 50
Set work's world-fact cap to 50 notes.
$ asterism config world-fact-cap --default 40
Set the install-wide world-fact cap to 40 notes — every agent without its own override now uses it.
Cognition trace¶
Opt a single agent into recording an auditable cognition trace of its
runs — what each tool did, in order — that you read back with asterism trace. It is
off by default, and observe-only: recording a trace never changes what an agent
may do.
$ asterism config cognition-provider work lodestar
Set work's cognition provider to lodestar. Its runs now record an auditable trace — read it with `asterism trace work`.
By default the trace records references only — which tool ran, success or failure, how much it returned. You can additionally record the redacted content each tool returned; even then, input arguments are never kept and common secret shapes are scrubbed, but the scrub is best-effort:
$ asterism config cognition-capture work content
Opt-in, and off by default. The default install records no trace. Leave content capture off for an agent that routinely handles secrets you cannot risk in an audit record. See
tracefor what the record contains and where it is kept.
serve¶
asterism serve <agent> [--port <n>] [--host <addr>]
Offer one agent over a local HTTP endpoint, with the same separation guarantees as the command line. The endpoint serves only this agent — it is never a way to reach another.
| Option | Default | Description |
|---|---|---|
--port <n> |
4831 |
Port to listen on (0–65535). |
--host <addr> |
127.0.0.1 |
Address to bind. The default is loopback — this machine only. |
$ asterism serve writer
Serving agent "writer" at http://127.0.0.1:4831
POST http://127.0.0.1:4831/agents/writer/runs start a run (JSON body: {"input":"<task>"})
POST http://127.0.0.1:4831/agents/writer/runs/<run>/confirm approve a paused run
GET http://127.0.0.1:4831/agents/writer/runs list runs
GET http://127.0.0.1:4831/agents/writer/events review activity
Access token (generated, save it — shown only once):
9f2c…(64 hex chars)…
Send it on every request: Authorization: Bearer <token>
Stored owner-only at …/.asterism/http-tokens/writer.token; set ASTERISM_HTTP_TOKEN to override.
Press Ctrl+C to stop.
Every request needs that bearer token — the endpoint is default-deny, on loopback
as anywhere else. The first serve generates and prints one (and saves it for next
time); set ASTERISM_HTTP_TOKEN to supply your own, which is what you want for an
exposed or unattended endpoint. See the HTTP reference
for the full token model.
Without a configured model the read endpoints still work; starting a run is
declined until a model is set. A destructive action has no one at the keyboard to
confirm it, so the run pauses and waits rather than running unattended — you
clear it out of band by POSTing to the run's confirm endpoint (or with
confirm). See the HTTP reference for full
request/response details.
dashboard¶
asterism dashboard [<url>] [--token <token>] [--headless] [--port <n>] [--host <addr>]
Your live terminal console over every agent at once — the one place to see what each agent is doing and steer it: review proposed memories, dial autonomy up or down, approve or decline an action an agent has paused for confirmation, and watch activity stream in. It shows many agents but never crosses between them — it only ever asks about one agent at a time, so their separate lives hold here too.
Run with no arguments, it opens the live view for this machine's agents:
$ asterism dashboard
| Key | Action |
|---|---|
↑/↓, j/k |
Select an agent |
t |
Set the selected agent's autonomy (trust) level |
c |
Approve the agent's pending destructive action |
x |
Decline the agent's pending destructive action |
m |
Reflect — review proposed memories (a accept · e edit · r reject) |
r |
Refresh now |
? |
Toggle help · q quit |
The dashboard is a thin client — it holds no logic of its own. Every action is
one request to a small local console endpoint that spans your agents, the same
surface the CLI and serve use, so it inherits the exact same trust
enforcement, destructive-action gate, and agent boundary. Reviewing memory needs a
configured model (it runs reflection on demand); the rest works without one.
| Option | Default | Description |
|---|---|---|
--headless |
— | Run the console without the terminal view — the endpoint a dashboard on another machine attaches to. Prints an access token like serve. |
--token <t> |
— | The access token when attaching to a remote console (or set ASTERISM_HTTP_TOKEN). |
--port <n> |
4832 |
With --headless, the port to listen on. |
--host <addr> |
127.0.0.1 |
With --headless, the address to bind (loopback by default). |
To watch a machine's agents from elsewhere, run the console there and attach to it:
# on the host
$ asterism dashboard --headless
# on your laptop
$ asterism dashboard http://host:4832 --token <token>
See the dashboard reference for the console endpoints behind it.
channel telegram¶
asterism channel telegram <agent> [--allow <chat-id>[,<chat-id>...]]
Reach one agent from a Telegram chat, with the same separation guarantees as the command line. The bot drives only this agent — never a way to reach another.
Set the bot token in the environment (ASTERISM_TELEGRAM_TOKEN, from
@BotFather) — it is a secret and never goes in a flag or
config. The allow-list is the channel's access boundary: only the chat ids you
allow can use the bot; anyone else is refused and told only their own chat id. A
destructive action pauses the run and asks in the chat — you reply /confirm to
approve just that action, the same gate you get at the keyboard.
| Option | Default | Description |
|---|---|---|
--allow <id,...> |
(none) | Chat ids allowed to use the bot, comma-separated. Combined with ASTERISM_TELEGRAM_ALLOW. With none, the bot starts but only hands out chat ids until you add one. |
$ export ASTERISM_TELEGRAM_TOKEN=123456789:AAx…
$ asterism channel telegram writer --allow 8675309
Listening as @writer_bot for agent "writer".
1 authorized chat; messages from any other chat are refused.
A destructive action pauses the run and asks the chat to reply /confirm.
Press Ctrl+C to stop.
A channel needs a configured model — every message is a task — so it will not start without one. See the chat channels guide for setup, the confirm-by-reply flow, and limitations.
channel discord¶
asterism channel discord <agent> [--allow <channel-id>[,<channel-id>...]]
Reach one agent from a Discord channel, with the same separation guarantees as the command line. The bot drives only this agent — never a way to reach another.
Set the bot token in the environment (ASTERISM_DISCORD_TOKEN, from the
Discord Developer Portal) — it is a
secret and never goes in a flag or config. Enable the bot's MESSAGE CONTENT
intent in the portal, or it can't read the messages it's sent. The allow-list
is the channel's access boundary: only the channel ids you allow can use the bot;
anyone else is refused and told only their own channel id. In a server the bot acts
only when you @mention it (a DM needs no mention). A destructive action pauses
the run and asks in the channel — you reply /confirm to approve just that action,
the same gate you get at the keyboard.
| Option | Default | Description |
|---|---|---|
--allow <id,...> |
(none) | Channel ids allowed to use the bot, comma-separated. Combined with ASTERISM_DISCORD_ALLOW. With none, the bot starts but only hands out channel ids until you add one. |
$ export ASTERISM_DISCORD_TOKEN=…
$ asterism channel discord writer --allow 403592…21
Listening as @writer for agent "writer".
1 authorized channel; messages from any other channel are refused.
In a server, @mention the bot; a DM needs no mention.
A destructive action pauses the run and asks the channel to reply /confirm.
Press Ctrl+C to stop.
The Discord channel talks to the Gateway over a WebSocket, so it needs Node 22+ or Bun (an older Node has no WebSocket; the channel declines at startup). Like Telegram, it needs a configured model. See the chat channels guide for setup, the confirm-by-reply flow, and limitations.
service¶
asterism service install <agent> [--kind serve|telegram|discord] [--capture-env] [-- <args>]
asterism service status <agent> [--kind <kind>]
asterism service uninstall <agent> [--kind <kind>]
Keep one agent running in the background as a service your computer starts for you
and restarts if it stops — the same separate-lives guarantees as the command line,
just always on. A service runs one long-lived command for one agent: its local
HTTP endpoint (serve, the default) or one of its chat channels. Supported on
macOS (launchd) and Linux (systemd user services).
Anything after -- is passed straight to the supervised command, so a serve
service can pick a port and a channel can carry its allow-list:
$ asterism service install writer -- --port 8080
$ asterism service install writer --kind telegram -- --allow 12345
| Subcommand | What it does |
|---|---|
install |
Write the service, register it with the OS, and start it (also at login). Re-running it replaces the definition and keeps your edited settings file. |
status |
Show whether the agent's services are running, and where each one's settings and logs live. With no --kind, reports every kind. |
uninstall |
Stop and remove a service. Its settings file is left in place, in case it holds secrets you want to keep. |
| Option | Default | Description |
|---|---|---|
--kind <kind> |
serve |
Which long-lived command to run: serve, telegram, or discord. |
--capture-env |
(off) | On install, write the values currently set in your shell into the private env file, instead of an empty template. A convenience that writes secret values to the 0600 file — only when you ask — and overwrites that file each time. |
Secrets stay out of the service definition. By default install creates a
private environment file (readable only by you) that names what the service needs
— your model API key, and a chat token for a channel — with no values filled in. You
edit that file; nothing secret is ever written for you. Until it's filled in, the
service keeps restarting and status shows it not running. If you have already
exported those variables, --capture-env copies their current values into the
0600 file for you (the one case where install writes a secret to disk — and only
because you asked).
$ asterism service install writer --kind telegram
Installed service "writer (telegram)".
Keeps `asterism channel telegram writer` running and restarts it if it fails.
Before it can work, set these in its private environment file:
ASTERISM_TELEGRAM_TOKEN your Telegram bot token (from @BotFather).
OPENAI_API_KEY your model API key — every chat message is a task, so a channel needs one.
Env file (0600): ~/.config/asterism/services/writer.telegram/service.env
Edit it, then restart: systemctl --user restart asterism-writer-telegram.service
Review it: asterism service status writer
Remove it: asterism service uninstall writer --kind telegram
The destructive-action gate still fires in a service exactly as it does at the
keyboard: a run it stops parks at awaiting_confirmation until you approve it out
of band, and a chat run asks for a /confirm reply. A background service never
loosens that gate. See the run-as-a-service guide for the full
setup, the boot-start note, and how a service finds the right install.
Reference tables¶
Trust levels — propose · notify · autonomous
(details).
Built-in souls — casual-helper (default) · careful-consultant, or a path
to your own Markdown file.
Memory types — semantic · procedural · convention · negative ·
episodic (reflection proposes the first four).
Event types — see the event log.
Environment variables — model selection (ASTERISM_MODEL_*, ASTERISM_API_KEY;
see config) · HTTP token (ASTERISM_HTTP_TOKEN; see serve) ·
channel tokens and allow-lists (ASTERISM_TELEGRAM_*, ASTERISM_DISCORD_*; see
chat channels) · opt-in recall endpoint
(ASTERISM_RECALL_EMBED_URL / _MODEL / _KEY; see Tuning recall).