Pi の設定、拡張、プラットフォーム設定、API リファレンス。

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+2028U+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...
```

これはつまり:

  1. Bash 出力は、すぐではなく 次のプロンプト の LLM コンテキストに含まれます
  2. プロンプトの前に複数の 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
    }
  }
}

tokenscost には、アシスタント メッセージ、ツールによって報告された使用状況、セッション全体にわたる圧縮/ブランチ サマリーの生成が含まれます。 contextUsage には、圧縮とフッター表示に使用される実際の現在のコンテキスト ウィンドウの推定値が含まれます。

使用可能なモデルまたはコンテキスト ウィンドウがない場合、contextUsage は省略されます。 contextUsage.tokenscontextUsage.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_updatepartialResult には、これまでに蓄積された出力 (デルタだけではなく) が含まれているため、クライアントは更新のたびにディスプレイを簡単に置き換えることができます。

キュー_アップデート

保留中のステアリングまたはフォローアップ キューが変更されるたびに発行されます。

{
  "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" で圧縮が成功した場合、willRetrytrue となり、エージェントは自動的にプロンプ​​トを再試行します。

圧縮が中止された場合、resultnullabortedtrue になります。

圧縮が失敗した場合 (例: API クォータの超過)、resultnullabortedfalseerrorMessage にはエラーの説明が含まれます。

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 つのカテゴリがあります。

  • ダイアログ メソッド (selectconfirminputeditor): stdout で extension_ui_request を発行し、クライアントが stdin で一致する id とともに extension_ui_response を返信するまでブロックします。
  • ファイアアンドフォーゲットメソッド (notifysetStatussetWidgetsetTitleset_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.hasUItrue です。実際の端末を必要とする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_responsevalue (選択されたオプション文字列) または cancelled: true

確認する

ユーザーに「はい/いいえ」の確認を求めます。

{
  "type": "extension_ui_request",
  "id": "uuid-2",
  "method": "confirm",
  "title": "Clear session?",
  "message": "All messages will be lost.",
  "timeout": 5000
}

予想される応答: extension_ui_responseconfirmed: true/false または cancelled: true

入力

ユーザーに自由形式のテキストの入力を求めます。

{
  "type": "extension_ui_request",
  "id": "uuid-3",
  "method": "input",
  "title": "Enter a value",
  "placeholder": "type something..."
}

予期される応答: extension_ui_responsevalue (入力されたテキスト) または cancelled: true

エディタ

オプションの事前入力コンテンツを含む複数行のテキスト エディターを開きます。

{
  "type": "extension_ui_request",
  "id": "uuid-4",
  "method": "editor",
  "title": "Edit some text",
  "prefill": "Line 1\nLine 2\nLine 3"
}

予想される応答: extension_ui_responsevalue (編集されたテキスト) または 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)

応答はダイアログ メソッド (selectconfirminputeditor) に対してのみ送信されます。 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..."
}

種類

ソースファイル:

モデル

{
  "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");
});