RPCモード
RPC モードでは、stdin/stdout 上の JSON プロトコルを介したコーディング エージェントのヘッドレス操作が有効になります。これは、他のアプリケーション、IDE、またはカスタム UI にエージェントを埋め込む場合に便利です。
Node.js/TypeScript ユーザーへの注意: Node.js アプリケーションを構築している場合は、サブプロセスを生成する代わりに、@earendil-works/pi-coding-agent から直接 AgentSession を使用することを検討してください。 APIについては、src/core/agent-session.tsを参照してください。サブプロセスベースの TypeScript クライアントについては、src/modes/rpc/rpc-client.ts を参照してください。
RPCモードの開始
pi --mode rpc [options]一般的なオプション:
--provider <name>: LLM プロバイダー (anthropic、openai、google など) を設定します。--model <pattern>: モデルパターンまたは ID (provider/idおよびオプションの:<thinking>をサポート)--name <name>/-n <name>: 起動時のセッション表示名を設定します--no-session: セッションの永続性を無効にする--session-dir <path>: カスタムセッション保存ディレクトリ
プロトコルの概要
- コマンド: JSON オブジェクトが stdin に送信されます (1 行に 1 つ)
- 応答: コマンドの成功/失敗を示す
type: "response"を持つ JSON オブジェクト - イベント: エージェント イベントは JSON 行として stdout にストリーミングされます
すべてのコマンドは、リクエスト/レスポンス相関のためのオプションの id フィールドをサポートしています。指定した場合、対応する応答には同じ id が含まれます。 bash_execution_update イベントには、元の bash コマンドの id も含まれます。
フレーミング
RPC モードは、LF (\n) を唯一のレコード区切り文字とする厳密な JSONL セマンティクスを使用します。
これはクライアントにとって重要です。
\nのみでレコードを分割します- 末尾の
\rを削除して、オプションの\r\n入力を受け入れます - Unicode 区切り文字を改行として扱う汎用の行リーダーを使用しないでください。
特に、ノード readline は、JSON 文字列内で有効な U+2028 と U+2029 でも分割されるため、RPC モードのプロトコルに準拠していません。
コマンド
プロンプト
プロンプト
ユーザー プロンプトをエージェントに送信します。コマンド応答は、プロンプトが受け入れられるか、キューに入れられるか、処理された後に出力されます。イベントは受け入れられた後も非同期でストリーミングを続けます。
{"id": "req-1", "type": "prompt", "message": "Hello, world!"}画像付き:
{"type": "prompt", "message": "What's in this image?", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}ストリーミング中: エージェントがすでにストリーミングしている場合は、メッセージをキューに入れるには streamingBehavior を指定する必要があります。
{"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}"steer": エージェントの実行中にメッセージをキューに入れます。これは、現在のアシスタント ターンがツール呼び出しの実行を終了した後、次の LLM 呼び出しの前に配信されます。"followUp": エージェントが終了するまで待ちます。メッセージはエージェントが停止した場合にのみ配信されます。
エージェントがストリーミング中であり、streamingBehavior が指定されていない場合、コマンドはエラーを返します。
拡張コマンド: メッセージが拡張コマンド (例: /mycommand) の場合、ストリーミング中でもすぐに実行されます。拡張コマンドは、pi.sendMessage() を介して独自の LLM インタラクションを管理します。
入力拡張: スキルコマンド (/skill:name) と prompt templates (/template) は送信/キューイング前に拡張されます。
応答:
{"id": "req-1", "type": "response", "command": "prompt", "success": true}success: true は、プロンプトが受け入れられた、キューに入れられた、またはすぐに処理されたことを意味します。 success: false は、プロンプトが受け入れられる前に拒否されたことを意味します。受け入れ後の失敗は、同じリクエスト ID に対する 2 番目の response としてではなく、通常のイベントおよびメッセージ ストリームを通じて報告されます。
images フィールドはオプションです。各画像は ImageContent 形式: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"} を使用します。
操縦する
エージェントの実行中にステアリング メッセージをキューに入れます。これは、現在のアシスタント ターンがツール呼び出しの実行を終了した後、次の LLM 呼び出しの前に配信されます。スキルコマンドとprompt templatesが拡張されます。拡張コマンドは使用できません (代わりに prompt を使用してください)。
{"type": "steer", "message": "Stop and do this instead"}画像付き:
{"type": "steer", "message": "Look at this instead", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}images フィールドはオプションです。各画像は ImageContent 形式 (prompt と同じ) を使用します。
応答:
{"type": "response", "command": "steer", "success": true}ステアリング メッセージの処理方法の制御については、set_steering_mode を参照してください。
フォローアップ
エージェントの終了後に処理されるフォローアップ メッセージをキューに入れます。エージェントがツール呼び出しやステアリング メッセージをもう受けなくなった場合にのみ配信されます。スキルコマンドとprompt templatesが拡張されます。拡張コマンドは使用できません (代わりに prompt を使用してください)。
{"type": "follow_up", "message": "After you're done, also do this"}画像付き:
{"type": "follow_up", "message": "Also check this image", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}images フィールドはオプションです。各画像は ImageContent 形式 (prompt と同じ) を使用します。
応答:
{"type": "response", "command": "follow_up", "success": true}フォローアップメッセージの処理方法の制御については、set_follow_up_mode を参照してください。
アボート
現在のエージェントの操作を中止します。
{"type": "abort"}応答:
{"type": "response", "command": "abort", "success": true}新しいセッション
新しいセッションを開始します。 session_before_switch 拡張イベント ハンドラーによってキャンセルできます。
{"type": "new_session"}オプションの親セッション追跡を使用すると、次のようになります。
{"type": "new_session", "parentSession": "/path/to/parent-session.jsonl"}応答:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": false}}延長がキャンセルされた場合:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": true}}州
状態の取得
現在のセッション状態を取得します。
{"type": "get_state"}応答:
{
"type": "response",
"command": "get_state",
"success": true,
"data": {
"model": {...},
"thinkingLevel": "medium",
"isStreaming": false,
"isCompacting": false,
"steeringMode": "all",
"followUpMode": "one-at-a-time",
"sessionFile": "/path/to/session.jsonl",
"sessionId": "abc123",
"sessionName": "my-feature-work",
"autoCompactionEnabled": true,
"messageCount": 5,
"pendingMessageCount": 0
}
}model フィールドは完全な Model オブジェクトまたは null です。 sessionName フィールドは、set_session_name で設定された表示名です。設定されていない場合は省略されます。
get_messages
会話内のすべてのメッセージを取得します。
{"type": "get_messages"}応答:
{
"type": "response",
"command": "get_messages",
"success": true,
"data": {"messages": [...]}
}メッセージは AgentMessage オブジェクトです (Message Types を参照)。
モデル
セットモデル
特定のモデルに切り替えます。
{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}応答には完全な Model オブジェクトが含まれます。
{
"type": "response",
"command": "set_model",
"success": true,
"data": {...}
}サイクルモデル
次に利用可能なモデルに切り替えます。利用可能なモデルが 1 つだけの場合は、null データを返します。
{"type": "cycle_model"}応答:
{
"type": "response",
"command": "cycle_model",
"success": true,
"data": {
"model": {...},
"thinkingLevel": "medium",
"isScoped": false
}
}model フィールドは完全な Model オブジェクトです。
get_available_models
構成されているすべてのモデルをリストします。
{"type": "get_available_models"}応答には完全な Model オブジェクトの配列が含まれます。
{
"type": "response",
"command": "get_available_models",
"success": true,
"data": {
"models": [...]
}
}考え
set_ Thinking_level
それをサポートするモデルの推論/思考レベルを設定します。
{"type": "set_thinking_level", "level": "high"}レベル: "off"、"minimal"、"low"、"medium"、"high"、"xhigh"、"max"
"xhigh" と "max" は、選択したモデルでサポートされている場合にのみ公開されます。 GPT-5.6 などの一部のモデルは両方を公開します。
応答:
{"type": "response", "command": "set_thinking_level", "success": true}サイクル思考レベル
利用可能な思考レベルを循環します。モデルが思考をサポートしていない場合は、null データを返します。
{"type": "cycle_thinking_level"}応答:
{
"type": "response",
"command": "cycle_thinking_level",
"success": true,
"data": {"level": "high"}
}get_available_ Thinking_levels
現在のモデルでサポートされている思考レベルをリストします。推論サポートのないモデルの場合は ["off"] を返します。
{"type": "get_available_thinking_levels"}応答:
{
"type": "response",
"command": "get_available_thinking_levels",
"success": true,
"data": {
"levels": ["off", "minimal", "low", "medium", "high"]
}
}キューモード
セットステアリングモード
ステアリング メッセージ (steer から) の配信方法を制御します。
{"type": "set_steering_mode", "mode": "one-at-a-time"}モード:
"all": 現在のアシスタント ターンがツール呼び出しの実行を終了した後に、すべてのステアリング メッセージを配信します。"one-at-a-time": 完了したアシスタントターンごとに 1 つのステアリングメッセージを配信します (デフォルト)
応答:
{"type": "response", "command": "set_steering_mode", "success": true}set_follow_up_mode
フォローアップ メッセージ (follow_up から) の配信方法を制御します。
{"type": "set_follow_up_mode", "mode": "one-at-a-time"}モード:
"all": エージェントの終了時にすべてのフォローアップ メッセージを配信します"one-at-a-time": エージェントの完了ごとに 1 つのフォローアップ メッセージを配信します (デフォルト)
応答:
{"type": "response", "command": "set_follow_up_mode", "success": true}圧縮
コンパクト
会話コンテキストを手動で圧縮して、トークンの使用量を削減します。
{"type": "compact"}カスタム命令を使用すると:
{"type": "compact", "customInstructions": "Focus on code changes"}応答:
{
"type": "response",
"command": "compact",
"success": true,
"data": {
"summary": "Summary of conversation...",
"firstKeptEntryId": "abc123",
"tokensBefore": 150000,
"estimatedTokensAfter": 32000,
"usage": {
"input": 32000,
"output": 1200,
"cacheRead": 0,
"cacheWrite": 0,
"totalTokens": 33200,
"cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
},
"details": {}
}
}estimatedTokensAfter は、プロバイダーが正確に定義するトークン数ではなく、圧縮直後に再構築されたメッセージ コンテキストに対するヒューリスティックな推定値です。 usage は、サマリーを生成した LLM 呼び出しをレポートします。カスタム圧縮ハンドラーによって省略される場合があります。
set_auto_compaction
コンテキストがほぼいっぱいになった場合の自動圧縮を有効または無効にします。
{"type": "set_auto_compaction", "enabled": true}応答:
{"type": "response", "command": "set_auto_compaction", "success": true}リトライ
set_auto_retry
一時的なエラー (過負荷、レート制限、5xx) での自動再試行を有効または無効にします。
{"type": "set_auto_retry", "enabled": true}応答:
{"type": "response", "command": "set_auto_retry", "success": true}中止_再試行
進行中の再試行を中止します (遅延をキャンセルし、再試行を停止します)。
{"type": "abort_retry"}応答:
{"type": "response", "command": "abort_retry", "success": true}バッシュ
bash
シェルコマンドを実行し、出力を会話コンテキストに追加します。コマンドの実行中にストリームを bash_execution_update イベントとして出力します。応答には最終結果が含まれます。
{"id": "req-1", "type": "bash", "command": "ls -la"}ストリーミングされた bash_execution_update イベントをこのコマンドに関連付けるには、id を含めます。
応答:
{
"id": "req-1",
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false
}
}出力が切り捨てられた場合、fullOutputPath が含まれます:
{
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "truncated output...",
"exitCode": 0,
"cancelled": false,
"truncated": true,
"fullOutputPath": "/tmp/pi-bash-abc123.log"
}
}bash の結果が LLM に届く仕組み:
bash コマンドはすぐに実行され、BashResult を返します。内部的には、BashExecutionMessage が作成され、エージェントのメッセージ状態に保存されます。
次の prompt コマンドが送信されると、すべてのメッセージ (BashExecutionMessage を含む) が変換されてから LLM に送信されます。 BashExecutionMessage は、次の形式で UserMessage に変換されます。
Ran `ls -la`
```
合計48
drwxr-xr-x...
```これはつまり:
- Bash 出力は、すぐではなく 次のプロンプト の LLM コンテキストに含まれます
- プロンプトの前に複数の bash コマンドを実行できます。すべての出力が含まれます
中止_bash
実行中の bash コマンドを中止します。
{"type": "abort_bash"}応答:
{"type": "response", "command": "abort_bash", "success": true}セッション
get_session_stats
トークンの使用状況、コスト統計、および現在のコンテキスト ウィンドウの使用状況を取得します。
{"type": "get_session_stats"}応答:
{
"type": "response",
"command": "get_session_stats",
"success": true,
"data": {
"sessionFile": "/path/to/session.jsonl",
"sessionId": "abc123",
"userMessages": 5,
"assistantMessages": 5,
"toolCalls": 12,
"toolResults": 12,
"totalMessages": 22,
"tokens": {
"input": 50000,
"output": 10000,
"cacheRead": 40000,
"cacheWrite": 5000,
"total": 105000
},
"cost": 0.45,
"contextUsage": {
"tokens": 60000,
"contextWindow": 200000,
"percent": 30
}
}
}tokens と cost には、アシスタント メッセージ、ツールによって報告された使用状況、セッション全体にわたる圧縮/ブランチ サマリーの生成が含まれます。 contextUsage には、圧縮とフッター表示に使用される実際の現在のコンテキスト ウィンドウの推定値が含まれます。
使用可能なモデルまたはコンテキスト ウィンドウがない場合、contextUsage は省略されます。 contextUsage.tokens と contextUsage.percent は、圧縮後の新しいアシスタント応答が有効な使用状況データを提供するまで、圧縮直後は null になります。
エクスポート_html
セッションを HTML ファイルにエクスポートします。
{"type": "export_html"}カスタムパスの場合:
{"type": "export_html", "outputPath": "/tmp/session.html"}応答:
{
"type": "response",
"command": "export_html",
"success": true,
"data": {"path": "/tmp/session.html"}
}スイッチセッション
別のセッション ファイルをロードします。 session_before_switch 拡張イベント ハンドラーによってキャンセルできます。
{"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}応答:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}内線番号が切り替えをキャンセルした場合:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": true}}フォーク
アクティブなブランチ上の以前のユーザー メッセージから新しいフォークを作成します。 session_before_fork 拡張イベント ハンドラーによってキャンセルできます。フォーク元のメッセージのテキストを返します。
{"type": "fork", "entryId": "abc123"}応答:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": false}
}拡張機能がフォークをキャンセルした場合:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": true}
}クローン
現在のアクティブなブランチを現在の位置で新しいセッションに複製します。 session_before_fork 拡張イベント ハンドラーによってキャンセルできます。
{"type": "clone"}応答:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": false}
}拡張機能がクローンをキャンセルした場合:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": true}
}get_fork_messages
フォークに使用できるユーザー メッセージを取得します。
{"type": "get_fork_messages"}応答:
{
"type": "response",
"command": "get_fork_messages",
"success": true,
"data": {
"messages": [
{"entryId": "abc123", "text": "First prompt..."},
{"entryId": "def456", "text": "Second prompt..."}
]
}
}get_entries
すべてのセッション エントリを追加順に取得します (セッション ヘッダーを除く)。セッションは、安定した ID を持つエントリの追加専用ツリーであるため、エントリ ID は永続カーソルとして機能します。クライアントの再起動後であっても、それ以降のエントリのみを厳密に取得するには、確認した最後のエントリ ID を since として渡します。 get_messages とは異なり、これには圧縮前の履歴と放棄されたブランチが含まれます。
{"type": "get_entries"}カーソルを使用する場合:
{"type": "get_entries", "since": "abc123"}応答:
{
"type": "response",
"command": "get_entries",
"success": true,
"data": {
"entries": [
{"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
],
"leafId": "def456"
}
}leafId は現在のリーフ エントリの ID (空のセッションの場合は null) であるため、クライアントはアクティブなブランチが移動したかどうかを 1 往復で知ることができます。 since がどのエントリ ID とも一致しない場合、応答は success: false になります。
get_tree
セッションをエントリのツリーとして取得します。各ノードは {entry, children, label?, labelTimestamp?} です。適切な形式のセッションにはルートが 1 つあります。孤立したエントリ (壊れた親チェーン) もルートとして表示されます。
{"type": "get_tree"}応答:
{
"type": "response",
"command": "get_tree",
"success": true,
"data": {
"tree": [
{
"entry": {"type": "message", "id": "abc123", "parentId": null, "...": "..."},
"children": [
{"entry": {"type": "message", "id": "def456", "parentId": "abc123", "...": "..."}, "children": []}
]
}
],
"leafId": "def456"
}
}get_last_assistant_text
最後のアシスタント メッセージのテキスト コンテンツを取得します。
{"type": "get_last_assistant_text"}応答:
{
"type": "response",
"command": "get_last_assistant_text",
"success": true,
"data": {"text": "The assistant's response..."}
}アシスタント メッセージが存在しない場合は、{"text": null} を返します。
セットセッション名
現在のセッションの表示名を設定します。この名前はセッション リストに表示され、セッションを識別するのに役立ちます。
{"type": "set_session_name", "name": "my-feature-work"}応答:
{
"type": "response",
"command": "set_session_name",
"success": true
}現在のセッション名は、sessionName フィールドの get_state で確認できます。 RPC モード開始時の初期名を設定するには、--name <name> または -n <name> を pi --mode rpc プロセスに渡します。
コマンド
get_commands
利用可能なコマンド(拡張コマンド、prompt templates、スキル)を取得します。これらは、/ をプレフィックスとして付けて、prompt コマンド経由で呼び出すことができます。
{"type": "get_commands"}応答:
{
"type": "response",
"command": "get_commands",
"success": true,
"data": {
"commands": [
{"name": "session-name", "description": "Set or clear session name", "source": "extension", "path": "/home/user/.pi/agent/extensions/session.ts"},
{"name": "fix-tests", "description": "Fix failing tests", "source": "prompt", "location": "project", "path": "/home/user/myproject/.pi/agent/prompts/fix-tests.md"},
{"name": "skill:brave-search", "description": "Web search via Brave API", "source": "skill", "location": "user", "path": "/home/user/.pi/agent/skills/brave-search/SKILL.md"}
]
}
}各コマンドには次のものがあります。
name: コマンド名(/nameで起動)description: 人間が読める説明 (拡張コマンドの場合はオプション)source: どのようなコマンド:"extension": 拡張機能のpi.registerCommand()経由で登録されました"prompt": プロンプト テンプレート.mdファイルからロード"skill": スキルディレクトリからロード (名前の前にskill:が付きます)
location: ロード元 (オプション、拡張機能には存在しません):"user": ユーザーレベル (~/.pi/agent/)"project": プロジェクトレベル (./.pi/agent/)"path": CLI または設定による明示的なパス
path: コマンドソースへの絶対ファイルパス (オプション)
注意: 組み込みの TUI コマンド (/settings、/hotkeys など) は含まれていません。これらは対話モードでのみ処理され、prompt 経由で送信された場合は実行されません。
イベント
エージェントの動作中、イベントは JSON 行として stdout にストリーミングされます。通常、イベントには id フィールドは含まれません。 bash_execution_update には、元の bash コマンドが提供された場合の id が含まれます。
イベントの種類
| イベント | 説明 |
|---|---|
agent_start |
エージェントが処理を開始します |
agent_end |
1 つの低レベル エージェントの実行が完了します (その後、再試行、圧縮、またはキューに入れられた継続が続く場合があります) |
agent_settled |
エージェントの実行は完全に完了しました。自動再試行、圧縮再試行、またはキューに入れられた継続は残りません。 |
turn_start |
新しいターンが始まります |
turn_end |
ターン完了 (アシスタント メッセージとツールの結果を含む) |
message_start |
メッセージが始まります |
message_update |
ストリーミング更新 (テキスト/思考/ツールコール デルタ) |
message_end |
メッセージが完了しました |
bash_execution_update |
直接 RPC bash コマンド出力チャンク |
tool_execution_start |
ツールの実行が開始されます |
tool_execution_update |
ツール実行の進行状況 (ストリーミング出力) |
tool_execution_end |
ツールが完了しました |
queue_update |
保留中のステアリング/フォローアップキューが変更されました |
compaction_start |
圧縮が始まります |
compaction_end |
圧縮が完了しました |
auto_retry_start |
自動再試行が開始されます (一時的なエラーの後) |
auto_retry_end |
自動再試行が完了しました (成功または最終失敗) |
summarization_retry_scheduled |
一時的な圧縮またはブランチサマリー要約エラーに対してスケジュールされた再試行 |
summarization_retry_attempt_start |
再試行された要約リクエストが開始されます |
summarization_retry_finished |
要約再試行ループが完了する |
extension_error |
拡張機能がエラーをスローしました |
エージェントの開始
エージェントがプロンプトの処理を開始すると発行されます。
{"type": "agent_start"}エージェントエンド
1 つの低レベル エージェントの実行が完了すると発行されます。この実行中に生成されたすべてのメッセージが含まれます。 willRetry が true の場合、自動再試行が続きます。
{
"type": "agent_end",
"messages": [...],
"willRetry": false
}エージェント_解決済み
完全なセッションレベルの実行が安定した後に発行されます。この時点では、Pi は、再試行、圧縮の再試行、またはキューに入れられたフォローアップ メッセージを通じて自動的に続行されません。
{"type": "agent_settled"}ターンスタート / ターンエンド
ターンは、1 つのアシスタントの応答と、その結果として生じるツールの呼び出しと結果で構成されます。
{"type": "turn_start"}{
"type": "turn_end",
"message": {...},
"toolResults": [...]
}メッセージ開始 / メッセージ終了
メッセージの開始時と完了時に発行されます。 message フィールドには AgentMessage が含まれています。
{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}message_update (ストリーミング)
アシスタント メッセージのストリーミング中に発行されます。累積メッセージ スナップショットのないデルタ イベントが含まれます。
{
"type": "message_update",
"assistantMessageEvent": {
"type": "text_delta",
"contentIndex": 0,
"delta": "Hello "
}
}assistantMessageEvent フィールドには、次のデルタ タイプのいずれかが含まれます。
| タイプ | 説明 |
|---|---|
text_start |
テキストコンテンツブロックが開始されました |
text_delta |
テキストコンテンツチャンク |
text_end |
テキストコンテンツブロックが終了しました |
thinking_start |
思考ブロックが始まりました |
thinking_delta |
思考コンテンツチャンク |
thinking_end |
思考ブロックが終わった |
toolcall_start |
ツール呼び出しが開始されました |
toolcall_delta |
ツール呼び出し引数チャンク |
toolcall_end |
ツール呼び出しが終了しました (完全な toolCall オブジェクトを含む) |
テキスト応答をストリーミングする例:
{"type":"message_update","assistantMessageEvent":{"type":"text_start","contentIndex":0}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":" world"}}
{"type":"message_update","assistantMessageEvent":{"type":"text_end","contentIndex":0,"content":"Hello world"}}message_update は、前の累積 message フィールドを意図的に省略し、
assistantMessageEvent.partial。ライブ部分メッセージが必要なクライアントはそれを組み立てる必要があります
message_start 以降のイベントは contentIndex を使用します。ご褒美message_end.message
権威あるものとして。ツール呼び出しの場合は、バッファ toolcall_delta.delta; toolcall_end.toolCall
完了した通話が含まれます。
bash_実行_更新
直接 bash コマンドから出力チャンクごとに 1 回発行されます。 id はコマンドの id と一致するため、クライアントは出力を正しいコマンドに関連付けることができます。
イベントは、最後の bash 応答の output が切り詰められている場合でも、コマンドの実行中にすべての出力をストリーミングします。
{
"type": "bash_execution_update",
"id": "req-1",
"delta": "total 48\n"
}ツール実行開始 / ツール実行更新 / ツール実行終了
ツールが開始され、ストリームが進行し、実行が完了したときに発行されます。
{
"type": "tool_execution_start",
"toolCallId": "call_abc123",
"toolName": "bash",
"args": {"command": "ls -la"}
}実行中、tool_execution_update イベントは部分的な結果をストリーミングします (例: bash は到着時に出力されます)。
{
"type": "tool_execution_update",
"toolCallId": "call_abc123",
"toolName": "bash",
"args": {"command": "ls -la"},
"partialResult": {
"content": [{"type": "text", "text": "partial output so far..."}],
"details": {"truncation": null, "fullOutputPath": null}
}
}完了したら:
{
"type": "tool_execution_end",
"toolCallId": "call_abc123",
"toolName": "bash",
"result": {
"content": [{"type": "text", "text": "total 48\n..."}],
"details": {...}
},
"isError": false
}イベントを関連付けるには、toolCallId を使用します。 tool_execution_update の partialResult には、これまでに蓄積された出力 (デルタだけではなく) が含まれているため、クライアントは更新のたびにディスプレイを簡単に置き換えることができます。
キュー_アップデート
保留中のステアリングまたはフォローアップ キューが変更されるたびに発行されます。
{
"type": "queue_update",
"steering": ["Focus on error handling"],
"followUp": ["After that, summarize the result"]
}圧縮開始 / 圧縮終了
手動か自動かに関係なく、圧縮が実行されるときに発行されます。
{"type": "compaction_start", "reason": "threshold"}reason フィールドは、"manual"、"threshold"、または "overflow" です。
{
"type": "compaction_end",
"reason": "threshold",
"result": {
"summary": "Summary of conversation...",
"firstKeptEntryId": "abc123",
"tokensBefore": 150000,
"estimatedTokensAfter": 32000,
"usage": {
"input": 32000,
"output": 1200,
"cacheRead": 0,
"cacheWrite": 0,
"totalTokens": 33200,
"cost": {"input": 0.01, "output": 0.02, "cacheRead": 0, "cacheWrite": 0, "total": 0.03}
},
"details": {}
},
"aborted": false,
"willRetry": false
}reason が "overflow" で圧縮が成功した場合、willRetry は true となり、エージェントは自動的にプロンプトを再試行します。
圧縮が中止された場合、result は null、aborted は true になります。
圧縮が失敗した場合 (例: API クォータの超過)、result は null、aborted は false、errorMessage にはエラーの説明が含まれます。
auto_retry_start / auto_retry_end
一時的なエラー (過負荷、レート制限、5xx) の後に自動再試行がトリガーされたときに発行されます。
{
"type": "auto_retry_start",
"attempt": 1,
"maxAttempts": 3,
"delayMs": 2000,
"errorMessage": "529 {\"type\":\"error\",\"error\":{\"type\":\"overloaded_error\",\"message\":\"Overloaded\"}}"
}{
"type": "auto_retry_end",
"success": true,
"attempt": 2
}最終的な失敗時 (最大再試行回数を超えた場合):
{
"type": "auto_retry_end",
"success": false,
"attempt": 3,
"finalError": "529 overloaded_error: Overloaded"
}summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished
一時的なプロバイダー エラーの後に圧縮またはブランチ サマリーの要約が再試行されるときに生成されます。これらのイベントでは、アシスタント ターンの自動再試行と同じ再試行設定が使用されます。
{
"type": "summarization_retry_scheduled",
"attempt": 1,
"maxAttempts": 3,
"delayMs": 2000,
"errorMessage": "terminated"
}{
"type": "summarization_retry_attempt_start",
"source": "compaction",
"reason": "threshold"
}ブランチサマリーの場合、source は "branchSummary" であり、reason は存在しません。
{
"type": "summarization_retry_finished"
}拡張エラー
拡張機能がエラーをスローしたときに発行されます。
{
"type": "extension_error",
"extensionPath": "/path/to/extension.ts",
"event": "tool_call",
"error": "Error message..."
}拡張UIプロトコル
Extensions は、ctx.ui.select()、ctx.ui.confirm() などを介してユーザー対話を要求できます。RPC モードでは、これらは、基本コマンド/イベント フローの上にある要求/応答サブプロトコルに変換されます。
拡張 UI メソッドには 2 つのカテゴリがあります。
- ダイアログ メソッド (
select、confirm、input、editor): stdout でextension_ui_requestを発行し、クライアントが stdin で一致するidとともにextension_ui_responseを返信するまでブロックします。 - ファイアアンドフォーゲットメソッド (
notify、setStatus、setWidget、setTitle、set_editor_text): stdout にextension_ui_requestを発行しますが、応答は期待しません。クライアントは情報を表示することも無視することもできます。
ダイアログ メソッドに timeout フィールドが含まれている場合、タイムアウトが経過すると、エージェント側はデフォルト値で自動解決します。クライアントはタイムアウトを追跡する必要はありません。
一部の ExtensionUIContext メソッドは、直接 TUI アクセスが必要なため、RPC モードではサポートされないか、機能が低下します。
custom()はundefinedを返しますsetWorkingMessage()、setWorkingIndicator()、setFooter()、setHeader()、setEditorComponent()、setToolsExpanded()は操作なしですgetEditorText()は""を返しますgetToolsExpanded()はfalseを返しますpasteToEditor()はsetEditorText()に委譲します (貼り付け/折りたたみ処理なし)getAllThemes()は[]を返しますgetTheme()はundefinedを返しますsetTheme()は{ success: false, error: "..." }を返します
注: ダイアログとファイア アンド フォーゲット メソッドは拡張 UI サブプロトコルを介して機能するため、RPC モードでは ctx.mode は "rpc" 、ctx.hasUI は true です。実際の端末を必要とするcustom() などの TUI 固有の機能を保護するには、ctx.mode === "tui" を使用します。
拡張 UI リクエスト (stdout)
すべてのリクエストには、type: "extension_ui_request"、一意の id、および method フィールドがあります。
選択する
ユーザーにリストから選択するよう求めます。 timeout フィールドを持つダイアログ メソッドにはミリ秒単位のタイムアウトが含まれます。クライアントが時間内に応答しない場合、エージェントは undefined で自動解決します。
{
"type": "extension_ui_request",
"id": "uuid-1",
"method": "select",
"title": "Allow dangerous command?",
"options": ["Allow", "Block"],
"timeout": 10000
}予期される応答: extension_ui_response と value (選択されたオプション文字列) または cancelled: true。
確認する
ユーザーに「はい/いいえ」の確認を求めます。
{
"type": "extension_ui_request",
"id": "uuid-2",
"method": "confirm",
"title": "Clear session?",
"message": "All messages will be lost.",
"timeout": 5000
}予想される応答: extension_ui_response と confirmed: true/false または cancelled: true。
入力
ユーザーに自由形式のテキストの入力を求めます。
{
"type": "extension_ui_request",
"id": "uuid-3",
"method": "input",
"title": "Enter a value",
"placeholder": "type something..."
}予期される応答: extension_ui_response と value (入力されたテキスト) または cancelled: true。
エディタ
オプションの事前入力コンテンツを含む複数行のテキスト エディターを開きます。
{
"type": "extension_ui_request",
"id": "uuid-4",
"method": "editor",
"title": "Edit some text",
"prefill": "Line 1\nLine 2\nLine 3"
}予想される応答: extension_ui_response と value (編集されたテキスト) または cancelled: true。
通知する
通知を表示します。ファイアアンドフォーゲット、応答は期待できません。
{
"type": "extension_ui_request",
"id": "uuid-5",
"method": "notify",
"message": "Command blocked by user",
"notifyType": "warning"
}notifyType フィールドは、"info"、"warning"、または "error" です。省略した場合のデフォルトは "info" です。
setStatus
フッター/ステータス バーのステータス エントリを設定またはクリアします。火をつけて忘れる。
{
"type": "extension_ui_request",
"id": "uuid-6",
"method": "setStatus",
"statusKey": "my-ext",
"statusText": "Turn 3 running..."
}statusText: undefined を送信 (または省略) して、そのキーのステータス エントリをクリアします。
ウィジェットを設定する
エディターの上または下に表示されるウィジェット (テキスト行のブロック) を設定またはクリアします。火をつけて忘れる。
{
"type": "extension_ui_request",
"id": "uuid-7",
"method": "setWidget",
"widgetKey": "my-ext",
"widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
"widgetPlacement": "aboveEditor"
}ウィジェットをクリアするには、widgetLines: undefined を送信 (または省略) します。 widgetPlacement フィールドは、"aboveEditor" (デフォルト) または "belowEditor" です。 RPC モードでは文字列配列のみがサポートされます。コンポーネント ファクトリは無視されます。
タイトルを設定
ターミナルウィンドウ/タブのタイトルを設定します。火をつけて忘れる。
{
"type": "extension_ui_request",
"id": "uuid-8",
"method": "setTitle",
"title": "pi - my project"
}set_editor_text
入力エディターでテキストを設定します。火をつけて忘れる。
{
"type": "extension_ui_request",
"id": "uuid-9",
"method": "set_editor_text",
"text": "prefilled text for the user"
}拡張機能 UI 応答 (stdin)
応答はダイアログ メソッド (select、confirm、input、editor) に対してのみ送信されます。 id はリクエストと一致する必要があります。
値の応答 (選択、入力、エディター)
{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}確認応答(確認)
{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}キャンセル応答(任意のダイアログ)
ダイアログ メソッドをすべて閉じます。拡張機能は、undefined (選択/入力/エディター用) または false (確認用) を受け取ります。
{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}エラー処理
失敗したコマンドは、success: false の応答を返します。
{
"type": "response",
"command": "set_model",
"success": false,
"error": "Model not found: invalid/model"
}解析エラー:
{
"type": "response",
"command": "parse",
"success": false,
"error": "Failed to parse command: Unexpected token..."
}種類
ソースファイル:
packages/ai/src/types.ts-Model,UserMessage,AssistantMessage,ToolResultMessagepackages/agent/src/types.ts-AgentMessage,AgentEventsrc/core/messages.ts-BashExecutionMessagesrc/modes/json-event.ts-JsonAgentSessionEventsrc/modes/rpc/rpc-types.ts- RPC コマンド/応答タイプ、拡張 UI リクエスト/応答タイプ
モデル
{
"id": "claude-sonnet-4-20250514",
"name": "Claude Sonnet 4",
"api": "anthropic-messages",
"provider": "anthropic",
"baseUrl": "https://api.anthropic.com",
"reasoning": true,
"input": ["text", "image"],
"contextWindow": 200000,
"maxTokens": 16384,
"cost": {
"input": 3.0,
"output": 15.0,
"cacheRead": 0.3,
"cacheWrite": 3.75
}
}ユーザーメッセージ
{
"role": "user",
"content": "Hello!",
"timestamp": 1733234567890,
"attachments": []
}content フィールドには、文字列または TextContent/ImageContent ブロックの配列を指定できます。
アシスタントメッセージ
{
"role": "assistant",
"content": [
{"type": "text", "text": "Hello! How can I help?"},
{"type": "thinking", "thinking": "User is greeting me..."},
{"type": "toolCall", "id": "call_123", "name": "bash", "arguments": {"command": "ls"}}
],
"api": "anthropic-messages",
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"usage": {
"input": 100,
"output": 50,
"cacheRead": 0,
"cacheWrite": 0,
"cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
},
"stopReason": "stop",
"timestamp": 1733234567890
}停止理由: "stop"、"length"、"toolUse"、"error"、"aborted"
ツール結果メッセージ
{
"role": "toolResult",
"toolCallId": "call_123",
"toolName": "bash",
"content": [{"type": "text", "text": "total 48\ndrwxr-xr-x ..."}],
"usage": {
"input": 100,
"output": 50,
"cacheRead": 0,
"cacheWrite": 0,
"totalTokens": 150,
"cost": {"input": 0.0003, "output": 0.00075, "cacheRead": 0, "cacheWrite": 0, "total": 0.00105}
},
"isError": false,
"timestamp": 1733234567890
}usage はオプションで、ツールによって実行されたネストされた LLM 作業を報告します。存在すると、セッション トークンとコストの合計に影響します。
Bash実行メッセージ
bash RPC コマンドによって作成されます (LLM ツール呼び出しによってではありません)。
{
"role": "bashExecution",
"command": "ls -la",
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false,
"fullOutputPath": null,
"timestamp": 1733234567890
}アタッチメント
{
"id": "img1",
"type": "image",
"fileName": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"content": "base64-encoded-data...",
"extractedText": null,
"preview": null
}例: 基本クライアント (Python)
import subprocess
import json
proc = subprocess.Popen(
["pi", "--mode", "rpc", "--no-session"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
def send(cmd):
proc.stdin.write(json.dumps(cmd) + "\n")
proc.stdin.flush()
def read_events():
for line in proc.stdout:
yield json.loads(line)
# Send prompt
send({"type": "prompt", "message": "Hello!"})
# Process events
for event in read_events():
if event.get("type") == "message_update":
delta = event.get("assistantMessageEvent", {})
if delta.get("type") == "text_delta":
print(delta["delta"], end="", flush=True)
if event.get("type") == "agent_end":
print()
break例: インタラクティブクライアント (Node.js)
完全な対話型の例については test/rpc-example.ts を、型指定されたクライアントの実装については src/modes/rpc/rpc-client.ts を参照してください。
拡張 UI プロトコルを処理する完全な例については、examples/extensions/rpc-demo.ts 拡張とペアになる examples/rpc-extension-ui.ts を参照してください。
const { spawn } = require("child_process");
const { StringDecoder } = require("string_decoder");
const agent = spawn("pi", ["--mode", "rpc", "--no-session"]);
function attachJsonlReader(stream, onLine) {
const decoder = new StringDecoder("utf8");
let buffer = "";
stream.on("data", (chunk) => {
buffer += typeof chunk === "string" ? chunk : decoder.write(chunk);
while (true) {
const newlineIndex = buffer.indexOf("\n");
if (newlineIndex === -1) break;
let line = buffer.slice(0, newlineIndex);
buffer = buffer.slice(newlineIndex + 1);
if (line.endsWith("\r")) line = line.slice(0, -1);
onLine(line);
}
});
stream.on("end", () => {
buffer += decoder.end();
if (buffer.length > 0) {
onLine(buffer.endsWith("\r") ? buffer.slice(0, -1) : buffer);
}
});
}
attachJsonlReader(agent.stdout, (line) => {
const event = JSON.parse(line);
if (event.type === "message_update") {
const { assistantMessageEvent } = event;
if (assistantMessageEvent.type === "text_delta") {
process.stdout.write(assistantMessageEvent.delta);
}
}
});
// Send prompt
agent.stdin.write(JSON.stringify({ type: "prompt", message: "Hello" }) + "\n");
// Abort on Ctrl+C
process.on("SIGINT", () => {
agent.stdin.write(JSON.stringify({ type: "abort" }) + "\n");
});