RPC¶
Cognition × Application
tau --mode rpc speaks JSON-RPC 2.0 over stdio, newline-delimited. τ as a
process a host drives, rather than a library it imports.
Twenty-one verbs are implemented. Seven more are formally declined, each with a stated reason, rather than silently absent.
A submission answers twice¶
submit and prompt return immediately with an acceptance, and the result of the turn arrives later as a notification — red, because a host that treats the first response as the answer will sit waiting for a turn that already finished. A rejected submission inverts it: the error arrives on the response and nothing follows, so a host waiting for agent_end after a rejection waits forever.Verbs¶
| Tier | Verbs |
|---|---|
| A — turn and state | submit* · prompt · abort · get_state · get_messages · get_commands · get_tools · get_capabilities · new_session · fork · switch_session |
| B — session management | compact · set_model · get_models · set_session_name · get_session_name · get_session_stats · list_sessions · set_auto_compaction · get_last_assistant_text |
* submit is Tier C on the generated table; it is grouped with the
turn verbs here because that is what a host uses it for.
Call get_capabilities first on a new connection, and check
protocol_version before sending anything mutating. It publishes the verb
list, the event schema, declined[], and limits.max_request_line_bytes.
Give your own subprocess reader an 8 MiB line limit before that call —
get_capabilities's own response is tens of kilobytes, well over the stdlib
StreamReader default of 64 KiB.
The seven declined verbs¶
Each is reachable through get_capabilities().declined[]. Calling one returns
-32601 METHOD_NOT_FOUND; the published reason, not the bare error, is how a
host learns why.
| Verb | Why declined |
|---|---|
send_tool_result |
τ's loop executes tool calls itself. Accepting a result over RPC opens a second, unauthenticated path into the same executor that a host never drove the call for. |
bash |
The same reasoning. τ's bash is a tool the loop runs under a submission's provenance and admission rules; an out-of-band shell verb bypasses both. |
cycle_model |
A keybinding affordance, not a protocol verb. A host names a model with set_model rather than stepping through a list it cannot see. |
cycle_thinking_level |
The same judgment, and τ has no thinkingLevel on AgentSession for a set_* verb to act on. |
set_steering_mode |
multitask_strategy is already a per-submission parameter on submit and prompt, not a session-wide mode to toggle. |
set_follow_up_mode |
The same — multitask_strategy="enqueue" covers it per call. |
export_html |
Rendering is the host's job. τ hands back get_messages and events. |
Two of these share one principle worth stating plainly: a second privileged path into the same executor is a second thing to secure.
Where an RPC session lives¶
--mode rpc defaults its session base to a private <tmp>/.tau-<uid>/sessions
rather than ~/.tau/sessions. A subprocess a host spawns and tears down should
not litter the shared directory.
The consequence is that a host and the human at the terminal are normally
looking at different session lists. --session-dir DIR is how a host joins
the user's. list_sessions returns scope: {store, cwd} precisely so a host
can tell which universe it is in, and its listing and switch_session's
resolution are the same set by construction — an id this verb returns is an id
that verb accepts.
Why a host might prefer RPC to embedding¶
A subprocess gives a host one property an in-process import cannot: a real
hard kill. terminate() and kill() against a τ child work the way they
work against any subprocess, so a runaway tool-call loop is stoppable from
outside rather than only cooperatively.
The reader loop is strictly serial, so abort stays answerable while a turn
is in flight — measured at get_state answering in 0.44s and abort in 0.46s
against a 20-second provider call.
Embedding trades that away. abort becomes cooperative only, and a wedged
tool or a CPU-bound stretch shares the host's event loop.
The generated reference¶
docs/RPC-PROTOCOL.md in the source repo is the exact wire reference. It is
generated from the real command table and event schema, and a drift test
asserts the checked-in file equals its own render() output — so it cannot
disagree with the code that serves it.
Treat the tables above as an index into that file, not a replacement for it.