Настройка, расширение, параметры платформы и справочник API для Pi.

Режим RPC

Режим RPC обеспечивает автономную работу агента кодирования через протокол JSON через stdin/stdout. Это полезно для встраивания агента в другие приложения, IDE или пользовательские интерфейсы.

Примечание для пользователей Node.js/TypeScript: если вы создаете приложение Node.js, рассмотрите возможность использования AgentSession непосредственно из @earendil-works/pi-coding-agent вместо создания подпроцесса. См. src/core/agent-session.ts для API. Информацию о клиенте TypeScript на основе подпроцесса см. в src/modes/rpc/rpc-client.ts.

Запуск режима RPC

pi --mode rpc [options]

Распространенные варианты:

  • --provider <name>: установите поставщика LLM (anthropic, openai, google и т. д.).
  • --model <pattern>: шаблон или идентификатор модели (поддерживается provider/id и необязательно :<thinking>).
  • --name <name> / -n <name>: установите отображаемое имя сеанса при запуске.
  • --no-session: отключить сохранение сеанса.
  • --session-dir <path>: Пользовательский каталог хранения сеансов.

Обзор протокола

  • Команды: JSON объектов, отправленных на stdin, по одному в строке.
  • Ответы: JSON объекты с type: "response", обозначающими успех/неуспех команды.
  • События: события агента передаются на stdout в виде строк JSON.

Все команды поддерживают необязательное поле id для корреляции запроса/ответа. Если это предусмотрено, соответствующий ответ будет содержать тот же id. События bash_execution_update также включают id исходной команды bash.

Framing

В режиме RPC используется строгая семантика JSONL с LF (\n) в качестве единственного разделителя записей.

Для клиентов это важно:

  • Разделить записи только по \n
  • Примите необязательный ввод \r\n, удалив конечный \r
  • Не используйте общие программы чтения строк, которые рассматривают разделители Юникода как символы новой строки.

В частности, Узел readline не соответствует протоколу для режима RPC, поскольку он также разбивается на U+2028 и U+2029, которые действительны внутри строк JSON.

Команды

Prompting

prompt

Отправьте агенту приглашение пользователя. Ответ на команду выдается после того, как приглашение принято, поставлено в очередь или обработано. После принятия события продолжают передаваться асинхронно.

{"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), оно выполняется немедленно, даже во время потоковой передачи. Команды расширения управляют своим собственным взаимодействием с LLM через pi.sendMessage().

Расширение ввода: команды навыков (/skill:name) и prompt templates (/template) расширяются перед отправкой/постановкой в очередь.

Ответ:

{"id": "req-1", "type": "response", "command": "prompt", "success": true}

success: true означает, что приглашение было принято, поставлено в очередь или обработано немедленно. success: false означает, что запрос был отклонен до принятия. О сбоях после принятия сообщается через обычный поток событий и сообщений, а не как секунду response для того же идентификатора запроса.

Поле images является необязательным. Каждое изображение использует формат ImageContent: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}.

steer

Поставьте в очередь управляющее сообщение во время работы агента. Он доставляется после того, как текущий ход помощника завершает выполнение вызовов инструментов, до следующего вызова 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 для управления обработкой рулевых сообщений.

follow_up

Поставьте в очередь последующее сообщение, которое будет обработано после завершения работы агента. Доставляется только тогда, когда у агента больше нет вызовов инструментов или сообщений управления. Команды навыков и 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 для управления обработкой последующих сообщений.

abort

Прервать текущую операцию агента.

{"type": "abort"}

Ответ:

{"type": "response", "command": "abort", "success": true}

new_session

Начните новый сеанс. Может быть отменено обработчиком событий расширения 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}}

State

get_state

Получить текущее состояние сеанса.

{"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).

Model

set_model

Перейдите на конкретную модель.

{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}

Ответ содержит полный объект Model:

{
  "type": "response",
  "command": "set_model",
  "success": true,
  "data": {...}
}

cycle_model

Перейдите к следующей доступной модели. Возвращает данные 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": [...]
  }
}

Thinking

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}

cycle_thinking_level

Перебирайте доступные уровни мышления. Возвращает данные 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"]
  }
}

Queue Modes

set_steering_mode

Управляйте доставкой управляющих сообщений (от steer).

{"type": "set_steering_mode", "mode": "one-at-a-time"}

Режимы:

  • "all": доставить все управляющие сообщения после того, как текущий ход ассистента завершит выполнение вызовов инструментов.
  • "one-at-a-time": доставлять одно управляющее сообщение за каждый завершенный ход ассистента (по умолчанию).

Ответ:

