Agent Manifest Reference
Every agent Tempest can launch is described by a JSON entry inconfig/agents.json. It is the single source of truth for the agent list — there is no hardcoded roster. The same entries drive the New Session menu, the Automations headless runner, the Chat pane bridges, and the agent-aware clear in the Message Queue.
This page documents the entry schema, how placeholders are substituted, and how the manifest reaches your install. For per-agent launch commands and troubleshooting, see Supported Agents; for how sessions consume the flags at spawn time, see Agent System Overview.
The bundled agents
config/agents.json ships inside the app build and defines 16 agents:
Agents you add yourself under Settings → Agents → Add merge on top of this list (see custom agents). They use the same schema with two extras: their
icon may also be a lucide:name reference from the bundled icon set, and the entry is stamped custom: true so it stays freely editable and removable in the UI.
Entry schema
The file has three top-level keys:schema (integer, currently 1), updatedAt (an ISO date), and agents (the entry array). Each entry follows this shape:
Flags
Capture
Some agents mint their own session id and print it to output instead of accepting one. For those,capture supplies:
The spawn path requires both a compiled pattern and resume args, so a capture spec without
resume does nothing.
Placeholder substitution
Substitution happens on every flag group. The known placeholders:
An unknown placeholder is the one failure mode worth knowing about: there is no substitution lookup for it, so it passes through verbatim and reaches the CLI as a literal string. A typo like
{UID} in a commit is therefore silent at runtime — which is why src/lib/agentsManifestFile.check.ts rejects unknown placeholders at check time (validation below).
Flag semantics and traps
Two conventions trip up new manifest entries, and both are guarded in the checks:autoApproveapplies to the interactive spawn as well as the headless one. A flag that only exists on a headless subcommand — Droid’s--autois the shipped example, Crush’s--yolothe other — breaks every launch of that agent the moment the user turns Auto on. Droid’s entry therefore ships withoutautoApprove, and the guard inagentsManifestFile.check.tsrejects a well-meaning “add the yolo flag” edit that would reintroduce it.sessionneeds a CLI that accepts an id we supply. A--resumeflag that only takes an id the CLI minted itself belongs incapture.resumeinstead, or nowhere — declaring it underflags.sessionpasses Tempest’s id to a CLI that does not expect it.
The signed distribution channel
The manifest travels on two layers:-
The bundled floor.
config/agents.jsonis imported at build time, so the agent list works instantly and offline, even before any network call. -
The signed remote patch. At startup,
src/lib/remoteAgents.tsfetches bothagents.jsonand its detached signatureagents.json.minisigfromand verifies the signature in Rust via theverify_minisigncommand againstAGENTS_PUBKEY, the public key bundled insrc-tauri/src/lib.rs(theminisign-verifycrate; both legacy and prehashed signature formats are accepted). Only after verification is the raw file parsed, theminAppVersiongate applied, and the surviving entries merged byidonto the floor. A new agent added upstream via a PR shows up in existing installs this way — no app release needed.
Why is this the one signed file? Every entry carries a
command that gets spawned and reaches a shell unquoted, plus the autoApprove flag that disables an agent’s own sandbox. An unsigned channel — reachable by a repo write, a merged PR, or a CDN cache-poisoning attack — would turn any of those into remote command execution on every client. On any verification failure the app keeps only its bundled agents; the fetch is deliberately silent about failures, because the bundled list already works.Key ceremony and publishing
1
Generate a signing key pair with
minisign -G -p tempest-agents.pub -s tempest-agents.key. Keep the private key offline or as a CI secret.2
Embed the public key. Paste the
RW… line from tempest-agents.pub into AGENTS_PUBKEY in src-tauri/src/lib.rs and ship it in a release — the key is bundled in the app. While AGENTS_PUBKEY is empty, the channel stays disabled and apps use only bundled agents.3
Sign and publish updates with
minisign -S -s tempest-agents.key -m config/agents.json, which produces config/agents.json.minisig. Commit both files and push; clients verify and pick the update up on next startup, after the ~12h jsDelivr cache TTL or a cache purge.sign-agents.ps1 performs the ceremony in one shot: it signs the manifest, verifies the result before committing anything, commits and pushes, then purges the jsDelivr cache for both files so clients see the update immediately.
The other config files, for contrast
agents.json
Fetched at runtime and minisign-verified. Carries spawned commands and flag templates — the only file that does.
models.json
Fetched at runtime but unsigned. Pure display data (model ids, labels, context sizes); nothing here is executed. See Chat models.
providers.json
Never fetched. Provider presets rewrite an API base URL and name the variable a key is exported into, so they ship bundled with each release — see provider presets.
Validation
src/lib/agentsManifestFile.check.ts runs under npm test and asserts things about the shipped file that are invisible at runtime. A malformed entry is dropped silently — the correct behavior for an untrusted downloaded manifest — which otherwise looks exactly like “my agent never showed up”. The checks make it a build failure instead:
- every shipped entry survives sanitization (a dropped entry is an agent that silently never appears);
- agent ids are unique;
- every
commandis a bare-token regex match, and every name is non-empty; - each
iconresolves either to a bundled key or to a real file inconfig/agent-icons/(anything else degrades to the fallback glyph with no error); - capture patterns compile, and every capture spec carries resume args so it is usable;
- every placeholder is a known one, since a typo reaches the CLI literally;
modelArgsactually reference{MODEL}andprintArgsreference{PROMPT}, so slots that carry a value carry its placeholder;downloadUrlis https;- and Droid’s entry must not declare
autoApprove, because its--autoflag is exec-only and would break interactive launches.
Worked example
A complete entry for a new resumable agent:config/agent-icons/, verify each flag against the CLI’s own --help, run npm test, and — because the channel is signed — expect the entry to reach existing installs once a key holder regenerates config/agents.json.minisig (or ship it in the bundled floor with the next release). Status detection options, including lifecycle hooks, are covered in Lifecycle hooks.