Skip to content

Agents

An agent is a page in the agents/ directory that runs as a specific user against a durable engine. It carries a prompt, an allowlist of tools, an args schema, a model, and an approval policy, and it edits the same knowledge base you do: its output appears as bullets under the bullet you invoked it from, its steps are journaled to Postgres, and its outbound actions pause for your approval. Agents are the layer that turns “an operating system for knowledge work” into a system that also does the work.

Because agents are just pages, you author them in the outliner, version them, and share them through GitOps exactly like any other content. There is no separate agent database and no external orchestrator: a run is rows in Postgres, and the same command model and outbox spine that power the rest of the product power agents too.

Runs execute on the Workflow DevKit (WDK) over the self-hosted Postgres World. The server boots a WDK world in the workflow schema and runs a graphile-worker in-process; each run executes as the registered agent-loop workflow over a shared step vocabulary.

The engine’s defining property is durability. Every step, an LLM call, a tool call, an approval, an output, is journaled as a row before it takes effect, and each iteration of the loop commits one Postgres transaction: the previous tool results, the new step rows, the messages checkpoint, any output nodes, and (on a gated call) the approval row, the needs-confirm task card, and a status flip to paused.

The messages array is the checkpoint (plain JSON, validated on load), so there is no hidden engine state to reconstruct. Tool execution happens between transactions, so a crash in that window re-executes the tool on resume: tools are therefore either idempotent or approval-gated, and irreversible tools additionally take an effect claim keyed on the call id so a replay refuses the silent re-run. See the agent engine chapter for the full journal model.

The invoking bullet gets a mono run ⌁ chip that toggles an inline run tree: an execution tree of virtual bullet rows rendered under the invoking node. These rows are telemetry, not knowledge base nodes, so they never pollute your outline.

  1. The anchor row

    Shows the agent name, a status pill, and a step count, with a ⏸ prefix while any approval anywhere in the subtree is pending. It also carries the run’s introspection chips and a Steer this run… input.

  2. Step rows

    ✻ LLM rows and ⚒ tool rows (materialized from the LLM step’s journaled tool calls before execution, so you see them go from awaiting-approval to running to done ✓), plus output rows and nested child runs.

  3. Detail panels

    Clicking a row expands redacted args and output JSON plus per-step logs (tools write to the log through ctx.log, best-effort). An LLM row’s detail also shows the exact model input: the system prompt, the run-scoped tool definitions as JSON Schema, and the full message history, captured just before the provider call.

The tree streams live over the outbox WebSocket topic run:<id> (with a 2s poll as a backstop while the run is non-terminal), so rows and status update as the run progresses. Output text becomes violet child bullets under the invoking node, one per line, written as ordinary knowledge base commands under a fixed agent device id and echoed live. Row titles, redaction, and viewer resolution run server-side through a shared manifest, so a mail.send row reads mail.send → to "subject" with the body elided.

Every run acts as a specific user. A ⌘↵ invocation runs as you; a scheduled or event-triggered run acts as the definition page’s owner. The run sees only what that user’s page ACLs allow, and its outbound actions (mail, calendar, messaging) go out under that identity. This is what makes per-user agents safe on a shared home.

Credentials are never ambient. A run acquires secrets through a capability broker that enforces per-run grants and audits every acquire into the step log. See security and trust for the plane split that isolates the worker that constructs the engine.

From a bullet

Press ⌘↵ (or ctrl+↵) on any plain bullet. Its text is sent as the instruction to the default agent (agents/default), and a toast confirms the run started. This is the fastest way to ask the system to do something.

Slash agents

Type /name to invoke a named agent from the slash menu (agents carry a violet agent badge). Argument placeholder chips guide you, and [[page]] args autocomplete. See definitions.

Triggers

A definition can list event types in on: and run automatically when a matching event lands. See triggers.

Schedules

A definition with a schedule: cron runs on a timer through the scheduler, appearing in the task queue’s Scheduled section.

Runs also start from task cards: the task queue surfaces suggestions and needs-confirm cards, and answering a card resumes the run that filed it. Whichever way a run starts, it lands in the run tree under an invoking node and streams live.