What this is. The reference for every
bioroutercommand-line subcommand and its flags, plus the slash commands, themes, and keyboard shortcuts available inside an interactive session. Status: Current. Audience: end users
biorouter ships a command-line interface (CLI) for managing sessions, configuration, and extensions, and for running tasks headlessly. This page lists the commands you can run from your shell, then the controls available once you are inside an interactive session. For the manual verification script that exercises this same surface, see the CLI QA checklist.
- Flag naming conventions
- Core commands
- Session management
- Task execution
- Extensions, skills, and knowledge
- Project management
- Interface
- Terminal integration
- Interactive session features
- Navigation and controls
The biorouter CLI follows consistent patterns for flag naming to make commands intuitive and predictable:
--session-id: Used for session identifiers (e.g.,20251108_1)--schedule-id: Used for schedule job identifiers (e.g.,daily-report)-n, --name: Used for human-readable names--path: Used for file paths (legacy support)-o, --output: Used for output file paths-r, --resumeor-r, --regex: Context-dependent (resume for sessions, regex for filters)-v, --verbose: Used for verbose output-l, --limit: Used for limiting result counts-f, --format: Used for specifying output formats-w, --working_dir: Used for working directory filters
Display the help menu.
Usage:
biorouter --helpConfigure biorouter settings - providers, extensions, etc.
Usage:
biorouter configureconfigure is interactive and needs a terminal. Run from a script, a pipe or
anywhere else without one, it changes nothing and exits with status 2, naming
the non-interactive route instead: choose the model with
biorouter models set --provider <provider> --model <model> and pass the
provider's API key in its environment variable (for example OPENAI_API_KEY).
The provider list, and the lead and worker lists of lead/worker mode, leave out the two
decision-model providers (ollama_decision, baseten_decision), as the desktop's provider
catalog does. A decision model answers typed questions and writes no reply, so the setup's test
request could never succeed, and a decision model as the lead would make every new chat a
decision chat. When the default model is itself a decision model, the lead/worker setup says
that lead/worker mode does not apply and changes nothing, because the worker is the default
model. To make a decision model a chat's model, use
biorouter models set --provider ollama_decision --model tev1:0.8b, or --provider and
--model for one session or run; see Decision models.
Their keys are their chat siblings' (OLLAMA_HOST, BASETEN_API_KEY), which configuring Ollama
or Baseten saves.
Shows biorouter information, including the version, configuration file location, session storage, and logs.
Options:
-v, --verbose: Show detailed configuration settings, including environment variables and enabled extensions
Usage:
biorouter infoInspect and update model/provider configuration from the CLI.
Usage:
# Show the configured provider and model
biorouter models current
# List available providers
biorouter models providers
# List known models for a provider
biorouter models list openai
biorouter models list ollama --format json
# Set the default provider and model
biorouter models set --provider openai --model gpt-6-sol
# Local models for Llama Server: the catalog, what is downloaded, and from where
biorouter models local list
biorouter models local list --format json
# Download a model ahead of time, then stop the server again
biorouter models local pull qwen3.8-27b
# Delete a downloaded model's Hugging Face files
biorouter models local rm gemma4-31b
# The decision models this machine can use, local first
biorouter models decision list
biorouter models decision list --format json
# Install a decision model into the Ollama on this computer (tev1:0.8b by default)
biorouter models decision pull
biorouter models decision pull clef-flash:latestmodels local works on the shared model store directly, so it needs no running app or daemon.
local list labels each downloaded model with the copy Llama Server would start from: the
Hugging Face cache first, then a GGUF pulled into Ollama. local pull resolves a name the way a
chat does, so a name from an earlier catalog (such as gemma4 or qwen3.5-4b) downloads the
model it now runs, qwen3.8-27b, and says so; when the retired model's own Hugging Face
download is still on disk, it also says how to free it. local rm resolves a name exactly, so
local rm gemma4 deletes the files that retired model left behind and never the default
model's. A model pulled through Ollama is removed with ollama rm. See the
Llama Server model catalog.
models decision list lists the decision models available now: the ones pulled into the local
Ollama that declare the decision capability, and Baseten's when a Baseten API key is saved. Each
line names the model as provider/model (the spelling --model takes everywhere), with
Baseten's by their aliases (baseten_decision/joey-1, elephant-1, mammoth-1), its privacy
tier, whether it runs on this computer, its context window in tokens, the question kinds it
answers (yes/no, choice, score) and the adapter that reads its probabilities. The list is in the
order the automatic choice tries it: local models first, with the shipped default tev1:0.8b
first among the Ollama models and the rest by name, then Baseten's smallest first. A copy Ollama
cannot load (one in a format it no longer supports) is listed after the other Ollama models and
before Baseten's, marked cannot be loaded, with a line under it naming the fix
(ollama pull <model> or ollama rm <model>); the automatic choice skips it. With
--format json that sentence is the entry's loadRefusal, present only when it is set, and a
Baseten entry keeps Baseten's slug in model (decision-models/baseten-joey-2b-latest) with
the alias in displayName. See decide and
Decision models.
models decision pull [MODEL] installs a decision model into the Ollama that OLLAMA_HOST names,
which must run on this computer, and checks that Ollama answers decisions with it: a decision
model on this computer is the only kind that may read a private chat. It offers tev1:0.8b (the
default, about 1.5 GB) and clef-flash:latest (about 12 GB; clef-flash is accepted). No daemon
is needed, and the next model listing sees the model. Before anything is printed it refuses what
POST /decisions/models/install refuses: a model it does not offer, an OLLAMA_HOST that does not
resolve, an Ollama on another computer (nothing is sent to it) and an Ollama that is not running.
On a terminal it shows a spinner with "Downloading tev1:0.8b… 412 MB of 1.5 GB". Off one it prints
to stderr "Downloading tev1:0.8b from Ollama's library…", each of Ollama's steps once, then
"Downloaded 1.5 GB. Checking that Ollama answers decisions with tev1:0.8b…", and on stdout
"Installed tev1:0.8b: Private, runs on this computer. Private chats can now use it for monitors
and decisions." A failure exits non-zero and quotes Ollama's own words. Two minutes with no bytes
from Ollama stops the download, and running the command again resumes it. See
Installing a local decision model.
Check the current biorouter version you have installed.
Usage:
biorouter --versionCheck system prerequisites and the CLI install. It prints one status line per prerequisite (Git, uv, Python 3, Node.js, AWS CLI, llama-server, the Rust toolchain), says whether biorouter is on your PATH, and reports the local model sidecar. Run it first when an extension will not start or a package runner is missing.
Options:
--format <FORMAT>:text(default) orjson--no-update: Skip the networked self-update check--fix [<DEPENDENCY>]: Hand a failing prerequisite to Biorouter. This opens a session briefed with the dependency, this machine's environment and what to verify. Pass a name (--fix uv), or leave it bare to take the first missing required one
Usage:
biorouter doctor
biorouter doctor --fix uv
biorouter doctor --format json --no-updateReport token and cost usage, per day or per model, month-to-date.
Options:
--from <YYYY-MM-DD>: Start of the range, local time. Defaults to 30 days ago--to <YYYY-MM-DD>: End of the range, local time and inclusive. Defaults to today--by-model: Group by model instead of by day--json: Print machine-readable JSON
Decision models. Calls to decision models about a chat (monitors, the decide tool, phase
gates, decision chats, biorouter decide --session) count in TOTAL, COST and the monthly limit,
and never in TURNS. With --by-model a decision model's row is labelled with its name and
(decision model), Baseten's by their short names (mammoth-1 (decision model)), and shows a
dash in TURNS. The range table and the month summary each add one line:
Decision models: 600 requests, 1,200,600 tokens, $0.12. Counted in TOTAL and COST, not in TURNS.
The second sentence is on the table's line only. A local decision model has no price, so it
counts as $0 and does not mark the totals as incomplete. A Baseten decision model with no known
price does, because Baseten billed it. The line's cost tells the two apart: not metered when
every request went to a local model, $X, some not metered when local requests sit beside priced
ones, cost unavailable when the requests went to a Baseten model with no known price (that
model's own row shows —), and $X, some unavailable when such requests sit beside priced ones. --json carries the same split
as decisionRequests and decisions on each row and on the totals, where decisions.hasUnmetered
marks requests to a local model and decisions.hasUnpriced requests to a hosted one with no
known price. A decision with no chat is not in this report; biorouter decide prints its
usage on stderr instead. See
Usage and billing.
Usage:
biorouter usage
biorouter usage --by-model --from 2026-09-01Install the biorouter command onto your PATH. Alias: install-cli.
Usage:
biorouter setup-pathGenerate the shell autocompletion script. <SHELL> is one of bash, elvish, fish, powershell or zsh.
Options:
--bin-name <BIN_NAME>: Generate the script for a different binary name. Default isbiorouter
Usage:
biorouter completion zsh > ~/.biorouter-completion.zshNote. biorouter stores sessions in a SQLite database (
sessions.db) rather than individual.jsonlfiles, a change introduced in version 1.10.0. Sessions that predate the change are automatically imported into the database. Legacy.jsonlfiles remain on disk but are no longer managed by biorouter.
Start or resume interactive chat sessions.
Basic Options:
--session-id <session_id>: Specify a session by its ID (e.g., '20251108_1')-n, --name <name>: Give the session a name--path <path>: Legacy parameter for specifying session by file path-r, --resume: Resume a previous session--history: Show previous messages when resuming a session--debug: Enable debug mode to output complete tool responses, detailed parameter values, and full file paths--max-tool-repetitions <NUMBER>: Set the maximum number of times the same tool can be called consecutively with identical parameters. Helps prevent infinite loops.--max-turns <NUMBER>: Set the maximum number of turns allowed without user input (0means Unlimited, the default)
Model Options:
--provider <PROVIDER>: OverrideBIOROUTER_PROVIDERfor this run. Available providers includeopenai,anthropic,google,ollama,llamacppanddatabricks--model <MODEL>: OverrideBIOROUTER_MODELfor this run. The model must be supported by the provider in use
Extension Options:
--with-extension <command>: Add stdio extensions--with-streamable-http-extension <url>: Add remote extensions over Streamable HTTP--with-builtin <id>: Enable built-in extensions (e.g., 'developer', 'computercontroller')
Usage:
# Start a basic session
biorouter session -n my-project
# Resume a previous session
biorouter session --resume -n my-project
biorouter session --resume --session-id 20251108_2
biorouter session --resume --path ./session.json # exported session
biorouter session --resume --path ./session.jsonl # legacy session storage
# Start with extensions
biorouter session --with-extension "npx -y @modelcontextprotocol/server-memory"
biorouter session --with-builtin developer
biorouter session --with-streamable-http-extension "http://localhost:8080/mcp"
# Advanced: Mix multiple extension types
biorouter session \
--with-extension "echo hello" \
--with-streamable-http-extension "http://localhost:8080/mcp" \
--with-builtin "developer"
# Control session behavior
biorouter session -n my-session --debug --max-turns 25List all saved sessions. A chat that has asked decision models shows how many calls it made at
the end of its row (- 642 decisions), and the JSON carries it as decision_count. The calls
themselves are listed by session decisions, never as rows here.
Options:
-f, --format <format>: Specify output format (textorjson). Default istext--ascending: Sort sessions by date in ascending order (oldest first)-w, --working_dir <path>: Filter sessions by working directory-l, --limit <number>: Limit the number of results--subagents: Include subagent runs, nested under the session that spawned them (and sessions that have no messages)--include-empty: Include sessions that have not recorded a message yet — what abiorouterrun that exited before its first prompt,doctor --fixandterm initleave behind. They are hidden by default, as they are in the desktop app's History
Usage:
# List all sessions in text format (default)
biorouter session list
# List sessions in JSON format
biorouter session list --format json
# Sort sessions by date in ascending order
biorouter session list --ascending
# Filter sessions by working directory
biorouter session list -w ~/projects/myapp
# List only the 10 most recent sessions
biorouter session list --limit 10
# Show subagent runs too, grouped under the session that spawned them
biorouter session list --subagentsBy default a listing shows only your own chats and scheduled runs — the work an
agent delegates to a subagent is filtered out, so a fan-out of five children is
invisible from the terminal. --subagents includes those runs and nests each one
under the session that spawned it:
20260731_1 - Migration review - 2026-07-31 15:04:11 UTC
└─ audit the migration for data loss [◐ running] 20260731_2 gpt-5.5 14 msgs 2026-07-31 15:09
└─ benchmark the covering index [● completed] 20260731_3 gpt-5.5 8 msgs 2026-07-31 15:07
Each child line carries its own label, its session id, the model it ran on and its message count —
enough to tell two siblings of one fan-out apart, and enough to hand the id to
biorouter session --resume or biorouter session watch. The state is the one
session subagents draws: ◐ running while the
daemon reports a turn in flight, otherwise the outcome the run recorded when it
ended (● completed, ⏸ blocked, ✗ failed, ✗ incomplete). A run that is not
running and recorded no outcome reads · ended. In --format json each row
carries the same outcome (or null) and model fields.
Two behaviours differ from a plain listing:
--limitcounts top-level sessions, not rows.--limit 5returns five parents with all of their children, rather than five rows total. Truncating before grouping would let one parent's six children consume the whole budget.- Live/finished state is read from a running daemon, which is the only
authority on it (
biorouterdtracks in-flight turns in memory). When no daemon can be reached — none running, orBIOROUTER_SERVER__SECRET_KEYnot set or not matching — a run with no recorded outcome reads· state unknownand the reason is printed once on stderr. It never readsended, because "finished" and "not knowable from here" are different answers and only one of them is safe to guess. A recorded outcome is shown either way, because it was written when the run ended.
--name resolves subagent runs too, so a run found this way can be addressed by
its label as well as by its id.
Remove one or more saved sessions.
Options:
--session-id <session_id>: Remove a specific session by its session ID — any session, including a subagent run or a session with no messages-n, --name <name>: Remove the one session carrying this name. When several sessions share it (every session a barebioroutercreates is listed asNew chatuntil it is renamed), nothing is removed and the matching IDs are listed-r, --regex <pattern>: Remove every session whose ID matches a regex pattern, among the sessionssession listshows--include-empty: With--regexor the picker, also match sessions that have not recorded a message yet--subagents: With--regexor the picker, also match subagent runs (and sessions with no messages)-y, --yes: Remove without asking for confirmation. Required when the command is not run from a terminal--path <path>: Remove a specific session by its file path (legacy)
Usage:
# Interactive removal (prompts you to choose sessions)
biorouter session remove
# Remove a specific session by ID
biorouter session remove --session-id 20251108_3
# The same, from a script: no confirmation prompt
biorouter session remove --session-id 20251108_3 --yes
# Remove a specific session by name
biorouter session remove -n my-project
# Remove all sessions starting with "project-"
biorouter session remove -r "project-.*"
# Remove all sessions containing "migration"
biorouter session remove -r ".*migration.*"Warning. Session removal is permanent and cannot be undone. Unless you pass
--yes, biorouter shows which sessions will be removed and asks for confirmation before deleting. Asking needs a terminal: without one — in a script or a pipe, even withypiped in — the command removes nothing and exits with status2, asking for--yes.
Removing a session also removes its per-turn usage records — which model and provider answered each reply, when, and how many tokens it used — whether you remove it here or delete it from the desktop app's chat history. The tokens it spent are first added to an anonymous total, kept per day, model and provider with nothing that identifies the chat, so biorouter usage and the desktop app's Usage panel still match your provider's own billing meter. The Home heatmap and its token tiles count only the chats that still exist.
It removes the rest of what was kept under the session's ID too: the chat's text in the search index that Chat Recall reads, its checkpoints (the records and the snapshots of its working directory), and any cross-institution data flow you accepted in it. Biorouter also no longer hands a removed session's ID to the next session it creates, so nothing still kept under that ID can be read as a newer chat's. (Before, removing the newest chat of the day and starting another gave the new chat the same ID.) Subagent runs the chat started are separate sessions and are kept; session list --subagents shows them, and you can remove them with --session-id or --regex ... --subagents.
Export sessions in different formats for backup, sharing, migration, or documentation purposes.
Options:
--session-id <session_id>: Export a specific session by ID-n, --name <name>: Export a specific session by name--path <path>: Export a specific session by file path (legacy)-o, --output <file>: Save exported content to a file (default: stdout)--format <format>: Output format:markdown,json,yaml. Default ismarkdown
Export Formats:
json: Complete session backup preserving all data including conversation history, metadata, and settingsyaml: Complete session backup in YAML formatmarkdown: Default format that creates a formatted, readable version of the conversation for documentation and sharing
Usage:
# Interactive export
biorouter session export
# Export specific session as JSON for backup
biorouter session export -n my-session --format json -o session-backup.json
# Export specific session as readable markdown
biorouter session export -n my-session -o session.md
# Export to stdout in different formats
biorouter session export --session-id 20251108_4 --format json
biorouter session export -n my-session --format yaml
# Export session by path (legacy)
biorouter session export --path ./my-session.jsonl -o exported.mdsession watch, session send, session attach, and session cancel do not open an agent in your terminal. They talk to a running biorouterd over HTTP, and the daemon stays the only writer to the conversation — which is what makes it safe to join a session another process is already running.
Each of them needs two things:
- A daemon listening on
127.0.0.1. The port comes fromBIOROUTER_PORT, defaulting to3000. If nothing answers, the command stops withno Biorouter daemon is listening on 127.0.0.1:<port>. BIOROUTER_SERVER__SECRET_KEYset in your shell, matching the key the daemon was started with.biorouterdgenerates a random key when the variable is unset, and no client can then authenticate. Start the daemon with a key you choose and reuse it:
BIOROUTER_SERVER__SECRET_KEY=<key> biorouterd agent
BIOROUTER_SERVER__SECRET_KEY=<key> biorouter session watch <session_id>A mismatched key surfaces as HTTP 401 with that hint attached.
The user-action key, when the daemon has one. A daemon started with a user-action key (its
SHA-256 digest piped to biorouterd agent on stdin) wants the raw key before it lets anyone stop or
steer a turn, and before it takes a message into a subagent's session. send, attach and cancel
never ask for it up front: each makes its request without the key, and only if the daemon refuses
it for want of one asks you for it once, without echo, and tries again. attach asks as it joins,
before it reads anything you type. A daemon started without a key — biorouter serve, or
biorouterd agent with nothing piped in — is never asked about one. Such a daemon refuses every
request to stop, steer or send to a subagent's session, and the command prints its reason. To supply the key
without a prompt, pass --user-action-key-stdin and pipe the raw key as the first line of stdin.
Never put it in an argument, an environment variable or a config file. See
Workspace control for how to start a
daemon with a key.
Stream a session's live events into your terminal — the same frames biorouter Desktop renders, printed as lines. Watching is read-only: it never writes to the conversation, and stopping the watch never stops the session.
Requires a running daemon. See Live-session commands and the daemon.
Arguments:
<SESSION_ID>(required): The session to observe
Options:
--follow: Keep watching after the current turn ends. Without it,watchexits as soon as the session reports a finish or an error
Usage:
# Watch the running turn and exit when it ends
biorouter session watch 20251108_2
# Keep watching across turns until you press Ctrl+C
biorouter session watch 20251108_2 --followSend a prompt into an existing session and stream the resulting turn, without opening an interactive chat. This is the scriptable equivalent of typing one message into a session.
Requires a running daemon. See Live-session commands and the daemon.
Arguments:
<SESSION_ID>(required): The session to send to<TEXT>(required): The prompt text
Options:
--no-wait: Return as soon as the daemon accepts the turn, printing[started] turn <turn_id> in session <session_id>, instead of streaming it to completion--user-action-key-stdin: Read the raw user-action key from the first line of stdin instead of being asked for it when the daemon wants it. See the user-action key
Usage:
# Send a prompt and watch the turn to completion
biorouter session send 20251108_2 "summarize what you have found so far"
# Kick off a turn and return immediately
biorouter session send 20251108_2 "run the full test suite" --no-waitA turn started with --no-wait runs on in the daemon: biorouter session watch <session_id> follows it and biorouter session cancel <session_id> stops it.
The daemon stops a turn once nothing has been attached to its reply stream for
five minutes, and session watch does not count as attached — so --no-wait
suits turns shorter than that. Leave --no-wait off for a longer one.
Join a session that is running right now. attach prints the conversation so far, then follows it live, and anything you type is delivered into the running turn (or starts a new one if the session is idle).
Requires a running daemon. See Live-session commands and the daemon.
Arguments:
<SESSION_ID>(optional): The session to attach to
Options:
--name <NAME>: Attach by session name instead of ID. Refuses, and lists the candidates, if several sessions share that name--of <PARENT_ID>: Attach to the running subagent of this parent session. Errors, listing what it found, if that parent has no subagent with a turn in flight or has more than one--read-only: Observe only — do not read stdin and do not send anything--user-action-key-stdin: Read the raw user-action key from the first line of stdin, before the lines you steer with, instead of being asked for it when the daemon wants it. See the user-action key
Give exactly one target: a session ID, --name, or --of. Passing none, or more than one, is an error. biorouter session list --subagents lists the sessions and subagent runs you can address.
Ctrl+C detaches and leaves the session running. If a turn you started from here is still streaming, the first Ctrl+C warns that leaving would cancel it; press it again to leave anyway.
Usage:
# Join a running session by ID
biorouter session attach 20251108_2
# Join by name
biorouter session attach --name migration-review
# Join the running subagent of a parent session
biorouter session attach --of 20251108_2
# Follow a run without being able to steer it
biorouter session attach 20251108_2 --read-onlyNote. Use
attach, notsession --resume, on a session that is running. Resuming builds a second agent over the same conversation inside your CLI process, and that agent does not share the daemon's turn lock — leaving two uncoordinated writers on one session. Reserve--resumefor finished transcripts.
Stop the turn a session is currently running. This is the same action as the Stop button in biorouter Desktop.
Requires a running daemon. See Live-session commands and the daemon.
Arguments:
<SESSION_ID>(required): The session whose running turn should be stopped
Options:
--user-action-key-stdin: Read the raw user-action key from the first line of stdin instead of being asked for it when the daemon wants it. See the user-action key
Cancelling is idempotent: a session with no turn in flight is not an error, it reports nothing to cancel: this session had no turn in flight.
Subagents. A cancel also stops every subagent the chat started that still has work in flight
(a delegated run, running or queued; a later turn in its tab; a continuation waiting to start),
even when the chat itself was idle, and prints a second line, "Also stopped 2 subagents this chat
started." ("1 subagent" for one). Each stopped subagent ends incomplete, and its result opens
"Stopped because the person stopped the chat that started this subagent." Ctrl-C during a turn in
biorouter session (the terminal UI and the classic REPL) and in biorouter run does the same and
prints the same line. Cancelling a tool approval ends the turn and leaves the subagents running.
Usage:
# Stop the turn a session is running
biorouter session cancel 20251108_2List the subagents a session started, oldest first, with what each one is doing and how it is set up. This is the terminal counterpart of the list in the chat's side panel in biorouter Desktop.
Arguments:
<SESSION_ID>(required): The parent chat whose subagents to list
Options:
-f, --format <FORMAT>: Output format:text(default) orjson. The JSON is exactly what the daemon'sGET /sessions/{session_id}/subagentsroute answers, with camelCase fields and snake_case status values--watch: Redraw every 2 seconds until you pressCtrl+C. On a terminal the text redraws in place; piped, or with--format json, one frame is printed per refresh--user-action-key-stdin: Read the raw user-action key from the first line of stdin, so a daemon that holds a key lists a private chat's subagents. See the user-action key
What it prints. When the chat's agent declared a phase plan, the plan comes first: its title, then one line per phase with the phase's status, title and progress (done/total, plus how many are running, blocked, failed or quiet). Then one numbered entry per subagent:
Plan: Migrate the auth layer
● Research 3/3 done
◐ Implementation 1/4 done · 2 running · 1 quiet
○ Verification 0/0 done
Subagents of session 20261104_1 (2):
1. ● survey the auth code (researcher) completed · phase Research
20261104_2 gpt-5.5 3 ext · 1 skill · 0 kb 12 tool calls 1m42s
Found three call sites that bypass the session check.
2. ◐ port the login handler running · quiet 4m00s · phase Implementation
20261104_5 gpt-5.5 2 ext · 0 skills · 1 kb 7 tool calls 6m10s
Follow a running one: biorouter session attach <session id>. Read a finished one: biorouter session export --session-id <session id>.
A gated phase's line adds who set its gate and your decision, as in "· gate set by the agent,
passed by you", and " · needs you" when the gate waits for you; a phase an earlier gate holds adds
" · waits for Build's gate". A subagent a monitor stopped shows the monitor's sentence as its last
line ("Stopped by monitor 'Off task' (set by this chat's agent): …"). To see or change the gates
themselves, use session gates.
The status glyphs are ○ queued, ◐ running (or stopping), ● completed, ⏸ blocked (it stopped to ask its parent a question), ✗ failed or incomplete, ⊘ interrupted (its process went away mid-run) and · unknown. "Quiet" means a running subagent has recorded no activity for 3 minutes or more, the same threshold the phase line counts with and the desktop panel and the TUI pane use. It is a hint, not a verdict that the run is stuck. The last line of an entry is the first line of the subagent's result, its error, or the question it is blocked on.
With and without a daemon. When a daemon is reachable (see Live-session commands and the daemon), the list comes from it, so a subagent's live state is known. Without one, the list is read from the local session store and a note on stderr says so: a subagent that was running then reads as interrupted or unknown, because nothing in your terminal is running it. A daemon that answers and refuses is never worked around this way.
Privacy. A private chat's subagents are listed only to a caller that may open the chat, exactly as GET /sessions/{session_id} decides. A terminal configured for a public model is refused a private chat on either path, and a subagent more private than its parent is left out of the list and out of every phase's counts.
Usage:
# List a chat's subagents
biorouter session subagents 20261104_1
# Keep the list on screen while the work runs
biorouter session subagents 20261104_1 --watch
# The same data for a script
biorouter session subagents 20261104_1 --format jsonList the decision-model calls one chat made. Decision models can be asked thousands of times in
one chat (a monitor after every iteration, a decide call over hundreds of chunks), so the
output leads with aggregates and pages through individual calls. This is the terminal counterpart
of the desktop's decision history view.
Arguments:
<SESSION_ID>(required): The chat whose decisions to list
Options:
--source <SOURCE>: Only calls frommonitor,tool(the agent'sdecidetool),gate(a phase gate),chat(a chat whose main model is a decision model) oradhoc(abiorouter decide --sessioncall)--origin-id <ID>: Only calls from one monitor, tool call or phase--turn <TURN_ID>: Only calls made in one turn--batch <BATCH_ID>: Only calls of onedecidebatch--outcome <OUTCOME>: Onlypassed,failed,decided,errororrefusedcalls--min-p <P>,--max-p <P>: Only calls whose lowest pass probability is in the range--errors: Only calls that failed or were refused-l, --limit <N>: Calls per page, 20 by default and at most 200--before <CURSOR>: The next page: the cursor the previous page's last line printed--json: Print the summary and the page as JSON--user-action-key-stdin: Read the raw user-action key from the first line of stdin, so a daemon that holds a key lists a private chat's decisions
What it prints. First the aggregates, one line per source and criterion (count, how many
passed and failed, errors, the last answer and its probability, the model), then one line per
source for each recent turn. A page of calls follows, newest first: the time, the source, the
criterion (and the chunk), the answers, the probability, the outcome, the model and the latency.
A model is named by its short name: an Ollama tag as it is (tev1:0.8b), and a Baseten model by
its alias (joey-1), never by the slug the log records; --json keeps the slug.
Decisions in session 20261104_1: 618 calls
By source:
monitor Form reached 18 checks · 18 failed · last no P 0.31 ██▌░░░░░ · tev1:0.8b
tool decide 600 calls · 41 passed · 557 failed · 2 errors · last yes P 0.93 ███████▌ · tev1:0.8b
Recent turns:
0193a1b2 monitor Form reached 18 checks · 18 failed · last P 0.31 · 10:42:01 to 10:44:12
Calls, newest first (20 of 618):
11-04 10:44:12 monitor Form reached no P 0.31 ██▌░░░░░ failed tev1:0.8b 22 ms
...
More: biorouter session decisions 20261104_1 --before <cursor>
With and without a daemon, and privacy. As for session subagents:
with a reachable daemon the calls come from it; without one they are read from the local session
store and a note on stderr says so. A private chat's decisions are listed only to a caller that
may open the chat, on either path.
Usage:
# A chat's decision activity, aggregates first
biorouter session decisions 20261104_1
# The errors of one decide batch
biorouter session decisions 20261104_1 --source tool --batch <batch id> --errors
# The calls of one turn, 50 at a time, as JSON
biorouter session decisions 20261104_1 --turn <turn id> --limit 50 --jsonList, add or remove the decision-model monitors that watch a chat. A monitor asks a decision
model one question about the chat's recent messages after each agent iteration, or once as a turn
ends. When the probability of the wanted answer reaches its threshold (or falls below it, for a
--below monitor), it stops the turn, shows a notice, or tells the model. A turn that ran
monitors shows one summary line per monitor, never one line per check. A chat takes at most 8
monitors. They are the same monitors the desktop's Monitors dialog edits, and a change applies at
the chat's next check, in a running turn too.
Arguments:
<SESSION_ID>(required): The chatlist [--json]: The chat's monitors and its default decision modeladd: Add a monitor, with the options belowremove <MONITOR>: Remove a monitor by its id or name
add options:
--name <NAME>(required): A short name, at most 60 characters. The id is made from it--ask <QUESTION>(required): The question about the chat's recent messages--threshold <P>(required): Fire when the probability of the wanted answer is at leastP(above 0, at most 1), or falls below it with--below--below: Fire when the probability falls below--thresholdinstead of reaching it--action <ACTION>:notify(the default) shows a notice,stopends the turn and hands control back to you,steertells the model what the monitor found--timing <TIMING>:post-iteration(the default) asks after every iteration of the agent loop,turn-endonce as a turn ends--model <PROVIDER/MODEL>: The decision model; without it the chat's default--option <OPTION>: The answer whose probability is compared:yes(the default) ornoon a yes/no question, or one of the--choiceoptions--choice <NAME[=DESCRIPTION]>: Make the question a choice; repeat it once per option--consecutive <N>: Checks in a row on the firing side of the threshold before it fires (1 to 5)--rearm-below <P>: After firing, fire again only once the probability has dropped belowP, which must be under--threshold. For a stop monitor it is also what keeps later turns going: without it a stop monitor stops every turn in which its answer holds--rearm-above <P>: For a--belowmonitor, the same the other way: re-arm only once the probability rises toPor above, which must be over--threshold--on-error <POLICY>:notify(the default) shows a notice, and after three failures in a row the monitor is off for the rest of the turn;pausestops the turn whenever a check fails--max-checks <N>: At most this many checks per turn (default 50, at most 200)--subagents: Also watch the subagents this chat starts: the monitor is copied onto each of them, and onto the ones running when it is saved. Refused on a subagent's own chat with "Only a chat that starts subagents can share its monitors with them."--id <ID>,--disabled: Choose the id, or add the monitor switched off
A direction mixed up is refused before anything is sent: "--rearm-below is for a monitor that
fires at or above its threshold; a --below monitor re-arms with --rearm-above", "--rearm-above is
for a --below monitor; …", "--rearm-above must be over --threshold: …", and "--option on a yes/no
question is yes or no".
What list prints. One entry per monitor, its rule first (P(yes) ≥ 0.85, or P(yes) < 0.30
for a below monitor), then what it does and when, and then what else is set: "re-arms below 0.40"
or "re-arms above 0.60", "stops again while it holds" for a stop monitor with no re-arm level, "an
error pauses the turn", "also watches the subagents this chat starts". A monitor on a subagent's
chat names who set it: "set by the agent of chat 20261106_1", or "from the chat that started this
subagent (20261106_1)" for a copy.
Removing. You may remove any monitor, an agent's included, and the message says who set it ("It was set by the agent of chat 20261106_1."). A copy you remove comes back when the parent's monitors are saved again while this subagent runs, and the message says so.
What a save checks. Before anything is stored, the command checks that what the change turns on can run for this chat: the model is there, takes the question and will load, and the chat's privacy tier, an institution or a Crew grant does not refuse it. Nothing of the chat is sent (What a save checks). With a daemon, a 400 or 422 prints the daemon's sentence as it is and never asks for a key. Without one the same check runs locally, prints "Error: Nothing was saved: …" and exits 1.
With and without a daemon. With a reachable daemon the monitors are read and written through
it. A change names where the chat's content goes, so a daemon started with a user-action key
wants the key: when it refuses a change with 403, the command shows its reason and asks for the
key once, without echo, and pressing Enter stops. --user-action-key-stdin supplies it without a
prompt. Without a daemon the monitors are read and written in the local session store, under the
same privacy rule as session decisions and with no key, whatever kind of daemon the store
belongs to. A daemon with no user-action key (biorouter serve) takes a change without the key
too, except to a subagent's chat. Copies onto running subagents are made only with a daemon.
What a steer monitor tells the model. The note quotes the monitor's name and question as
JSON strings inside a <decision-monitor> frame and says they come from the chat's monitor
settings, not from the user, so a question cannot pass itself off as an instruction. The notice
you see when it fires shows the question the model was told, on one line and clipped to 120
characters:
Monitor 'Form reached' fired (P 0.93, threshold 0.50) and told the agent. Its question: "Is the intake form complete?"
Usage:
# The chat's monitors
biorouter session monitors 20261104_1 list
# Stop the turn once the agent has reached the consent form, and stay quiet after that
biorouter session monitors 20261104_1 add --name "Form reached" \
--ask "Has the agent reached the consent form page?" --threshold 0.85 --action stop \
--rearm-below 0.40
# Stop the turn while the chance that tests pass falls below 0.30, on this chat and its subagents
biorouter session monitors 20261104_1 add --name "Tests failing" \
--ask "Do the latest results show the tests passing?" --threshold 0.30 --below \
--action stop --on-error pause --subagents
# Remove it again
biorouter session monitors 20261104_1 remove form-reachedList a chat's phase gates, or set, remove, pass, skip or retry one. A phase gate asks a decision model yes/no questions about a phase's hand-off before the phase may complete, and holds every phase listed after it until it passes. Whoever set a gate (the agent, you, or the workflow the chat was started from), the agent cannot change, remove or skip it; only you can, here or in the app. See Decision gates.
Arguments and subcommands:
<SESSION_ID>(required): The chat whose plan holds the phaseslist(the default, also with no subcommand): Every phase and its gateset <PHASE> --ask NAME=QUESTION ...: Set or replace a phase's gate. The gate is yours, its attempts start over, and a phase its old gate blocked goes back in progressremove <PHASE>: Remove a phase's gate, which releases the phases after itpass <PHASE>,skip <PHASE>: Pass or skip a gated phase without asking its gate: at once, or, while its subagents still work, once they finish and the agent completes itretry <PHASE>: Give a gate its attempts again, so the agent can complete the phase once more to ask it
set options:
--ask <NAME=QUESTION>(required, up to 8): One yes/no question the decision model answers from the phase's hand-off; every question must pass. The text splits at the first=, and the name is made into lowercase letters, digits,_and---threshold <P>: The P(yes) every question must reach, from 0 to 1 (default 0.80). The 0.50 floor the agent's gates have does not apply to yours--attempts <N>: Failed evaluations allowed before the phase is blocked, 1 to 5 (default 3)--model <PROVIDER/MODEL>: The decision model; without it the chat's default--allow-uncalibrated: Accept probabilities a model states in words
Options for every subcommand:
--format <FORMAT>:text(the default) orjson. Forlist, JSON is the plan asGET /sessions/{session_id}/subagentsreturns it; for a write,{message, applied, plan}--user-action-key-stdin: As forsession monitors
What list prints. One entry per phase: its id, title and status, then its gate (the
questions, the threshold, the attempts used, the last result, whether it is blocked, who set it
and your decision), the model it asks, how many times its attempts were given again, the last
error, what it holds, a "needs you" line when it waits for you, and "your pass stands" or "your
skip stands" on an open phase. A held phase reads "waits for build's gate", naming the holder by
its id, which is what the subcommands take.
With and without a daemon. With a daemon, list reads GET /sessions/{session_id}/subagents
and the writes go through the phase-gate routes. A 400, 404, 409 or 422 prints the daemon's
sentence alone; a 403 adds the advice about the key and, on a terminal, asks for it once. Without
a daemon the gates are read and written in the local session store, under the privacy rule of
session monitors, and a gate's model is checked as the route checks it: "Phase "build"'s gate
cannot be answered in this chat: …. Nothing was changed."
Usage:
# A chat's gates
biorouter session gates 20261106_1
# Your own gate on the research phase, at a threshold of 0.30
biorouter session gates 20261106_1 set research \
--ask facts="Does the hand-off list two facts about the moon?" --threshold 0.3
# Let a blocked phase through, or give its gate another three tries
biorouter session gates 20261106_1 pass build
biorouter session gates 20261106_1 retry buildGenerate a comprehensive diagnostics bundle for troubleshooting issues with a specific session.
Options:
--session-id <session_id>: Generate diagnostics for a specific session by ID-n, --name <name>: Generate diagnostics for a specific session by name--path <path>: Generate diagnostics for a specific session by file path (legacy)-o, --output <file>: Save diagnostics bundle to a specific file path (default:diagnostics_{session_id}.zip)
What's included:
- System Information: App version, operating system, architecture, and timestamp
- Session Data: Complete conversation messages and history for the specified session
- Configuration Files: Your configuration files (if they exist)
- Log Files: Recent application logs for debugging
Usage:
# Generate diagnostics for a specific session by ID
biorouter session diagnostics --session-id 20251108_5
# Generate diagnostics for a session by name
biorouter session diagnostics -n my-project-session
# Save diagnostics to a custom location
biorouter session diagnostics --session-id 20251108_5 -o /path/to/my-diagnostics.zip
# Interactive selection (prompts you to choose a session)
biorouter session diagnosticsWarning. Diagnostics bundles contain your session messages and system information. If your session includes sensitive data (API keys, personal information, proprietary code), review the contents before sharing publicly.
Tip. Generate diagnostics before reporting bugs to provide technical details that help with faster resolution. The ZIP file can be attached to GitHub issues or shared with support.
Rename a saved session. This reads and writes the local session store directly, so it needs no running daemon.
Options:
--session-id <session_id>: Rename a specific session by ID-n, --name <name>: Rename a specific session by its current name--path <path>: Rename a specific session by file path (legacy)--new-name <NAME>(required): The new name for the session
If you supply no identifier, biorouter prompts you to choose a session. The new name is trimmed, must not be empty, and must be at most 200 characters.
Usage:
# Rename a session by ID
biorouter session rename --session-id 20251108_6 --new-name migration-review
# Rename a session by its current name
biorouter session rename -n old-project --new-name new-project
# Interactive selection (prompts you to choose a session)
biorouter session rename --new-name migration-reviewBranch a stored conversation into a brand-new session that keeps the history up to the last complete assistant answer. The original session is left untouched. Like rename, this works against the local session store and needs no running daemon.
The new session ID is printed to stdout (so it can be captured in a script); the human-readable summary goes to stderr.
Options:
--session-id <session_id>: Diverge a specific session by ID-n, --name <name>: Diverge a specific session by name--path <path>: Diverge a specific session by file path (legacy)--branch-name <NAME>: Name for the new branched session. Without it, the branch is named after its parent with a(branch N)suffix
If you supply no identifier, biorouter prompts you to choose a session.
Usage:
# Branch a session, letting biorouter name the branch
biorouter session diverge --session-id 20251108_6
# Branch a session and name the branch
biorouter session diverge -n migration-review --branch-name try-covering-index
# Resume the branch that was just created
biorouter session --resume --session-id 20251108_7Lower a session's privacy classification from private to public, so it may run on any model. The change is confirmed at the terminal and recorded in the classification ledger. It works by session id, including for sessions no listing shows: subagent runs, --no-session runs and terminal sessions.
Usage:
biorouter session declassify 20251108_7Execute commands from an instruction file or stdin.
Input Options:
-i, --instructions <FILE>: Path to instruction file containing commands. Use-for stdin-t, --text <TEXT>: Input text to provide to biorouter directly--system <TEXT>: Provide additional system instructions to customize the agent's behavior--workflow <WORKFLOW_NAME_OR_PATH> <OPTIONS>: Load a custom workflow in current session--params <KEY=VALUE>: Key-value parameters to pass to the workflow file. Can be specified multiple times--sub-workflow <WORKFLOW>: Specify sub-workflows to include alongside the main workflow. Can be specified multiple times
Session Options:
-s, --interactive: Continue in interactive mode after processing initial input-n, --name <name>: Name for this run session (e.g.daily-tasks)-r, --resume: Resume from a previous run--path <PATH>: Path for this run session (e.g../playground.jsonl). Used for legacy file-based session storage.--no-session: Run biorouter commands without creating or storing a session file
Extension Options:
--with-extension <COMMAND>: Add stdio extensions (can be used multiple times)--with-streamable-http-extension <URL>: Add remote extensions over Streamable HTTP (can be used multiple times)--with-builtin <name>: Add builtin extensions by name (e.g., 'developer' or multiple: 'developer,github')
Control Options:
--debug: Output complete tool responses, detailed parameter values, and full file paths--max-tool-repetitions <NUMBER>: Maximum number of times the same tool can be called consecutively with identical parameters. Helps prevent infinite loops--max-turns <NUMBER>: Maximum number of turns allowed without user input (0means Unlimited, the default)--explain: Show a workflow's title, description, and parameters, and a "Supervision:" section when it declares monitors or a phase plan: the default decision model, one line per monitor, the plan and each gate, and a note that these apply once to the new chat and that a monitor's decision model reads that chat's newest messages--render-workflow: Print the rendered workflow instead of running it-q, --quiet: Quiet mode. Suppress non-response output, printing only the model response to stdout--output-format <FORMAT>: Output format (text,json, orstream-json). Default istext. Use JSON structured output for automation and scripting:jsonfor results after completion,stream-jsonfor events as they occur--provider: Specify the provider to use for this session (overrides environment variable)--model: Specify the model to use for this session (overrides environment variable)
A workflow's monitors and phase plan. A workflow that declares decision_settings or an
orchestration_plan (the workflow schema reference)
applies them once, to the new chat the run makes, before its first message. When they cannot be
applied (a decision model the chat may not use, a plan outside the Autonomous approval mode, a
block that is malformed), the run prints "The workflow was not started: " and the reason, removes
the chat it made, and exits 1. With --resume on a chat that already has messages, nothing is
applied again, and one line says which case it is: "This chat already has messages. The monitors
and phase plan this workflow declares were applied when the chat started and are not applied
again, so the chat keeps them as they are now." when this workflow seeded it, or "This chat
already has messages, so the monitors and phase plan this workflow declares were not applied to
it. Run the workflow in a new chat to get them." otherwise. With --resume on a chat that has no
messages yet but that another workflow already set up, nothing is applied either, and the line
reads "Another workflow already gave this chat its monitors and phase plan before its first
message, so the ones this workflow declares were not applied to it. Run the workflow in a new chat
to get them." Resuming such a chat with the workflow that set it up says nothing. The run then goes
on.
Usage:
# Run from instruction file
biorouter run --instructions plan.md
# Load a workflow with a prompt that biorouter executes and then exits
biorouter run --workflow workflow.yaml
# Load a workflow and stay in an interactive session
biorouter run --workflow workflow.yaml --interactive
# Load a workflow in debug mode
biorouter run --workflow workflow.yaml --debug
# Show workflow details
biorouter run --workflow workflow.yaml --explain
# Run a workflow with parameters
biorouter run --workflow workflow.yaml --params environment=production --params region=us-west-2
# Run instructions from a file without session storage
biorouter run --no-session -i instructions.txt
# Run with a specified provider and model
biorouter run --provider anthropic --model claude-sonnet-5 -t "initial prompt"
# Run with limited turns before prompting user
biorouter run --workflow workflow.yaml --max-turns 10Ask a decision model one typed question about a text. A decision model answers with a typed answer (yes or no, one of named options, or a level of a score) and the probability of every outcome, in tens of milliseconds, instead of writing text. The command prints a probability bar per outcome, the answer and its probability, whether the probabilities are calibrated, the model and the latency.
Options:
--ask <QUESTION>(required): The question to ask about the text--model <PROVIDER/MODEL>: The decision model, asbiorouter models decision listnames it. Without it the model is the chat's default decision model (with--session), elseBIOROUTER_DECISION_MODEL(orBIOROUTER_DECISION_PROVIDER), else the first model in the list that may read the text: local first,tev1:0.8bfirst among the Ollama models, never a copy Ollama cannot load. The model is chosen once, before anything is sent, and used for every request of the run--choice <NAME[=DESCRIPTION]>: Makes the question a choice; repeat it once per option (2 to 26). The model reads the description, and the answer is the name--levels <LEVEL,LEVEL,...>: Makes the question a score with 1 to 10 levels, lowest first--threshold <P>: Pass when the probability of the wanted answer is at leastP(0 to 1)--threshold-on <ANSWER>: What the threshold is on:yes(the default) ornofor a yes/no question, an option name for a choice, or a level (its text, or its number from 0) for a score, which then counts that level and every level above it--text <TEXT>: The text to decide on. Without it, and without--batch, the text is read from stdin--batch <DIR>: Decide once per file in the directory (not recursive, hidden files skipped, in name order), several at a time, and print one row per file and a summary--session <SESSION_ID>: Record the decision in that chat's decision log--name <NAME>: The question's name in the log and the JSON (defaultquestion)--json: Print JSON instead of text
What it prints. For one text:
Does this note state the patient has multiple sclerosis?
▸ yes ███████████████████▋░░░░ 0.82
no ████▍░░░░░░░░░░░░░░░░░░░ 0.18
Answer: yes p 0.82 calibrated ollama_decision/tev1:0.8b 22 ms
Threshold: P(yes) 0.82 ≥ 0.80, passed
The Answer line names the model as provider/model, a Baseten one by its alias
(baseten_decision/joey-1), the same name the stderr lines below and models decision list
use; --json keeps Baseten's slug in the decision's model.
calibrated means the probabilities are the model's own distribution, or token probabilities
that covered at least 90% of the probability. uncalibrated says where the number came from
instead, for example a number the model wrote. Either way these are model probabilities that have
not been validated on your data, and a line on stderr says so. With --batch, one row per file
shows the answer, its probability, a short bar and whether it passed the threshold. The last
lines give the count of each answer, how many passed, how many failed with an error, and each
error in full. A file over 4 MiB, an empty file, or one that is not UTF-8 text is reported as an
error row and not sent. So is a text too large for the model's window; split it and ask again.
On stderr. Besides the line on probabilities, a run can print these:
warning: <model> is hosted outside your institution (public tier). Do not send it private or patient data.
note: this chat is not private, so the text you send is kept in its decision history, where public chats can read it. For private text, leave out --session or name a private chat.
usage: 2 requests on baseten_decision/mammoth-1: 1,200,000 input and 3 output tokens, about $0.12. Without --session this is not recorded in Biorouter's usage.
warning:comes before anything is sent, when the model is hosted outside your institution. It is left out for a private chat with privacy tiers on, which refuses such a model anyway.note:comes before anything is sent, with--sessionnaming a chat that is not private and privacy tiers on.usage:comes after a run without--session, because nothing in Biorouter records that run. A local model has no price, so its line gives tokens only, and a run whose model reported no usage prints none. A run with--sessionis counted inusageinstead.- With
--session, a run whose decision requests are still in flight when it ends waits up to 30 seconds for them, so their rows are stored and their usage counted, and says so: "waiting up to 30 s for 2 decision request(s) that answered late, so their usage is counted". One still out after that is named: "note: 1 decision request(s) had not answered when this run ended; if the provider bills them, Biorouter does not count it." Nothing is printed when none is in flight.biorouter sessionandbiorouter runwait the same way when they end, before the SessionEnd hooks, and Ctrl-C stops their wait.
Privacy. With --session, the chat's privacy tier decides which decision models may read the
text, exactly as it does for the agent: a private chat never reaches a model hosted outside your
institution, and the refusal comes before anything is sent. Its advice follows where the model
came from: a model you named with --model, or the chat's own default, is refused with "Name a
private decision model", and one the configured default chose with "Set
BIOROUTER_DECISION_MODEL to a private model". The text is yours, not the chat's, and
it is kept in the chat's decision history at the chat's reach, which is why a chat that is not
private gets the note above. Without --session there is no chat to protect and nothing is
logged. When the model the command resolved is hosted outside your institution (Baseten's are),
one line on stderr names it before the text is sent, so you can stop if the text is private.
Because the model is chosen once, a long --batch never moves from a local model to a hosted one
partway: if the chosen model goes away, the run fails.
The question types and their limits descend from Albert Lee's System One prototype in PR #469.
Usage:
# Ask a yes/no question about a note
biorouter decide --model ollama_decision/tev1:0.8b --ask "Is an EDSS score stated?" < note.txt
# A choice, with a threshold on one option
biorouter decide --ask "Which MS course is described?" \
--choice rrms="Relapsing-remitting" --choice spms="Secondary progressive" \
--choice ppms="Primary progressive" --choice none="Not stated" \
--threshold 0.8 --threshold-on rrms --text "Neuro f/u. 34F, RRMS dx 2019."
# One decision per note in a directory, with a summary table. For a screen, a choice with the
# abbreviations spelled out, a meaning for each answer and a threshold near 0.5 keeps true cases
biorouter decide --ask "Does this note state the patient has multiple sclerosis (MS, RRMS, SPMS or PPMS)?" \
--choice yes="The patient has multiple sclerosis (MS, RRMS, SPMS or PPMS)." \
--choice no="The patient does not have multiple sclerosis." \
--threshold 0.5 --threshold-on yes --batch notes/
# Record the decision in a chat's decision log, as JSON
biorouter decide --session 20261104_1 --ask "Is the form complete?" --json < form.txtUsed to evaluate system-configuration across a range of practical tasks.
Usage:
biorouter bench ...etc.Used to validate workflow files, manage workflow sharing, list available workflows, and open workflows in biorouter desktop.
Commands:
deeplink <WORKFLOW_NAME>: Generate a shareable link for a workflow file-p, --param <KEY=VALUE>: Pre-fill workflow parameter (can be specified multiple times)
list [OPTIONS]: List all available workflows from local directories and configured GitHub repositories--format <FORMAT>: Output format (textorjson). Default istext-v, --verbose: Show verbose information including workflow titles and full file paths
open <WORKFLOW_NAME>: Open a workflow file directly in biorouter desktop-p, --param <KEY=VALUE>: Pre-fill workflow parameter (can be specified multiple times)
validate <WORKFLOW_NAME>: Validate a workflow file
Usage:
# Generate a shareable link
biorouter workflow deeplink my-workflow.yaml
# Generate a deeplink and provide parameter values
biorouter workflow deeplink my-workflow.yaml -p environment=production -p region=us-west-2
# List all available workflows
biorouter workflow list
# List workflows with detailed information
biorouter workflow list --verbose
# List workflows in JSON format for automation
biorouter workflow list --format json
# Open a workflow in biorouter desktop
biorouter workflow open my-workflow.yaml
# Open a workflow by name
biorouter workflow open my-workflow
# Open a workflow and provide parameter value
biorouter workflow open my-workflow --param name=myproject
# Validate a workflow file
biorouter workflow validate my-workflow.yaml
# Get help about workflow commands
biorouter workflow helpAutomate workflows by running them on a schedule.
Commands:
add <OPTIONS>: Create a new scheduled job. Copies the current version of the workflow to thescheduled_workflowsdirectory in Biorouter's data directorylist: View all scheduled jobsremove: Delete a scheduled jobsessions: List sessions created by a scheduled workflowrun-now: Run a scheduled workflow immediatelycron-help: Show cron expression examples and help
Options:
--schedule-id <NAME>: A unique ID for the scheduled job (e.g.daily-report)--cron "* * * * * *": Specifies when a job should run using a cron expression--workflow-source <PATH>: Path to the workflow YAML file-l, --limit <NUMBER>: Max number of sessions to display when using thesessionscommand
Usage:
biorouter schedule <COMMAND>
# Add a new scheduled workflow which runs every day at 9 AM
biorouter schedule add --schedule-id daily-report --cron "0 0 9 * * *" --workflow-source ./workflows/daily-report.yaml
# List all scheduled jobs
biorouter schedule list
# List the 10 most recent biorouter sessions created by a scheduled job
biorouter schedule sessions --schedule-id daily-report -l 10
# Run a workflow immediately
biorouter schedule run-now --schedule-id daily-report
# Remove a scheduled job
biorouter schedule remove --schedule-id daily-reportWhich process makes the change. add, remove, list and run-now go to a running daemon when this terminal can reach one. They find it the way session send does: BIOROUTER_SERVER__SECRET_KEY and BIOROUTER_PORT (default 3000) from this shell, and a daemon answering there. That daemon then makes the change itself, so the schedule is live, listed and deletable in the app at once, and run-now runs inside the daemon, where the app's Stop button can reach it. A daemon that rejects the key is reported as an error. The command does not fall back to editing the file behind the daemon's back.
When no daemon can be reached, the command writes schedule.json in Biorouter's data directory itself and says so. For example:
No running Biorouter could be reached from this terminal (no daemon answered on 127.0.0.1:3000), so the job was written to the schedule file directly. A Biorouter that is already running — the desktop app included — picks it up from that file within 60 seconds; if none is running, it first runs the next time Biorouter starts.
This is always the case next to the desktop app. The app's own daemon uses a random port and a new secret every launch, so a terminal cannot reach it. It is also always the case in an agent's shell, because the daemon's secret is never passed to a tool. A running Biorouter polls the file and usually picks up the change within a couple of seconds, but the promise is 60. sessions reads the session store directly and never needs a daemon.
A change made in the file needs a person. A scheduled job is a standing unattended agent run, so add, remove and run-now print what will happen and ask for confirmation before writing the file — and refuse, with exit 2 and nothing written, when stdin and stderr are not both terminals. There is no --yes: a flag that skipped the question would be a flag an agent could pass. list and sessions only read, and ask nothing. To make the change from a script, reach a daemon instead: with BIOROUTER_SERVER__SECRET_KEY and BIOROUTER_PORT pointing at a running one, the daemon makes the change and no terminal is involved.
Run one of the MCP servers bundled with Biorouter as a subprocess speaking MCP over stdio. MCP is the Model Context Protocol, the standard biorouter extensions speak. <name> is not an arbitrary extension name: it must be one of the five bundled servers below, and any other value is refused with Invalid command: <name>.
autovisualiser(Auto Visualiser)computercontroller(the config key whose display name is "Biorouter Copilot"; the key kept its original spelling so existing configs and command lines keep working)webdocuments(the config key whose display name is "Web & Documents")developer(Developer)memory(Memory)
Usage:
biorouter mcp developerRun biorouter as an Agent Client Protocol (ACP) agent server over stdio. This enables biorouter to work with ACP-compatible clients like Zed.
ACP is an emerging protocol specification that standardizes communication between AI agents and client applications, making it easier for clients to integrate with various AI agents.
Usage:
biorouter acpNote. This command is automatically invoked by ACP-compatible clients and is not typically run directly by users. The client manages the lifecycle of the
biorouter acpprocess.
Install and manage extensions. Alias: ext.
Subcommands:
install <PATH>: Install an extension from a.brxtbundle.--env <KEY=VALUE>sets a plain environment variable and--secret <KEY=VALUE>a secret one (repeatable). A secret passed this way is visible tops, so prefer the interactive prompt, or--secret-stdinto readKEY=VALUElines from stdin in an unattended run.--no-enableinstalls without enablinglist: List configured extensionsconfigure <NAME>: Configure an installed extension's credentialsremove <NAME>: Remove a configured extension
Usage:
biorouter extension install ./SPOKEAgent.brxt
biorouter ext list
biorouter ext configure SPOKEAgentInstall and manage skills.
Subcommands:
install <SOURCE>: Install a skill or skill package from a.zipor a repository URL.--forcereplaces one already installed;--as <bundle|individual>answers the question a source that could be either one package or several separate skills raiseslist: List installed skills and their enabled stateenable <NAME>/disable <NAME>: Turn a skill on or off without removing it.<NAME>is a skill name, a bundle name, or a slug asskill listshows itremove <NAME>: Remove an installed skill
Usage:
biorouter skill install https://github.com/BaranziniLab/biorouter-skills
biorouter skill list
biorouter skill disable code-reviewManage personal knowledge bases. Alias: kb.
Subcommands:
list: List knowledge basesactive: Show or set the primary base, which is the write target when no--kbis given.--set <ID>makes a base primary (it must not be hidden),--clearclears it,--inheritdrops this scope's preference, and--session <ID>acts on one chat's primary instead of the machine-wide onecreate <ID>: Create a base with a kebab-case id.--name <NAME>and--color <HEX>are optionalingest: Ingest a source into a base. One of--url,--fileor--text, with an optional--kb <ID>,--focus <HINTS>,--providerand--modelingest-conversation: Digest chat history into a baselint: Lint a basequery <QUESTION>: Ask a base a question.--savepersists the answer as a knowledge pagehide/unhide: Hide a base from the agent, or bring it back
Usage:
biorouter kb create ms-genetics --name "MS genetics"
biorouter kb active --set ms-genetics
biorouter kb ingest --url https://example.org/paper
biorouter kb query "what is known about HLA-DRB1*15:01?"List, open, and serve the Biorouter apps built by Agent Drafter. Apps open in a real browser; there is no in-terminal rendering.
Subcommands:
list: List installed Biorouter apps.--jsonprints them as JSONopen <ID>: Open an app in your default browser. A daemon this command starts keeps running after it returns, so the app stays openserve <ID>: Serve an app in the foreground untilCtrl-C, then stop the daemon it started. When a daemon it can use is already running, it prints a link and returns instead
Which daemon they use. When this profile's shared daemon is running (the one the desktop app and biorouter crew use), both commands open the app there and start nothing. They reach it the way every crew command does, and send the browser to that daemon's own address. serve then prints the link and returns, because that daemon keeps running on its own. A shared daemon they cannot use, for example one an older Biorouter started, is passed over with a line saying why. A port you name in BIOROUTER_PORT keeps its meaning: with BIOROUTER_PORT=8080 set, the shared daemon is used only if it listens on port 8080, and otherwise the commands work on port 8080 as described below. (BIOROUTER_PORT=0 names no port.)
Otherwise they look for a biorouterd from the configured port (BIOROUTER_PORT, default 3000) up, and start one on the first free port. Only a server whose /status answers ok counts as a Biorouter daemon; a port held by anything else, such as a web development server, is passed over for the next one, up to six ports. A daemon only opens an app for a caller holding its secret key, so they use a running daemon only when they know its key: a daemon apps open or apps serve started (they record its key in a file only your account can read), or one whose key BIOROUTER_SERVER__SECRET_KEY names. Any other daemon on the configured port, such as one you started by hand, is refused with a message saying so; set BIOROUTER_SERVER__SECRET_KEY to its key, or set BIOROUTER_PORT to a free port so the command starts its own. On Windows, the daemon open leaves running has no console of its own, so pressing Ctrl-C at that prompt later, or closing the window, does not stop it.
The link opens the app once. The address is http://127.0.0.1:<port>/apps/<id>/?t=<token>. It works once and for five minutes: opening it gives that browser the app, and the plain http://127.0.0.1:<port>/apps/<id>/ address keeps working in that browser until the daemon stops or the browser is closed. open never hands the link to a command line, where other accounts on the machine could read it and use it first. It writes the link into a page only your account can read and opens that page. It also prints the link, for a browser that cannot open the page (a snap packaged browser cannot read hidden folders).
For another link, run apps open again. While apps serve is running, run apps open <ID> (or a second apps serve <ID>) in another terminal; it mints a new link on the same daemon. A browser that follows a spent or expired link sees a page that says how to open the app again.
Usage:
biorouter apps list
biorouter apps open cohort-explorer
# Serve in the foreground; open the printed link in a browser
biorouter apps serve cohort-explorer
# Another link while that serve runs, from a second terminal
biorouter apps open cohort-explorerStart working on your last project or create a new one.
Alias: p
Usage:
biorouter projectChoose one of your projects to start working on.
Alias: ps
Usage:
biorouter projectsBoth project and projects are interactive and need a terminal. Without one
they exit with status 2 and point at the scriptable equivalent,
biorouter run --resume --session-id <id> --text "<prompt>".
Run Biorouter and reach it from a browser. serve starts the biorouterd daemon, points it at the built interface, and prints the URL to open — the same interface the desktop app shows, served on one origin with no proxy in the path.
Alias: headless — the name the retired standalone binary was known by, kept so older instructions still work.
Options:
--host <HOST>: Address to bind. Anything reachable from another machine requires a token. Default is127.0.0.1-p, --port <PORT>: Port to listen on. Default is8765— deliberately not3000, which isbiorouterd's own default--token <TOKEN>: Use this access token instead of generating a fresh one. The flag stays on the command line whileserveruns, where other accounts on the machine can read it, soserveprints a note saying so. To keep a token private, setBIOROUTER_BROWSER_TOKENin the environment instead--no-token: Serve without an access token. Refused for a non-loopback bind, and cannot be combined with--token--web-dir <DIR>: Directory holding the built interface. Takes precedence overBIOROUTER_SERVE_UI; whichever of the two is used must contain anindex.html, orserverefuses to start. Located automatically when neither is set--computer-use-approval: Interactively set a Biorouter Copilot approval key for the browser session. Requires a terminal--open: Open a browser once the server is ready. The browser is handed a page only your account can read, which carries the address, and never the address itself: the address holds the access token, and a browser's command line can be read by other accounts on the machine. The page is removed two minutes later, or whenservestops
Usage:
# Serve on this machine only, and open a browser
biorouter serve --open
# Serve on a different port
biorouter serve --port 9000
# Reach it from another machine on the network — a token is mandatory here
biorouter serve --host 0.0.0.0
# Reuse one address across restarts, for a bookmark or a service unit
BIOROUTER_BROWSER_TOKEN="$(openssl rand -hex 32)" biorouter serve --host 0.0.0.0The printed URL carries an access token as ?t=<token>, minted per launch and shown once. Opening it exchanges the token for a session cookie and answers a short page that moves on to the interface, so the token leaves the address bar. It is not used up, and opens the interface again for anyone who has it until the daemon stops. Use Ctrl+C to stop the server, or send serve SIGTERM (kill <pid>); either way it stops the daemon it started and frees the port.
Note. A browser session cannot change its model or provider, deliberately — run
biorouter configureto choose them before startingserve. Reaching Biorouter from a browser explains why, and covers the access token, remote access and troubleshooting.
Deprecated. Use
serveinstead.webserves a minimal standalone chat page rather than the Biorouter interface, and its default port collides withbiorouterd's. It is kept for now; new deployments should not use it.Since 2026-09-11 it lists no chats and returns no transcripts, and it opens a private chat only if it started that chat itself while running a private model. Continue any other private chat in the desktop app. SD-13 records why.
Start a new session in biorouter Web, a lightweight web-based interface launched via the CLI that mirrors the desktop app's chat experience.
biorouter Web is particularly useful when:
- You want to access biorouter with a graphical interface without installing the desktop app
- You need to use biorouter from different devices, including mobile
- You're working in an environment where installing desktop apps isn't practical
Warning. Don't expose the web interface to the internet without proper security measures.
Options:
-p, --port <PORT>: Port number to run the web server on. Default is3000--host <HOST>: Host to bind the web server to. Default is127.0.0.1--open: Automatically open the browser when the server starts--auth-token <TOKEN>: Require a password to access the web interface
Usage:
# Start web interface at `http://127.0.0.1:3000` and open the browser
biorouter web --open
# Start web interface at `http://127.0.0.1:8080`
biorouter web --port 8080
# Start web interface accessible from local network at `http://192.168.1.7:8080`
biorouter web --host 192.168.1.7 --port 8080
# Start web interface with authentication required
biorouter web --auth-token <TOKEN>Note. Use
Ctrl+Cto stop the server.
Limitations:
While the web interface provides most core features, be aware of these limitations:
- Some file system operations may require additional confirmation
- Extension management must be done through the CLI
- Certain tool interactions might need extra setup
- Configuration changes require a server restart
Run a Biorouter session tied to your terminal window. Each terminal keeps its own persistent session and resumes it automatically. This is the command that installs the @biorouter and @g aliases described below.
Subcommands:
init <SHELL>: Print the shell initialization script to evaluate from your shell profile.--defaultsends anything you type that is not a command to biorouterrun <PROMPT>...: Run a prompt in the terminal session. Multiple words need no quotesinfo: Print compact session info (token usage, model) for prompt integration, for example●○○○○ sonnet
Usage:
# Set up, once
echo 'eval "$(biorouter term init zsh)"' >> ~/.zshrc
source ~/.zshrc
biorouter term run list files in this directoryAsk biorouter questions directly from your shell prompt, with command history included in the context. These aliases are created when you set up terminal integration.
Examples:
# Ask questions with command history context
@biorouter create a python script to process these files
@biorouter create a PR description summarizing these changes
@g how do I fix these permission denied errors?The desktop composer, terminal UI (TUI), and classic CLI share these agent commands:
| Command | Action |
|---|---|
/compact (alias /summarize) |
Summarize the current chat to reduce context length. |
/clear |
Clear the current chat history. |
/effort [quick|normal|deep] |
Show or change this chat's reasoning effort. |
/goal <condition> or /goal clear |
Work toward a condition, or clear the goal. |
/loop <interval> <prompt> |
Run a prompt repeatedly; use /loop for status and stop syntax. |
/schedule <schedule> <prompt> |
Schedule a recurring prompt; use /schedule list to inspect jobs. |
/bug [description] |
Report a Biorouter bug. The agent investigates the chat, the documentation and the source, then drafts an issue you approve before anything is filed. See Diagnostics and bug reports. |
/<workflow-command> [arguments] |
Run a configured workflow shortcut. A missing or invalid workflow reports an error. |
Type / to browse commands in the desktop or TUI; the classic CLI uses Tab completion.
Workflow shortcuts cannot reuse built-in command names or resource-reference syntax.
Resource references select a capability, active extension, skill, or knowledge base for the agent's next response. They are not navigation commands and do not grant permissions:
| Reference | Example |
|---|---|
/ext:<id> |
/ext:developer Explain the current project |
/ext(<name with spaces>) |
/ext(Biorouter Copilot) Inspect the approved test app |
/skill:<name> or /skill(<name with spaces>) |
/skill(literature review) Summarize these papers |
/kb:<id> or /kb(<id with spaces>) |
/kb:project-notes Find the latest decision |
The desktop picker inserts references as chips, keeping their exact identifiers and
current display names. If a custom extension uses a bundled capability name, rename
it in Settings → Extensions before referring to it explicitly; those ambiguous custom
entries are omitted from the picker. Biorouter Copilot replaces the former Computer Use
and legacy Computer Controller display names; the computercontroller identifier remains
compatible. /extend is not an extension command: it gives guidance to use /ext:
instead. Biorouter Copilot still requires its normal runtime, OS permissions, and per-task
approval.
The desktop also offers /knowledge as a Knowledge reference shortcut and /diverge
to continue an existing conversation in a new chat. The message action is also labelled
Diverge. Start a chat before using it.
In the desktop composer, /bug followed by a space becomes a Report a bug chip at the
start of the message. The chip is only how the composer draws it: the message is still
sent as /bug <text>.
The TUI and classic CLI additionally support /help (alias /?), /exit (alias /quit),
/rename <name>, and /diverge [name]. The TUI also has /decisions, an overlay listing the
chat's decision-model calls by source and by recent turn; ↑↓ selects a source, Enter shows its
last calls and Esc goes back. In the TUI and the classic CLI a turn's decision calls show as one
line per source (◇ Monitor Form reached: 18 checks this turn · 18 failed · last P 0.31), never
one line per call, and the reply of a chat whose main model is a decision model shows as a small
card with a bar per option. Such a chat has no running context, so neither prints a context
meter for it, and /compact answers that there is no context to compact, without announcing a
compaction or asking you to confirm one first. The TUI's decision card names a Baseten model by
its alias (joey-1). The following commands
are classic CLI only; start it with BIOROUTER_CLI_CLASSIC=1 biorouter session:
/builtin <names>— add built-in extensions by name, comma-separated./extension <command>— add a stdio extension, with optionalENV=valueprefixes./mode <name>— selectauto,approve,chat, orsmart_approvemode./plan [message]and/endplan— enter or leave plan mode./workflow [file.yaml]— export the current conversation as a workflow./t [light|dark|ansi]— cycle or set the terminal theme./r— toggle full, untruncated tool output.
The /t command controls the syntax highlighting theme for markdown content in biorouter CLI responses. This affects the styles used for headers, code blocks, bold/italic text, and other markdown elements in the response output.
Commands:
/t- Cycles through themes:light→dark→ansi→light/t light- Setslighttheme (subtle light colors)/t dark- Setsdarktheme (subtle darker colors)/t ansi- Setsansitheme (most visually distinct option with brighter colors)
Configuration:
- The default theme is
dark - The theme setting is saved to the configuration file as
BIOROUTER_CLI_THEMEand persists between sessions - The saved configuration can be overridden for the session using the
BIOROUTER_CLI_THEMEenvironment variable
Note. Syntax highlighting styles only affect the font, not the overall terminal interface. The
lightanddarkthemes have subtle differences in font color and weight.
The biorouter CLI theme is independent from the biorouter Desktop theme.
Examples:
# Start a named session
biorouter session --name use-custom-theme
# Toggle theme during a session
/t
# Set the light theme during a session
/t lightNote. The first example starts a session only; set
BIOROUTER_CLI_THEMEin the environment, as described under Configuration above, to choose the theme for that session.
Session Control:
Ctrl+C- Interrupt the current requestCtrl+J- Add a newline
Navigation:
Cmd+Up/Down arrows- Navigate through command historyCtrl+R- Interactive command history search (reverse search). More info.
The Ctrl+R shortcut provides interactive search through your stored CLI command history. This feature makes it easy to find and reuse recent commands without retyping them. When you type a search term, biorouter searches backwards through your history for matches.
How it works:
- Press
Ctrl+Rin your biorouter CLI session - Type a search term
- Navigate through the results using:
Ctrl+Rto cycle backwards through earlier matchesCtrl+Sto cycle forward through newer matches
- Press
Return(orEnter) to run the found command, orEscto cancel
For example, instead of retyping this long command:
analyze the GWAS summary statistics file and suggest follow-up enrichment analyses
Use the "GWAS summary" or "enrichment" search term to find and rerun it.
Search tips:
- Distinctive terms work best: Choose unique words or phrases to help filter the results
- Partial matches and multiple words are supported: You can search for phrases like
"gith"and"run the unit test"
- CLI QA checklist — the manual and headless verification script covering every command on this page.
- Configuration file reference — the
config.yamlkeys behindbiorouter configureand the theme setting. - Environment variables — per-invocation overrides for the same settings, including
BIOROUTER_CLI_THEME. - Managing sessions — how sessions are stored, resumed, and pruned behind the
sessionsubcommands. - Workspace control — the daemon routes behind
session watch,send,attach, andcancel, and the agent-side tools that do the same things. - Decision models: what a decision model is, monitors, the
decidetool and phase gates, behinddecide,models decision list,session decisionsandsession monitors. - Reaching Biorouter from a browser — the full guide to
serve: the access token, reaching it from another machine, and what a browser session cannot do. - Creating and sharing workflows — how to author the workflow files that
run --workflow,workflow, andscheduleconsume.