{"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": доставлять одно последующее сообщение после завершения работы агента (по умолчанию).

Ответ:

{"type": "response", "command": "set_follow_up_mode", "success": true}

Compaction

compact

Вручную сжимайте контекст разговора, чтобы сократить использование токенов.

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

Retry

set_auto_retry

Включите или отключите автоматический повтор при временных ошибках (перегрузка, ограничение скорости, 5xx).

{"type": "set_auto_retry", "enabled": true}

Ответ:

{"type": "response", "command": "set_auto_retry", "success": true}

abort_retry

Прервать выполняющуюся повторную попытку (отменить задержку и прекратить повторную попытку).

{"type": "abort_retry"}

Ответ:

{"type": "response", "command": "abort_retry", "success": true}

Bash

bash

Выполните команду оболочки и добавьте вывод в контекст разговора. Вывод потоков как событий bash_execution_update во время выполнения команды; ответ содержит окончательный результат.

{"id": "req-1", "type": "bash", "command": "ls -la"}

Добавьте id, чтобы связать потоковые события bash_execution_update с этой командой.

Ответ:

{
  "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`
```
total 48
drwxr-xr-x ...
```

Это означает:

  1. Вывод Bash попадает в контекст LLM при следующем prompt, а не сразу.
  2. Перед prompt можно выполнить несколько команд Bash; будут включены все их результаты.

abort_bash

Прервите выполняющуюся команду Bash.

{"type": "abort_bash"}

Ответ:

{"type": "response", "command": "abort_bash", "success": true}

Session

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 сразу после уплотнения, пока новый ответ помощника после уплотнения не предоставит действительные данные об использовании.

export_html

Экспортируйте сеанс в файл HTML.

{"type": "export_html"}

С пользовательским путем:

{"type": "export_html", "outputPath": "/tmp/session.html"}

Ответ:

{
  "type": "response",
  "command": "export_html",
  "success": true,
  "data": {"path": "/tmp/session.html"}
}

switch_session

Загрузите другой файл сеанса. Может быть отменено обработчиком событий расширения 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}}

fork

Создайте новую вилку на основе предыдущего сообщения пользователя в активной ветке. Может быть отменено обработчиком событий расширения 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}
}

clone

Дублируйте текущую активную ветку в новый сеанс в текущей позиции. Может быть отменено обработчиком событий расширения 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

Получите все записи сеанса в порядке добавления (за исключением заголовка сеанса). Сеанс представляет собой дерево записей со стабильными идентификаторами, доступное только для добавления, поэтому идентификатор записи работает как устойчивый курсор: передайте идентификатор последней записи, который вы видели, как 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 — это идентификатор текущей конечной записи (null для пустого сеанса), поэтому клиент может за один проход определить, переместилась ли активная ветвь. Если since не соответствует ни одному идентификатору записи, ответом будет success: false.

get_tree

Получите сеанс в виде дерева записей. Каждый узел равен {entry, children, label?, labelTimestamp?}. Правильно сформированный сеанс имеет один корень; потерянные записи (разорванная родительская цепочка) также отображаются как корни.

