RPC mode

circle --mode rpc [folder] runs Circle as a child process for another program, such as an editor. It reads one JSON command per line on standard input and writes JSON lines to standard output until its input closes. The commands follow pi’s RPC mode where Circle has the same thing.

circle --mode rpc ~/code/app

Each command gets one response line, with the command’s id when it had one:

{"id": "1", "type": "prompt", "message": "Review src/app.ts"}
{"type": "response", "command": "prompt", "success": true, "id": "1", "data": {"disposition": "started"}}

A command that fails has "success": false and an error. A line that is not a JSON object gets a response with "command": "parse". Empty lines are skipped.

Commands

type Fields What it does
prompt message, streamingBehavior Start a turn. While one runs, streamingBehavior must be steer (the model reads it before its next step) or followUp (sent when the turn ends). data.disposition is started or queued.
steer message A steering message for the running turn, or a new turn when none runs.
follow_up message A message for after the running turn, or a new turn when none runs.
abort   Stop the running turn and drop the waiting messages; answers once the turn has stopped.
clear_queue   Take back the waiting messages: data.steering and data.followUp.
new_session   A new conversation: data.sessionId. Not while a turn runs.
get_state   model, thinkingLevel, isStreaming, sessionId, sessionName, messageCount, pendingMessageCount.
get_messages   Every message, as {"type": "human" \| "ai" \| "tool" \| "system", "data": {...}}, the shape LangChain message dicts have.
get_native_messages   Every message as Circle stores it, with attachments and the recorded price of each call.
get_last_assistant_text   data.text.
get_session_stats   sessionId, userMessages, assistantMessages, toolCalls, toolResults, totalMessages, tokens (input, output, total) and costs. Subagents and compactions are counted.
set_session_name name Give the conversation a title.
get_available_models   data.models: what the endpoint lists.
set_model modelId Use another model for the rest of the process: data.model. Not while a turn runs.
set_thinking_level level minimal … max, for the rest of the process, also after set_model: data.thinkingLevel. Not while a turn runs.
export_html outputPath Write the conversation as HTML: data.path. A relative path is inside the workspace; the default is <session id>.html there.
list_jobs   Every background job of the process: data.jobs, each as in the job event. get_jobs works too.
stop_job jobId Stop a running job with everything it started: data.job. job_id works too.
background   Move the command being waited on to the background, as ctrl+b does: data.moved, how many.

Events

The work a prompt starts is written as the JSON events of print mode, without session. On top of those:

type When
steer The model read a steering message (message).
turn_end with "aborted": true The turn was stopped by abort or ctrl+c.
agent_settled The work a prompt or a job notice started has ended, and nothing more will run on its own.
jobs_stopped The last line: count, the jobs that were still running at the end and were stopped.

When a background job the model started ends while nothing runs, Circle starts a turn for it, up to ten in a row. Its turn_start has no message, and it also ends with agent_settled. After abort or new_session such turns wait for the next prompt.

Options and approvals

--model, --thinking, --tools, --exclude-tools, --no-tools, --system-prompt, --append-system-prompt, --no-context-files, --name, --session, --session-id, --fork, -c, --no-session and --yolo apply as usual. Messages and @files on the command line are refused, and so are -r and --line: send prompts as commands.

Nobody can answer an approval card, so calls are decided as in print mode: without --yolo, a call that would ask is not run and the model is told why; with --yolo it runs, except the calls that always ask. A question from the model comes back to it as text to ask in its answer. MCP servers and extensions load only with --extensions (circle --mode rpc --extensions); custom commands are not loaded. The folder must be trusted and Circle set up already; see Print mode.

When the input closes

Closing standard input lets the current turn finish and save its answer, including when the input stream closes without an end-of-file event. Processes a command left running are stopped. Then Circle waits for the jobs the model started, as print mode does: when one ends the model gets its notice and a turn, up to CIRCLE_JOB_WAIT seconds (1800 by default) and ten turns, and not at all after an abort. It stops what still runs, writes jobs_stopped, and exits with code 0, or 130 after ctrl+c.