An agent definition is an ordinary outline page in the agents/ directory. Its
metadata.agent block declares how the agent behaves, and its page body holds the prompt.
Because the definition is a page, you edit it in the outliner,
diff and restore its versions, dump it to disk through the OKF bridge,
and deploy it with GitOps.
The metadata.agent object is validated with Zod at the boundary. Invalid definitions are
skipped by the runtime and listed with their error in the commands settings.
Field
Type
Meaning
prompt
string
The instruction template. {{arg}} placeholders interpolate args at invoke time. Often the page body rather than a metadata string.
tools
string[]?
Registry allowlist. Absent means all built-in tools. See tools & approvals.
caps
string[]?
Capability grant set. Absent means the union of the allowlisted tools’ requires; naming caps only narrows reach.
Every home has an agents/default page, ensured on boot along with the agents/
directory. When you press ⌘↵ (or ctrl+↵) on a plain bullet, its text is sent as the
instruction to the default agent (a built-in DEFAULT_AGENT constant is the fallback if
the page is missing). Edit agents/default to change what the default keystroke does: its
prompt, its tool allowlist, its model, and its approval policy.
Named agents join the slash menu with a violet agent badge (custom functions keep a fn
badge). Picking one inserts /name followed by argument-placeholder chips drawn from
argsSchema.
Placeholder chips
Required args come first, then the rest alphabetically (jsonb metadata does not preserve
authored key order). A page-type arg gets [[ autocomplete, so /prep-meeting [[Marcus Feld]]
binds the person arg to that page and the depth chip stays as a hint until you fill it.
Binding args
key: value tokens bind named args explicitly; any other token fills the next
positional slot. So /prep-meeting [[Marcus Feld]] depth: full sets both args.
Invoke
Enter or ⌘↵ on a completed /name … call invokes agents.invoke {agentPageId, args, pageId, nodeId}. The prompt renders with {{arg}} interpolation, and the run’s violet
output bullets grow under the invoking node.
agents.list is the registry endpoint behind the slash menu and the settings page.
/settings/commands (sidebar entry commands) is the command directory: every custom
function (code/) and agent (agents/) with its name, a type badge, description, and args
summary. Agent rows also show model, approval policy, schedule, and tools. A row navigates
to the underlying definition page (a directory of pages, not a bespoke editor). Invalid
agent definitions show their Zod validation error inline, so a broken metadata.agent
block is visible without reading logs.
A definition can change between runs, so every run records exactly which version configured
it. At run start, startRun snapshots the definition page (and every bound
skills//mcp//code/ page) into kb.page_versions and stamps
ops.agent_runs.definition_hash. The hash is computed by the page-versioning canonicalizer,
so an unchanged definition used a hundred times deduplicates to one stored version, and
every version that ever configured a run is retained.
Definition as of this run
The run tree’s anchor row shows a definition vN chip when the run’s definition_hash
resolves. Clicking it renders the pinned canonical OKF from the store: the exact
definition that configured the run, whatever the live page says now. Bound skill, MCP, and
fn chips on the same anchor resolve the same way through ops.run_page_versions.
Config pages (anything under agents/, skills/, mcp/, judges/, evals/, code/,
settings/, plus any page with metadata.versioned: true) show a header chip like
v7 · 12 versions (or edited since v7 when the live content is unsaved). The versions
panel lists versions newest-first with a source chip (manual, ⟳ run …, or ⚙ plugin@ver)
and a “used by N runs” count, defaults to a structural outline diff with an exact-text
toggle, and offers Restore: it confirms with the diff, then applies client-side as one
ordinary edit, so a restore is optimistic, synced, and on the ⌘Z undo stack. Pre-versioning
runs are backfilled: a boot sweep reconstructs their definition contract from the journaled
first message and inserts it as a source: backfill version, so old runs group and open
like any other.