{"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}, если сообщений помощника не существует.

set_session_name

Установите отображаемое имя для текущего сеанса. Имя появляется в списках сеансов и помогает идентифицировать сеансы.

{"type": "set_session_name", "name": "my-feature-work"}

Ответ:

{
  "type": "response",
  "command": "set_session_name",
  "success": true
}

Имя текущего сеанса доступно через get_state в поле sessionName. Чтобы установить исходное имя при запуске режима RPC, передайте --name <name> или -n <name> процессу pi --mode rpc.

Commands

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.

События

Во время работы агента события передаются в stdout как JSON строки. События обычно не включают поле id; bash_execution_update включает id исходной команды bash, если она была предоставлена.

Event Types

Событие Описание
agent_start Агент начинает обработку
agent_end Завершен один запуск агента низкого уровня (может последовать повторная попытка, уплотнение или продолжение в очереди)
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 Расширение выдало ошибку

agent_start

Генерируется, когда агент начинает обрабатывать приглашение.

{"type": "agent_start"}

agent_end

Генерируется при завершении одного запуска агента низкого уровня. Содержит все сообщения, созданные во время этого запуска. Если willRetry истинно, последует автоматическая повторная попытка.

{
  "type": "agent_end",
  "messages": [...],
  "willRetry": false
}

agent_settled

Выдается после завершения полного выполнения на уровне сеанса. На этом этапе Pi не будет автоматически продолжать повторную попытку, повторную попытку уплотнения или последующие сообщения в очереди.

{"type": "agent_settled"}

turn_start / turn_end

Ход состоит из одного ответа помощника, а также любых результирующих вызовов инструментов и результатов.

{"type": "turn_start"}
{
  "type": "turn_end",
  "message": {...},
  "toolResults": [...]
}

message_start / message_end

Генерируется, когда сообщение начинается и завершается. Поле message содержит AgentMessage.

{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}

message_update (Streaming)

Генерируется во время потоковой передачи сообщений помощника. Содержит разностное событие без совокупного снимка сообщения.

{
  "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_execution_update

Выдается один раз для каждого выходного фрагмента прямой команды bash. id соответствует id команды, что позволяет клиентам связать вывод с правильной командой.

События пересылают весь вывод во время выполнения команды, даже если output окончательного ответа bash усекается.

{
  "type": "bash_execution_update",
  "id": "req-1",
  "delta": "total 48\n"
}

tool_execution_start / tool_execution_update / tool_execution_end

Генерируется, когда инструмент запускается, отслеживает ход выполнения и завершает выполнение.

{
  "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 для корреляции событий. partialResult в tool_execution_update содержит накопленные на данный момент выходные данные (а не только дельту), что позволяет клиентам просто заменять свое отображение при каждом обновлении.

queue_update

Генерируется всякий раз, когда изменяется ожидающая управляющая или отслеживающая очередь.

{
  "type": "queue_update",
  "steering": ["Focus on error handling"],
  "followUp": ["After that, summarize the result"]
}

compaction_start / compaction_end

Генерируется во время уплотнения, ручного или автоматического.

{"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, abortedfalse, а 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"
}

extension_error

Генерируется, когда расширение выдает ошибку.

{
  "type": "extension_error",
  "extensionPath": "/path/to/extension.ts",
  "event": "tool_call",
  "error": "Error message..."
}

Расширение протокола пользовательского интерфейса

Extensions может запрашивать взаимодействие с пользователем через ctx.ui.select(), ctx.ui.confirm() и т. д. В режиме RPC они преобразуются в подпротокол запроса/ответа поверх базового потока команд/событий.

Существует две категории методов расширения пользовательского интерфейса:

  • Методы диалога (select, confirm, input, editor): выдают extension_ui_request на stdout и блокируются до тех пор, пока клиент не отправит обратно extension_ui_response на stdin с соответствующим id.
  • Методы «выстрелил и забыл» (notify, setStatus, setWidget, setTitle, set_editor_text): выдает extension_ui_request на stdout, но не ожидает ответа. Клиент может отображать информацию или игнорировать ее.

Если метод диалога включает поле timeout, на стороне агента будет автоматически разрешено значение по умолчанию по истечении тайм-аута. Клиенту не нужно отслеживать таймауты.

Некоторые методы ExtensionUIContext не поддерживаются или ухудшаются в режиме RPC, поскольку они требуют прямого доступа TUI:

  • custom() возвращает undefined
  • setWorkingMessage(), setWorkingIndicator(), setFooter(), setHeader(), setEditorComponent(), setToolsExpanded() не используются
  • getEditorText() возвращает ""
  • getToolsExpanded() возвращает false
  • pasteToEditor() делегирует setEditorText() (без обработки вставки/свертывания)
  • getAllThemes() возвращает []
  • getTheme() возвращает undefined
  • setTheme() возвращает { success: false, error: "..." }

Примечание. ctx.mode — это "rpc", а ctx.hasUI — это true в режиме RPC, поскольку методы диалога и «выстрелил и забыл» функционируют через подпротокол пользовательского интерфейса расширения. Используйте ctx.mode === "tui" для защиты функций, специфичных для TUI, таких как custom(), для которых требуется настоящий терминал.

Extension UI Requests (stdout)

Все запросы имеют type: "extension_ui_request", уникальное id и method поля.

select

Предложите пользователю выбрать из списка. Методы диалога с полем 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.

confirm

Запросите у пользователя подтверждение «да/нет».

{
  "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.

input

Запрашивайте у пользователя текст в произвольной форме.

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

Ожидаемый ответ: extension_ui_response с value (введенный текст) или cancelled: true.

editor

Откройте многострочный текстовый редактор с дополнительным предварительно заполненным содержимым.

{
  "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.

notify

Отображение уведомления. По принципу «выстрелил и забыл», ответа не ожидается.

{
  "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 (или опустите его), чтобы очистить запись статуса для этого ключа.

setWidget

Установите или очистите виджет (блок текстовых строк), отображаемый над или под редактором. Выстрелил и забыл.

{
  "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 поддерживаются только строковые массивы; фабрики компонентов игнорируются.

setTitle

Установите заголовок окна/вкладки терминала. Выстрелил и забыл.

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

Extension UI Responses (stdin)

Ответы отправляются только для диалоговых методов (select, confirm, input, editor). id должен соответствовать запросу.

Value response (select, input, editor)

{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}

Confirmation response (confirm)

{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}

Cancellation response (any dialog)

Отклоните любой метод диалога. Расширение получает 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..."
}

Типы

Исходные файлы:

Model

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

UserMessage

{
  "role": "user",
  "content": "Hello!",
  "timestamp": 1733234567890,
  "attachments": []
}

Поле content может быть строкой или массивом блоков TextContent/ImageContent.

AssistantMessage

{
  "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"

ToolResultMessage

{
  "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, выполняемой инструментом. Если он присутствует, он участвует в токенах сеанса и общей стоимости.

BashExecutionMessage

Создается командой bash RPC (а не вызовами инструментов LLM):

{
  "role": "bashExecution",
  "command": "ls -la",
  "output": "total 48\ndrwxr-xr-x ...",
  "exitCode": 0,
  "cancelled": false,
  "truncated": false,
  "fullOutputPath": null,
  "timestamp": 1733234567890
}

Attachment

{
  "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 для реализации типизированного клиента.

Полный пример обработки протокола пользовательского интерфейса расширения см. в разделе examples/rpc-extension-ui.ts, который сочетается с расширением examples/extensions/rpc-demo.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");
});