Files
Christian Klotz 25cc5c7bf4 docs(coding-agent): refresh documentation (#9898)
* docs(coding-agent): improve getting started documentation

* docs(coding-agent): correct getting started details

* docs(coding-agent): clarify SDK entry point

* docs(coding-agent): restructure guides and references

* docs(coding-agent): improve getting started guides

* docs(coding-agent): refresh integration guides

* docs(coding-agent): improve terminal and CLI guides

* docs(coding-agent): refresh customisation guides

* docs(coding-agent): clarify project trust terminology

* docs(coding-agent): simplify customisation guidance

* docs(coding-agent): improve runtime and reference guidance

* Fix settings reference

* feat(coding-agent): add Crowdin documentation sync

* docs(coding-agent): separate CLI and slash command references

* docs(coding-agent): correct compaction reference

* docs(coding-agent): streamline package documentation

* docs(coding-agent): split RPC reference documentation

* Update configuration docs

* Shorten config docs

* docs(coding-agent): refine configuration references

* docs(coding-agent): streamline settings reference

* docs(coding-agent): clarify configuration reference

* docs(coding-agent): clarify project trust exception

* docs(coding-agent): simplify keybindings reference

* docs(coding-agent): remove Crowdin integration

* docs(coding-agent): turn themes reference into guide

* docs(coding-agent): consolidate model and authentication docs

* docs(tui): require Component.invalidate() (fixes #9358)

* docs(coding-agent): document offline catalog behavior (fixes #8684)

* docs(coding-agent): preserve established documentation routes

* docs(coding-agent): reorganize documentation navigation

* docs(coding-agent): correct audited behavior

Clarify provider, session, local-model, Termux, TUI, SDK, debug, and extension behavior. Simplify the documentation audit to report only clear user-visible contradictions.

* docs(coding-agent): fix broken documentation links
2026-09-22 16:48:19 +02:00

8.5 KiB

RPC Mode

RPC mode runs Pi as a long-lived subprocess controlled through JSON records on stdin and stdout. Use it for language-independent integrations, process isolation, IDEs, and custom user interfaces.

For an in-process Node.js or Bun integration, prefer the SDK. For a subprocess-based TypeScript integration, prefer the exported RpcClient, which starts Pi, correlates responses, exposes typed command methods, and delivers events to listeners.

Interface Process boundary Control model Best fit
SDK In process Direct TypeScript methods and events Node.js or Bun hosts that want complete API access
RPC Child process JSONL commands, responses, and events Other languages, isolated processes, IDEs, or custom clients

Start RPC mode

pi --mode rpc --no-session

Normal CLI options still select the working folder, model, tools, resources, and session behavior. Common choices include --provider, --model, --name, --no-session, and --session-dir. See Command Line for the complete, version-specific interface; pi --help is authoritative for the installed version.

RPC mode rejects @file prompt arguments. Send prompts through the prompt command instead.

Protocol records

The protocol has four record families:

Direction Record Purpose
stdin Command Ask Pi to prompt, inspect state, change configuration, or manage the session
stdout response Report whether one command succeeded and return any command data
stdout Session event Stream run, message, tool, queue, compaction, and retry activity
Both Extension UI record Forward supported extension interactions between Pi and the client

See RPC Commands, JSON Event Stream, and RPC Extension UI for the canonical record definitions.

Correlate commands and responses

Every command accepts an optional string id. A matching response repeats it:

{"id":"req-1","type":"get_state"}
{"id":"req-1","type":"response","command":"get_state","success":true,"data":{"...":"..."}}

Use unique IDs whenever more than one command can be outstanding. Command handling is asynchronous, so clients should correlate by ID rather than response order.

Session events generally have no command ID because they describe session activity. bash_execution_update is the exception: when the originating bash command has an ID, its output events repeat that ID.

An extension_ui_response uses the ID supplied by its extension_ui_request. It does not produce a normal command response.

Framing

RPC uses strict JSONL framing. Write one complete JSON object per record and terminate it with LF (\n). Read stdout as a byte or UTF-8 stream and split records only on LF. Strip an optional preceding carriage return to accept CRLF input.

Do not use a generic line reader that treats Unicode line or paragraph separators as record boundaries. In particular, Node.js readline also splits on U+2028 and U+2029, which are valid inside JSON strings.

Read stdout continuously. Pi honors stdout backpressure, but a client that stops reading can stall the process. Honor stdin backpressure when writing commands. Stdout is reserved for protocol records; diagnostics and application logging go to stderr.

Run lifecycle

A successful prompt response means the prompt was accepted, queued, or handled. It does not mean model work completed:

{"id":"req-2","type":"prompt","message":"Review this repository"}
{"id":"req-2","type":"response","command":"prompt","success":true}

Continue consuming events after that response. agent_end marks the end of one low-level agent run, but retries, overflow recovery, compaction, steering, or follow-up work can still follow. Wait for agent_settled when the client needs to know Pi will not continue automatically.

Subscribe before sending a prompt to avoid missing a fast completion. RpcClient.promptAndWait() does this internally. If using separate RpcClient calls, install the event listener before prompt() and call waitForIdle() only while a run is active.

Errors

A failed command returns one response with success: false:

{"id":"req-3","type":"response","command":"set_model","success":false,"error":"Model not found: invalid/model"}

Malformed JSON produces a parse response without a request ID:

{"type":"response","command":"parse","success":false,"error":"Failed to parse command: Unexpected token..."}

A success response only covers command handling. Provider failures and aborts after a prompt is accepted appear in the message and event stream.

Clients must also handle child-process startup failures, unexpected exits, stderr diagnostics, cancellation, and their own deadlines. Do not parse stderr as protocol data.

Shutdown

Close the child's stdin to request an orderly shutdown. Pi disposes the active runtime before exiting. Clients should still handle process signals and unexpected exits.

An extension can also request shutdown through its extension context. Pi completes shutdown after the current command or after the active run emits agent_settled.

Minimal client

This Python example uses a binary pipe reader, which splits on LF without treating Unicode separators as protocol boundaries:

import json
import subprocess

process = subprocess.Popen(
    ["pi", "--mode", "rpc", "--no-session"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
)

assert process.stdin is not None
assert process.stdout is not None

command = {"id": "prompt-1", "type": "prompt", "message": "Hello"}
process.stdin.write(json.dumps(command).encode("utf-8") + b"\n")
process.stdin.flush()

while line := process.stdout.readline():
    record = json.loads(line)
    if record.get("type") == "message_update":
        update = record["assistantMessageEvent"]
        if update["type"] == "text_delta":
            print(update["delta"], end="", flush=True)
    elif record.get("type") == "agent_settled":
        print()
        break

process.stdin.close()
process.wait()

For maintained TypeScript clients, use the checked RPC client example. It requires a built Pi CLI because the repository example points to dist/cli.js.

Reference

Moved reference anchors

The detailed references formerly on this page now have dedicated pages. These anchors preserve existing links.

Command details moved to RPC Commands.

Event details moved to JSON Event Stream.

Extension interaction details moved to RPC Extension UI.