Agent tools
Every agent run composes its toolset from this registry (plus whatever MCP servers and custom functions its definition binds). A tool is a Zod-schemed function with a description the model sees verbatim and a gating flag; this page is the exhaustive reference. For how gating, capabilities, and the approval queue actually work, see Tools and approvals.
The approval-gate rule
Section titled “The approval-gate rule”Reversible knowledge-base, task, and memory writes are ungated: they
land as ordinary audited, invertible commands, so there’s nothing to
approve. Anything outbound, irreversible, or reaching outside Subspace
(sending mail, messaging someone, executing on the host, spawning more
agents, installing a plugin) is gated: the run pauses, a Needs
Confirmation card opens in the task queue, and the run
resumes only after a human approves or denies it. A handful of tools
(os.exec, code.claude.run, containers.run, plugins.install) carry
alwaysGate: no approvalPolicy on a definition page can lift them, a
trust floor the model itself never controls.
Before a gated call even reaches the approval queue, the capability
broker checks whether the run’s owner has the credential reach the tool
declares (requires: string[]): a missing grant denies the call as a
plain tool result, never opening an approval row. Credentials are acquired
per call, never read ambiently from process.env by tool code. See
Tools and approvals for the full broker
model and the caps: grant syntax on definition pages.
Knowledge base (kb.*)
Section titled “Knowledge base (kb.*)”Ungated except where noted; kb.edit/kb.create/kb.node.edit still
re-check the caller’s live page ACLs before writing.
| Tool | Gated | Description |
|---|---|---|
kb.search {q, kinds?} |
No | Hybrid (lexical + vector) search, top 10. kinds filters to node, memory, file, or mail. |
kb.expand {directory?} |
No | List one directory’s immediate child directories and pages (workspace root when omitted) for PageIndex-style navigation. |
kb.corpus {maxChars?} |
No | A bounded, title-ordered dump of every readable page’s text, for when search retrieval isn’t enough. |
kb.read {page, depth?} |
No | Read a page as OKF markdown (node ids as <!--nid--> comments) plus labels and backlinks. depth (0-2) follows [[links]] breadth-first. |
kb.edit {page, okf} |
No | Replace a page with submitted full-page OKF. Creates the page if missing; a node-level diff lands as one command. |
kb.create {title, dir?, okf?} |
No | Create a new page, optionally under a directory (created by slug if missing) and seeded with OKF content. |
kb.grep {pattern, regex?, limit?} |
No | Exhaustive exact or regex scan over every bullet, with a total match count (complements kb.search’s ranked top 10). |
kb.node.read {node, depth?} |
No | Read one node’s subtree as an OKF fragment. |
kb.node.edit {node, okf} |
No | Replace one node’s subtree (exactly one root bullet): the scoped version of kb.edit. |
kb.append {page, text} |
No | Append a bullet to a page. |
kb.remove {page, text} |
Yes | Delete the first bullet matching text: destructive, so it’s gated. |
Tasks (tasks.*)
Section titled “Tasks (tasks.*)”All ungated.
| Tool | Description |
|---|---|
tasks.list {section?} |
List the task queue by section (needs-confirm, up-next, scheduled, done), or everything open. |
tasks.create {title, section?} |
Add a card to the queue (up-next by default). |
tasks.done {id} |
Complete a task: it lands permanently in the Done log. |
tasks.doneLog {since?} |
Read the permanent Done log, newest first. |
See Task queue.
Ingestion and memory
Section titled “Ingestion and memory”| Tool | Gated | Description |
|---|---|---|
ingest.capture {url, title, markdown, screenshotFileId?, source?} |
No | File extracted source content under ingestion/raw and emit capture-received for the wiki compiler. Markdown must be extracted text, never live HTML. |
memory.propose {kind, text} |
No | Propose a semantic, episodic, or procedural memory item. Propose-only: curation happens in the consolidation pass. |
memory.consolidate {} |
No | Run the deterministic consolidation pass: dedup near-duplicates, mark superseded items. |
ops.heartbeat {} |
No | Re-check condition reminders and file calendar-prep suggestions. |
ops.patternSweep {} |
No | Cluster run observations into named patterns and rewrite the agents/health page. |
Calendar and reminders
Section titled “Calendar and reminders”| Tool | Gated | Description |
|---|---|---|
cal.events.list {from?, to?} |
No | List synced calendar events in a time range (default: next 7 days). Read-only, owner-scoped. |
reminders.create {text, when?} |
No | Create a reminder from natural language. A date phrase schedules it; otherwise it becomes a condition reminder the heartbeat re-checks. |
See Calendar.
Mail (mail.*)
Section titled “Mail (mail.*)”| Tool | Gated | Description |
|---|---|---|
mail.threadState {threadId} |
No | Read-only reply state for a thread: last inbound/outbound message and whether a reply is awaited. |
mail.proposeReply {threadId, body, sources?} |
No | Propose a reply as a card in the thread view: nothing sends until the user clicks Approve & send. Proposing is safe; the send itself gates at approve time. |
mail.send {accountId?, to, subject, body, threadId?} |
Yes | Send mail from a connected account. Outbound and irreversible; prefer mail.proposeReply for replies so the draft is visible first. |
See Mail and Mail rules.
Instant messaging
Section titled “Instant messaging”| Tool | Gated | Description |
|---|---|---|
im.send {app, conversationId, body} |
Yes | Send a Slack/Telegram message directly, or queue a browser job for WhatsApp/Messenger/LinkedIn (delivered from the user’s own logged-in session). Outbound and irreversible. |
See Instant messaging.
Web and research
Section titled “Web and research”| Tool | Gated | Description |
|---|---|---|
web.fetch {url} |
No | GET an HTTPS URL, return crudely tag-stripped text. 5s timeout, 1 MB cap. Read-only: used for CRM enrichment and general research. There is no dedicated crm.* tool family, so enrichment agents compose web.fetch with kb.read/kb.append/kb.edit/memory.propose instead. |
See CRM for how the built-in crm-enrich agent uses these tools together.
Spaced repetition
Section titled “Spaced repetition”| Tool | Description |
|---|---|
srs.replace {page, cards} |
Idempotently replace the derived flashcard set for a page. |
See Spaced repetition.
Agent-authored apps and plugins
Section titled “Agent-authored apps and plugins”| Tool | Gated | Description |
|---|---|---|
apps.scaffold {name, ...fields} |
No | Scaffold a personal app as plugin source (page type, dashboard view, sample item) under <home>/app-src/<name>. Nothing changes until apps.install. |
apps.install {name} |
Yes | Install (or upgrade) the scaffolded app through the ordinary plugin pipeline. Changes the workspace. |
plugins.inspect {path} |
No | Parse a local plugin directory and return the install review (identity, capabilities, dependencies, trust lanes). |
plugins.install {path} |
Yes (alwaysGate) |
Install a validated local plugin. The approval card is derived from the parsed manifest, labeling sandboxed, client-code, and full-trust artifacts. |
See Plugins overview and Agent-authored apps.
Coding agents (code.*)
Section titled “Coding agents (code.*)”| Tool | Gated | Description |
|---|---|---|
code.task.create {page, title, description?} |
Yes | File a coding task in the bd issue ledger of a page’s linked directory and add a linked row on that page. |
code.claude.prefill {...} |
Yes | Open a terminal node with a Claude Code invocation prefilled, for a human to review and run. |
code.claude.run {...} |
Yes (alwaysGate) |
Run Claude Code directly on the host: os.exec with a model attached, so it carries the same trust floor. |
code.devin.run {...} |
Yes | Dispatch a task to Devin through the capability broker. |
code.run.status {...} |
No | Poll the status of a dispatched coding-agent run. |
code.result.file {...} |
No | Fetch a result file from a completed coding-agent run. |
See Coding agents.
Containers (containers.*)
Section titled “Containers (containers.*)”Admin run owners only. Requires SUBSPACE_CONTAINERS=1 (or a fixture
SUBSPACE_DOCKER_BIN in tests). See
Environment variables.
| Tool | Gated | Description |
|---|---|---|
containers.run {...} |
Yes (alwaysGate) |
Start a detached container (docker run -d) with isolation defaults: no network, read-only mounts, explicit env only, resource caps. The host docker daemon is host-root-equivalent, hence the trust floor. |
containers.status {id} |
No | Inspect a container: status, running, exit code. |
containers.logs {id, tail?} |
No | Tail container logs. |
containers.stop {id} |
Yes | Stop a running container. |
Host execution
Section titled “Host execution”| Tool | Gated | Description |
|---|---|---|
os.exec {command, cwd?} |
Yes (alwaysGate) |
Run a shell command on the host. Admin run owners only; disabled unless SUBSPACE_OS_EXEC=1 (or PROVIDERS=fake). Always approval-gated no matter the definition’s approvalPolicy. |
MLOps (mlops.* and platform families)
Section titled “MLOps (mlops.* and platform families)”A large family covering local runs and connected platforms (MLflow, Weights & Biases, Dagster) plus the research-goal orchestration tools. Reads are ungated; launches and cancellations gate. See MLOps overview for the full contract and Launchers for what each platform prefix maps to.
| Prefix | Covers |
|---|---|
mlops.ingest, mlops.metrics.history, mlops.registry.sync |
Run ingestion, metric history, and model-registry materialization (the core platform-ingest engine). |
mlops.local.{submit,status,cancel,logs,capacity} |
Local run launcher (submit gated, reads/cancel ungated per call). |
fn.mlflow.* |
MLflow runs, metrics, artifacts, models, plugin-shipped sandbox fns (read-only, ungated). |
fn.wandb.* |
Weights & Biases runs, metrics, artifacts, sweeps, models, launch, alerts, plugin fns. launch.submit/launch.cancel/submitSweep/alerts.subscribe gate. |
fn.dagster.* |
Dagster runs, metrics, launch, assets, plugin fns. Launch submit/cancel gate. |
research.* |
Research-goal heartbeat (gather/reflect/propose/report), scheduler (plan/record), and elo sweep tooling for Autoresearch. |
Orchestration (agents.*, workflows.*, waits)
Section titled “Orchestration (agents.*, workflows.*, waits)”| Tool | Gated | Description |
|---|---|---|
agents.spawn {agentPage?, definitionHash?, instruction, wait?, autoJudge?} |
Yes | Start a sub-agent run. Omit wait to continue in the background and gather up to six child ids later with runs.await. Capped at depth 3, 8 children per run. |
workflows.start {name, args?} |
Yes | Start a registered workflow by name with JSON args (requires the WDK engine). |
time.sleep {duration} |
No | Pause the run for a duration like 30s, 5m, 3 days. Durable and zero-cost on the WDK engine: a wait, not an effect. |
human.ask {question, options?, context?, timeout?, default?} |
No | Ask the user a question and wait for their answer (a task-queue card). The answer is data, never permission: it never substitutes for an approval gate. |
runs.await {runId} or {runIds} |
No | Wait for one run, or concurrently gather up to six, and return their terminal statuses. |
See Orchestration.
Skills
Section titled “Skills”| Tool | Gated | Description |
|---|---|---|
skill.load {name} |
No | Load a skill’s full instructions by name. Ungated by design: loading a skill never expands privileges, since whatever it instructs still routes through the same registry, allowlist, and approval gates. |
See Skills.
MCP servers and plugin functions
Section titled “MCP servers and plugin functions”Beyond the built-in registry, two more tool families compose into a run:
mcp.<server>.<tool>: an agent definition’smcp: string[]bindsmcp/pages; the bound servers’ toolsets are snapshotted at run start and registered under this namespace with the server’s real JSON Schema. Each server has a per-tool trust setting (defaultalways= gated); a dead server yields a tool-result error rather than failing the run. See MCP servers.fn.<name>: an agent definition’sfns: string[]bindscode/pages as tools, executed in the same QuickJS sandbox the custom-function render path uses. Gated by default (needsApproval: true); a bare function gets a curated read/append host API, a plugin-imported function gets its plugin’s declaredmanifest.capabilities. See Custom functions and Plugins: tools.