Five verbs replaced a registry of twenty-five bespoke tools. They are the entire operation set of the Harness: new capability arrives as a new address or a new document in the Space, never as a new tool schema in the provider payload. The user holds the same five through the wrench palette — "the user did it" and "the agent did it" are one code path with different actor stamps.
They live in agents/protocol/src/tool-registry.ts as seedVerbRegistry, beside callableToolRegistry (search, web_search, navigate, execute) which is reached only through call. The verbs are always on and are never grants.
read
read {address, format?, options?} — one dispatcher keyed by the shape of the address:
Address | Returns |
|---|---|
| A memory file; a directory lists entries with sizes |
| A tool contract; |
| A hypermedia document or comment, as markdown |
| Content by CID, fetched into memory |
| A public web page as markdown |
| The activity feed; filter with |
| A session file; images ride to vision models |
| Another conversation transcript |
| A run journal |
View terms refine an hm:// read: /:directory lists child documents, /:attributes returns metadata without the body, /:profile reads an account's profile.
write
write {address, content?, options?, dryRun?} — the mirror, same address polymorphism:
~/memory/<path> — write content; parent folders are created, and a write replaces the whole file (there is no append). {delete: true} removes; {fromUrl} downloads; {fromAttachment} saves a conversation file.
~/tools/<name> — author or delete a lambda tool document.
ipfs:// — publish bytes and get a CID URL back.
hm://<account>/<path> — publish a document. Default creates; {action: "update"} revises in place; comment, move, redirect, delete, fork cover the rest, and any dotted action (profile.update, draft.create, …) passes through as a raw command.
The write path refuses rather than guesses, and each refusal came from a real failure: an unknown option key is a 400 naming the key and the supported set (extra command fields belong in options.input, never as loose keys); a document with no name refuses to publish; and every hm:// link in the content is validated before anything is signed. dryRun: true validates without publishing. Publishing requires the publish grant; memory writes never do. Details in Hypermedia Integration.
call
call {tool, input} — invoke a callable by name: search, web_search, execute, or an authored lambda.
Its defining behavior is touch-expand: calling a tool the thread has not expanded, or calling one with invalid input, returns the tool's contract as the result instead of an error — so the model reads the spec and retries correctly in one step. That is a deliberate inversion of the usual "invalid arguments" failure, and it is also how a tool becomes promoted for the rest of the thread (Tools as Documents).
delegate
delegate {title?, brief?, script?, input?, agentId?, tools?, output?, await?} — spawn a child run. A script makes it a script child; otherwise it is a model child with its own session, whose brief becomes its first message verbatim. await: false detaches. An output schema makes the child owe a typed result. Full story in Delegation.
It returns {status: 'succeeded' | 'failed' | 'canceled' | 'detached', sessionId?, runId?, output?, error?}.
plan
plan {title?, steps} — maintain the thread's visible checklist, each step {id, label, status} where status is pending | running | done | failed | skipped. See Plans for attachment and settlement, and Obligations for what an unfinished checklist costs.
A sixth, hidden one
return_result is registered beside the five but is not a verb you choose: it is how a child with an output schema delivers its typed result, with its real parameters swapped in at session start.
Related
Tools as Documents — what call reaches
Do you like what you are reading? Subscribe to receive updates.
Unsubscribe anytime