Настройка, расширение, параметры платформы и справочник 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.

Обрамление

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

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

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

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

Команды

Подсказка

быстрый

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

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

управлять

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

Состояние

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

Модель

set_model

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

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

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

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

цикл_модель

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

Режимы очереди

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}

Уплотнение

компактный

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

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

abort_retry

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

{"type": "abort_retry"}

Ответ:

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

Баш

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`
```
всего 48
дрвхр-хр-х...
```

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

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

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

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

Команды

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, если она была предоставлена.

Типы событий

Событие Описание
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 Расширение выдало ошибку

агент_старт

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

{"type": "agent_start"}

агент_конец

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

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

агент_поселение

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

{"type": "agent_settled"}

начало_поворота/конец_поворота

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

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

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

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

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

очередь_обновление

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

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

расширение_ошибка

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

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

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

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

Ответы пользовательского интерфейса расширения (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..."
}

Типы

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

Модель

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

BashExecutionСообщение

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

Полный пример обработки протокола пользовательского интерфейса расширения см. в разделе 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");
});