RPC Modo
O modo RPC permite a operação sem cabeça do agente de codificação por meio de um protocolo JSON sobre stdin/stdout. Isso é útil para incorporar o agente em outros aplicativos, IDEs ou UIs personalizadas.
Nota para usuários Node.js/TypeScript: Se você estiver construindo uma aplicação Node.js, considere usar AgentSession diretamente de @earendil-works/pi-coding-agent em vez de gerar um subprocesso. Veja src/core/agent-session.ts para API. Para um cliente TypeScript baseado em subprocesso, consulte src/modes/rpc/rpc-client.ts.
Iniciando o modo RPC
pi --mode rpc [options]Opções comuns:
--provider <name>: Defina o provedor LLM (antrópico, openai, google, etc.)--model <pattern>: Padrão ou ID do modelo (suportaprovider/ide opcional:<thinking>)--name <name>/-n <name>: Defina o nome de exibição da sessão na inicialização--no-session: Desativa a persistência da sessão--session-dir <path>: Diretório de armazenamento de sessão personalizado
Visão geral do protocolo
- Comandos: objetos JSON enviados para stdin, um por linha
- Respostas: JSON objetos com
type: "response"indicando sucesso/falha do comando - Eventos: eventos do agente transmitidos para stdout como JSON linhas
Todos os comandos suportam um campo opcional id para correlação solicitação/resposta. Se fornecido, a resposta correspondente incluirá o mesmo id. Os eventos bash_execution_update também incluem o id do comando bash de origem.
Enquadramento
O modo RPC usa semântica JSONL estrita com LF (\n) como o único delimitador de registro.
Isso é importante para os clientes:
- Dividir registros apenas em
\n - Aceite a entrada opcional
\r\nremovendo um\rfinal - Não use leitores de linha genéricos que tratam separadores Unicode como novas linhas
Em particular, o nó readline não é compatível com protocolo para o modo RPC porque também se divide em U+2028 e U+2029, que são válidos dentro de strings JSON.
Comandos
Solicitando
incitar
Envie um prompt do usuário ao agente. A resposta do comando é emitida depois que o prompt é aceito, colocado na fila ou manipulado. Os eventos continuam sendo transmitidos de forma assíncrona após a aceitação.
{"id": "req-1", "type": "prompt", "message": "Hello, world!"}Com imagens:
{"type": "prompt", "message": "What's in this image?", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}Durante o streaming: se o agente já estiver transmitindo, você deverá especificar streamingBehavior para enfileirar a mensagem:
{"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}"steer": Coloca a mensagem na fila enquanto o agente está em execução. Ele é entregue após o turno atual do assistente terminar de executar suas chamadas de ferramenta, antes da próxima chamada do LLM."followUp": Espere até o agente terminar. A mensagem é entregue somente quando o agente para.
Se o agente estiver transmitindo e nenhum streamingBehavior for especificado, o comando retornará um erro.
Comandos de extensão: Se a mensagem for um comando de extensão (por exemplo, /mycommand), ela será executada imediatamente, mesmo durante o streaming. Os comandos de extensão gerenciam sua própria interação LLM via pi.sendMessage().
Expansão de entrada: Os comandos de habilidade (/skill:name) e prompt templates (/template) são expandidos antes do envio/enfileiramento.
Resposta:
{"id": "req-1", "type": "response", "command": "prompt", "success": true}success: true significa que o prompt foi aceito, colocado na fila ou tratado imediatamente. success: false significa que o prompt foi rejeitado antes da aceitação. As falhas após a aceitação são relatadas através do evento normal e do fluxo de mensagens, não como um segundo response para o mesmo ID de solicitação.
O campo images é opcional. Cada imagem usa o formato ImageContent: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}.
dirigir
Coloque uma mensagem de orientação na fila enquanto o agente está em execução. Ele é entregue após o turno atual do assistente terminar de executar suas chamadas de ferramenta, antes da próxima chamada do LLM. Os comandos de habilidade e prompt templates foram expandidos. Comandos de extensão não são permitidos (use prompt).
{"type": "steer", "message": "Stop and do this instead"}Com imagens:
{"type": "steer", "message": "Look at this instead", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}O campo images é opcional. Cada imagem usa o formato ImageContent (igual a prompt).
Resposta:
{"type": "response", "command": "steer", "success": true}Consulte set_steering_mode para controlar como as mensagens de direção são processadas.
seguir
Coloque uma mensagem de acompanhamento na fila para ser processada após a conclusão do agente. Entregue somente quando o agente não tiver mais chamadas de ferramenta ou mensagens de orientação. Os comandos de habilidade e prompt templates foram expandidos. Comandos de extensão não são permitidos (use prompt).
{"type": "follow_up", "message": "After you're done, also do this"}Com imagens:
{"type": "follow_up", "message": "Also check this image", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}O campo images é opcional. Cada imagem usa o formato ImageContent (igual a prompt).
Resposta:
{"type": "response", "command": "follow_up", "success": true}Consulte set_follow_up_mode para controlar como as mensagens de acompanhamento são processadas.
abortar
Anule a operação do agente atual.
{"type": "abort"}Resposta:
{"type": "response", "command": "abort", "success": true}nova_sessão
Inicie uma nova sessão. Pode ser cancelado por um manipulador de eventos de extensão session_before_switch.
{"type": "new_session"}Com rastreamento opcional da sessão pai:
{"type": "new_session", "parentSession": "/path/to/parent-session.jsonl"}Resposta:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": false}}Se uma extensão for cancelada:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": true}}Estado
get_state
Obtenha o estado atual da sessão.
{"type": "get_state"}Resposta:
{
"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
}
}O campo model é um objeto Model completo ou null. O campo sessionName é o nome de exibição definido por meio de set_session_name ou omitido se não for definido.
get_messages
Receba todas as mensagens da conversa.
{"type": "get_messages"}Resposta:
{
"type": "response",
"command": "get_messages",
"success": true,
"data": {"messages": [...]}
}As mensagens são objetos AgentMessage (veja Message Types).
Modelo
conjunto_modelo
Mude para um modelo específico.
{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}A resposta contém o objeto Model completo:
{
"type": "response",
"command": "set_model",
"success": true,
"data": {...}
}modelo_de_ciclo
Passe para o próximo modelo disponível. Retorna dados null se apenas um modelo estiver disponível.
{"type": "cycle_model"}Resposta:
{
"type": "response",
"command": "cycle_model",
"success": true,
"data": {
"model": {...},
"thinkingLevel": "medium",
"isScoped": false
}
}O campo model é um objeto Model completo.
get_available_models
Liste todos os modelos configurados.
{"type": "get_available_models"}A resposta contém uma matriz de objetos Model completos:
{
"type": "response",
"command": "get_available_models",
"success": true,
"data": {
"models": [...]
}
}Pensamento
set_thinking_level
Defina o nível de raciocínio/pensamento para modelos que o suportem.
{"type": "set_thinking_level", "level": "high"}Níveis: "off", "minimal", "low", "medium", "high", "xhigh", "max"
"xhigh" e "max" são expostos somente quando suportados pelo modelo selecionado. Alguns modelos, incluindo o GPT-5.6, expõem ambos.
Resposta:
{"type": "response", "command": "set_thinking_level", "success": true}nível_de_pensamento_de_ciclo
Percorra os níveis de pensamento disponíveis. Retorna dados null se o modelo não suportar o pensamento.
{"type": "cycle_thinking_level"}Resposta:
{
"type": "response",
"command": "cycle_thinking_level",
"success": true,
"data": {"level": "high"}
}get_available_thinking_levels
Liste os níveis de pensamento suportados pelo modelo atual. Retorna ["off"] para um modelo sem suporte de raciocínio.
{"type": "get_available_thinking_levels"}Resposta:
{
"type": "response",
"command": "get_available_thinking_levels",
"success": true,
"data": {
"levels": ["off", "minimal", "low", "medium", "high"]
}
}Modos de fila
set_steering_mode
Controle como as mensagens de direção (de steer) são entregues.
{"type": "set_steering_mode", "mode": "one-at-a-time"}Modos:
"all": Entrega todas as mensagens de direção após o turno atual do assistente terminar de executar suas chamadas de ferramenta"one-at-a-time": Entrega uma mensagem de direção por curva de assistente concluída (padrão)
Resposta:
{"type": "response", "command": "set_steering_mode", "success": true}set_follow_up_mode
Controle como as mensagens de acompanhamento (de follow_up) são entregues.
{"type": "set_follow_up_mode", "mode": "one-at-a-time"}Modos:
"all": Entrega todas as mensagens de acompanhamento quando o agente termina"one-at-a-time": Entrega uma mensagem de acompanhamento por conclusão do agente (padrão)
Resposta:
{"type": "response", "command": "set_follow_up_mode", "success": true}Compactação
compactar
Compacte manualmente o contexto da conversa para reduzir o uso de token.
{"type": "compact"}Com instruções personalizadas:
{"type": "compact", "customInstructions": "Focus on code changes"}Resposta:
{
"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 é uma estimativa heurística sobre o contexto da mensagem reconstruída imediatamente após a compactação, não uma contagem exata de tokens do provedor. usage relata a chamada ou chamadas LLM que geraram o resumo e podem ser omitidas por manipuladores de compactação customizados.
set_auto_compaction
Ative ou desative a compactação automática quando o contexto estiver quase cheio.
{"type": "set_auto_compaction", "enabled": true}Resposta:
{"type": "response", "command": "set_auto_compaction", "success": true}Tentar novamente
set_auto_retry
Habilite ou desabilite a nova tentativa automática em erros transitórios (sobrecarregado, limite de taxa, 5xx).
{"type": "set_auto_retry", "enabled": true}Resposta:
{"type": "response", "command": "set_auto_retry", "success": true}abortar_retry
Abortar uma nova tentativa em andamento (cancelar o atraso e parar de tentar novamente).
{"type": "abort_retry"}Resposta:
{"type": "response", "command": "abort_retry", "success": true}Bash
bash
Execute um comando shell e adicione saída ao contexto da conversa. Fluxos de saída como eventos bash_execution_update enquanto o comando é executado; a resposta contém o resultado final.
{"id": "req-1", "type": "bash", "command": "ls -la"}Inclua um id para associar eventos bash_execution_update transmitidos a este comando.
Resposta:
{
"id": "req-1",
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false
}
}Se a saída foi truncada, inclui fullOutputPath:
{
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "truncated output...",
"exitCode": 0,
"cancelled": false,
"truncated": true,
"fullOutputPath": "/tmp/pi-bash-abc123.log"
}
}Como os resultados bash chegam ao LLM:
O comando bash é executado imediatamente e retorna BashResult. Internamente, um BashExecutionMessage é criado e armazenado no estado de mensagem do agente.
Quando o próximo comando prompt é enviado, todas as mensagens (incluindo BashExecutionMessage) são transformadas antes de serem enviadas ao LLM. O BashExecutionMessage é convertido em UserMessage com este formato:
Ran `ls -la`
```
total 48
drwxr-xr-x...
```Isso significa:
- A saída do Bash é incluída no contexto LLM no próximo prompt, não imediatamente
- Vários comandos bash podem ser executados antes de um prompt; todas as saídas serão incluídas
abortar_bash
Abortar um comando bash em execução.
{"type": "abort_bash"}Resposta:
{"type": "response", "command": "abort_bash", "success": true}Sessão
get_session_stats
Obtenha uso de token, estatísticas de custo e uso atual da janela de contexto.
{"type": "get_session_stats"}Resposta:
{
"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 e cost incluem mensagens de assistente, uso relatado por ferramentas e geração de compactação/resumo de ramificação em toda a sessão. contextUsage contém a estimativa atual da janela de contexto usada para compactação e exibição de rodapé.
contextUsage é omitido quando nenhum modelo ou janela de contexto está disponível. contextUsage.tokens e contextUsage.percent são null imediatamente após a compactação até que uma nova resposta do assistente pós-compactação forneça dados de uso válidos.
exportação_html
Exporte a sessão para um arquivo HTML.
{"type": "export_html"}Com caminho personalizado:
{"type": "export_html", "outputPath": "/tmp/session.html"}Resposta:
{
"type": "response",
"command": "export_html",
"success": true,
"data": {"path": "/tmp/session.html"}
}switch_session
Carregue um arquivo de sessão diferente. Pode ser cancelado por um manipulador de eventos de extensão session_before_switch.
{"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}Resposta:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}Se um ramal cancelou a troca:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": true}}garfo
Crie uma nova bifurcação a partir de uma mensagem de usuário anterior na ramificação ativa. Pode ser cancelado por um manipulador de eventos de extensão session_before_fork. Retorna o texto da mensagem que está sendo bifurcada.
{"type": "fork", "entryId": "abc123"}Resposta:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": false}
}Se uma extensão cancelou a bifurcação:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": true}
}clone
Duplique a ramificação ativa atual em uma nova sessão na posição atual. Pode ser cancelado por um manipulador de eventos de extensão session_before_fork.
{"type": "clone"}Resposta:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": false}
}Se uma extensão cancelou o clone:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": true}
}get_fork_messages
Obtenha mensagens do usuário disponíveis para bifurcação.
{"type": "get_fork_messages"}Resposta:
{
"type": "response",
"command": "get_fork_messages",
"success": true,
"data": {
"messages": [
{"entryId": "abc123", "text": "First prompt..."},
{"entryId": "def456", "text": "Second prompt..."}
]
}
}get_entries
Obtenha todas as entradas da sessão em ordem de acréscimo (excluindo o cabeçalho da sessão). A sessão é uma árvore de entradas somente anexadas com IDs estáveis, portanto, um ID de entrada funciona como um cursor durável: passe o último ID de entrada que você viu como since para obter apenas entradas estritamente depois dele, mesmo após reinicializações do cliente. Ao contrário de get_messages, isso inclui histórico de pré-compactação e ramificações abandonadas.
{"type": "get_entries"}Com um cursor:
{"type": "get_entries", "since": "abc123"}Resposta:
{
"type": "response",
"command": "get_entries",
"success": true,
"data": {
"entries": [
{"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
],
"leafId": "def456"
}
}leafId é o id da entrada folha atual (null para uma sessão vazia), para que um cliente possa dizer em uma viagem de ida e volta se a filial ativa foi movida. Se since não corresponder a nenhum ID de entrada, a resposta será success: false.
get_tree
Obtenha a sessão como uma árvore de entradas. Cada nó é {entry, children, label?, labelTimestamp?}. Uma sessão bem formada possui uma única raiz; entradas órfãs (cadeia pai quebrada) também aparecem como raízes.
{"type": "get_tree"}Resposta:
{
"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
Obtenha o conteúdo de texto da última mensagem do assistente.
{"type": "get_last_assistant_text"}Resposta:
{
"type": "response",
"command": "get_last_assistant_text",
"success": true,
"data": {"text": "The assistant's response..."}
}Retorna {"text": null} se não existirem mensagens do assistente.
set_session_name
Defina um nome de exibição para a sessão atual. O nome aparece nas listagens de sessões e ajuda a identificar as sessões.
{"type": "set_session_name", "name": "my-feature-work"}Resposta:
{
"type": "response",
"command": "set_session_name",
"success": true
}O nome da sessão atual está disponível através de get_state no campo sessionName. Para definir o nome inicial ao iniciar o modo RPC, passe --name <name> ou -n <name> para o processo pi --mode rpc.
Comandos
obter_comandos
Obtenha os comandos disponíveis (comandos de extensão, prompt templates e habilidades). Eles podem ser invocados por meio do comando prompt prefixando /.
{"type": "get_commands"}Resposta:
{
"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"}
]
}
}Cada comando possui:
name: Nome do comando (invocar com/name)description: Descrição legível por humanos (opcional para comandos de extensão)source: Que tipo de comando:"extension": Registrado viapi.registerCommand()em uma extensão"prompt": Carregado de um arquivo de modelo de prompt.md"skill": Carregado de um diretório de habilidades (o nome é prefixado comskill:)
location: De onde foi carregado (opcional, não presente para extensões):"user": Nível do usuário (~/.pi/agent/)"project": Nível do projeto (./.pi/agent/)"path": Caminho explícito via CLI ou configurações
path: Caminho absoluto do arquivo para a fonte do comando (opcional)
Nota: Os comandos TUI integrados (/settings, /hotkeys, etc.) não estão incluídos. Eles são tratados apenas no modo interativo e não seriam executados se enviados via prompt.
Eventos
Os eventos são transmitidos para stdout como JSON linhas durante a operação do agente. Os eventos geralmente não incluem um campo id; bash_execution_update inclui o id de seu comando bash de origem quando um foi fornecido.
Tipos de eventos
| Evento | Descrição |
|---|---|
agent_start |
Agente começa a processar |
agent_end |
Uma execução do agente de baixo nível é concluída (ainda pode ser seguida por nova tentativa, compactação ou continuações na fila) |
agent_settled |
A execução do agente está totalmente liquidada; nenhuma nova tentativa automática, nova tentativa de compactação ou continuação na fila permanece |
turn_start |
Novo turno começa |
turn_end |
Turno concluído (inclui mensagem do assistente e resultados da ferramenta) |
message_start |
A mensagem começa |
message_update |
Atualização de streaming (deltas de texto/pensamento/toolcall) |
message_end |
Mensagem concluída |
bash_execution_update |
Bloco de saída de comando direto RPC bash |
tool_execution_start |
Ferramenta inicia execução |
tool_execution_update |
Progresso da execução da ferramenta (saída de streaming) |
tool_execution_end |
Ferramenta concluída |
queue_update |
Fila de orientação/acompanhamento pendente alterada |
compaction_start |
A compactação começa |
compaction_end |
Compactação concluída |
auto_retry_start |
A nova tentativa automática começa (após erro transitório) |
auto_retry_end |
A nova tentativa automática é concluída (sucesso ou falha final) |
summarization_retry_scheduled |
Nova tentativa agendada para um erro de compactação transitória ou de resumo de ramificação |
summarization_retry_attempt_start |
A solicitação de resumo repetida é iniciada |
summarization_retry_finished |
Loop de nova tentativa de resumo concluído |
extension_error |
A extensão gerou um erro |
agente_start
Emitido quando o agente começa a processar um prompt.
{"type": "agent_start"}agente_end
Emitido quando uma execução de agente de baixo nível é concluída. Contém todas as mensagens geradas durante esta execução. Se willRetry for verdadeiro, uma nova tentativa automática ocorrerá.
{
"type": "agent_end",
"messages": [...],
"willRetry": false
}agente_settled
Emitido após a conclusão da execução completa no nível da sessão. Neste ponto, Pi não continuará automaticamente através de novas tentativas, novas tentativas de compactação ou mensagens de acompanhamento enfileiradas.
{"type": "agent_settled"}turn_start / turn_end
Um turno consiste em uma resposta do assistente mais quaisquer chamadas e resultados de ferramenta resultantes.
{"type": "turn_start"}{
"type": "turn_end",
"message": {...},
"toolResults": [...]
}mensagem_início / mensagem_fim
Emitido quando uma mensagem começa e é concluída. O campo message contém um AgentMessage.
{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}message_update (transmissão)
Emitido durante o streaming de mensagens do assistente. Contém um evento delta sem um instantâneo de mensagem cumulativo.
{
"type": "message_update",
"assistantMessageEvent": {
"type": "text_delta",
"contentIndex": 0,
"delta": "Hello "
}
}O campo assistantMessageEvent contém um destes tipos delta:
| Tipo | Descrição |
|---|---|
text_start |
Bloqueio de conteúdo de texto iniciado |
text_delta |
Pedaço de conteúdo de texto |
text_end |
O bloco de conteúdo de texto terminou |
thinking_start |
Bloqueio de pensamento iniciado |
thinking_delta |
Pedaço de conteúdo pensando |
thinking_end |
O bloqueio de pensamento terminou |
toolcall_start |
Chamada de ferramenta iniciada |
toolcall_delta |
Parte de argumentos de chamada de ferramenta |
toolcall_end |
Chamada de ferramenta encerrada (inclui objeto toolCall completo) |
Exemplo de streaming de uma resposta de texto:
{"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 omite intencionalmente o antigo campo cumulativo message e
assistantMessageEvent.partial. Os clientes que precisam de uma mensagem parcial ao vivo devem montá-la
de message_start e eventos subsequentes usando contentIndex. Tratar message_end.message
como autoritário. Para chamadas de ferramenta, buffer toolcall_delta.delta; toolcall_end.toolCall
contém a chamada concluída.
bash_execution_update
Emitido uma vez para cada pedaço de saída de um comando bash direto. id corresponde ao id do comando, permitindo que os clientes associem a saída ao comando correto.
Os eventos transmitem toda a saída enquanto o comando é executado, mesmo que o output da resposta bash final esteja truncado.
{
"type": "bash_execution_update",
"id": "req-1",
"delta": "total 48\n"
}tool_execution_start / tool_execution_update / tool_execution_end
Emitido quando uma ferramenta é iniciada, transmite o progresso e conclui a execução.
{
"type": "tool_execution_start",
"toolCallId": "call_abc123",
"toolName": "bash",
"args": {"command": "ls -la"}
}Durante a execução, os eventos tool_execution_update transmitem resultados parciais (por exemplo, bash produz a saída quando chega):
{
"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}
}
}Quando concluído:
{
"type": "tool_execution_end",
"toolCallId": "call_abc123",
"toolName": "bash",
"result": {
"content": [{"type": "text", "text": "total 48\n..."}],
"details": {...}
},
"isError": false
}Use toolCallId para correlacionar eventos. O partialResult em tool_execution_update contém a saída acumulada até agora (não apenas o delta), permitindo que os clientes simplesmente substituam sua exibição em cada atualização.
queue_update
Emitido sempre que a fila de direcionamento ou acompanhamento pendente é alterada.
{
"type": "queue_update",
"steering": ["Focus on error handling"],
"followUp": ["After that, summarize the result"]
}compactação_início / compactação_fim
Emitido durante a execução da compactação, seja ela manual ou automática.
{"type": "compaction_start", "reason": "threshold"}O campo reason é "manual", "threshold" ou "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
}Se reason for "overflow" e a compactação for bem-sucedida, willRetry será true e o agente tentará novamente o prompt automaticamente.
Se a compactação foi abortada, result é null e aborted é true.
Se a compactação falhou (por exemplo, API cota excedida), result é null, aborted é false e errorMessage contém a descrição do erro.
auto_retry_start /auto_retry_end
Emitido quando uma nova tentativa automática é acionada após um erro transitório (sobrecarregado, limite de taxa, 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
}Em caso de falha final (máximo de tentativas excedido):
{
"type": "auto_retry_end",
"success": false,
"attempt": 3,
"finalError": "529 overloaded_error: Overloaded"
}summarization_retry_scheduled / summarization_retry_attempt_start / summarization_retry_finished
Emitido quando a compactação ou o resumo do branch são repetidos após um erro transitório do provedor. Esses eventos usam as mesmas configurações de novas tentativas que as novas tentativas automáticas de giro do assistente.
{
"type": "summarization_retry_scheduled",
"attempt": 1,
"maxAttempts": 3,
"delayMs": 2000,
"errorMessage": "terminated"
}{
"type": "summarization_retry_attempt_start",
"source": "compaction",
"reason": "threshold"
}Para resumos de ramificações, source é "branchSummary" e nenhum reason está presente.
{
"type": "summarization_retry_finished"
}erro_de_extensão
Emitido quando uma extensão gera um erro.
{
"type": "extension_error",
"extensionPath": "/path/to/extension.ts",
"event": "tool_call",
"error": "Error message..."
}Protocolo de UI de extensão
Extensions pode solicitar interação do usuário via ctx.ui.select(), ctx.ui.confirm(), etc. No modo RPC, eles são traduzidos em um subprotocolo de solicitação/resposta no topo do fluxo base de comando/evento.
Existem duas categorias de métodos de extensão de UI:
- Métodos de diálogo (
select,confirm,input,editor): emite umextension_ui_requestem stdout e bloqueia até que o cliente envie de volta umextension_ui_responseem stdin com oidcorrespondente. - Métodos disparar e esquecer (
notify,setStatus,setWidget,setTitle,set_editor_text): emite umextension_ui_requestem stdout, mas não espera uma resposta. O cliente pode exibir as informações ou ignorá-las.
Se um método de diálogo incluir um campo timeout, o lado do agente resolverá automaticamente com um valor padrão quando o tempo limite expirar. O cliente não precisa rastrear tempos limite.
Alguns métodos ExtensionUIContext não são suportados ou degradados no modo RPC porque requerem acesso direto TUI:
custom()retornaundefinedsetWorkingMessage(),setWorkingIndicator(),setFooter(),setHeader(),setEditorComponent(),setToolsExpanded()são autônomosgetEditorText()retorna""getToolsExpanded()retornafalsepasteToEditor()delega parasetEditorText()(sem manipulação de colar/recolher)getAllThemes()retorna[]getTheme()retornaundefinedsetTheme()retorna{ success: false, error: "..." }
Nota: ctx.mode é "rpc" e ctx.hasUI é true no modo RPC porque os métodos de diálogo e disparar e esquecer são funcionais por meio do subprotocolo de extensão da UI. Use ctx.mode === "tui" para proteger recursos específicos de TUI, como custom(), que requerem um terminal real.
Solicitações de UI de extensão (stdout)
Todas as solicitações possuem type: "extension_ui_request", um campo id exclusivo e um campo method.
selecione
Solicita ao usuário que escolha em uma lista. Os métodos de diálogo com um campo timeout incluem o tempo limite em milissegundos; o agente resolve automaticamente com undefined se o cliente não responder a tempo.
{
"type": "extension_ui_request",
"id": "uuid-1",
"method": "select",
"title": "Allow dangerous command?",
"options": ["Allow", "Block"],
"timeout": 10000
}Resposta esperada: extension_ui_response com value (a string de opção selecionada) ou cancelled: true.
confirmar
Solicita ao usuário uma confirmação sim/não.
{
"type": "extension_ui_request",
"id": "uuid-2",
"method": "confirm",
"title": "Clear session?",
"message": "All messages will be lost.",
"timeout": 5000
}Resposta esperada: extension_ui_response com confirmed: true/false ou cancelled: true.
entrada
Solicita ao usuário um texto de formato livre.
{
"type": "extension_ui_request",
"id": "uuid-3",
"method": "input",
"title": "Enter a value",
"placeholder": "type something..."
}Resposta esperada: extension_ui_response com value (o texto inserido) ou cancelled: true.
editor
Abra um editor de texto multilinhas com conteúdo pré-preenchido opcional.
{
"type": "extension_ui_request",
"id": "uuid-4",
"method": "editor",
"title": "Edit some text",
"prefill": "Line 1\nLine 2\nLine 3"
}Resposta esperada: extension_ui_response com value (o texto editado) ou cancelled: true.
notificar
Exibir uma notificação. Dispare e esqueça, nenhuma resposta é esperada.
{
"type": "extension_ui_request",
"id": "uuid-5",
"method": "notify",
"message": "Command blocked by user",
"notifyType": "warning"
}O campo notifyType é "info", "warning" ou "error". O padrão é "info" se omitido.
definirStatus
Defina ou desmarque uma entrada de status no rodapé/barra de status. Dispare e esqueça.
{
"type": "extension_ui_request",
"id": "uuid-6",
"method": "setStatus",
"statusKey": "my-ext",
"statusText": "Turn 3 running..."
}Envie statusText: undefined (ou omita) para limpar a entrada de status dessa chave.
setWidget
Defina ou desmarque um widget (bloco de linhas de texto) exibido acima ou abaixo do editor. Dispare e esqueça.
{
"type": "extension_ui_request",
"id": "uuid-7",
"method": "setWidget",
"widgetKey": "my-ext",
"widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
"widgetPlacement": "aboveEditor"
}Envie widgetLines: undefined (ou omita) para limpar o widget. O campo widgetPlacement é "aboveEditor" (padrão) ou "belowEditor". Apenas matrizes de string são suportadas no modo RPC; fábricas de componentes são ignoradas.
definirTítulo
Defina o título da janela/guia do terminal. Dispare e esqueça.
{
"type": "extension_ui_request",
"id": "uuid-8",
"method": "setTitle",
"title": "pi - my project"
}set_editor_text
Defina o texto no editor de entrada. Dispare e esqueça.
{
"type": "extension_ui_request",
"id": "uuid-9",
"method": "set_editor_text",
"text": "prefilled text for the user"
}Respostas da UI de extensão (stdin)
As respostas são enviadas apenas para métodos de diálogo (select, confirm, input, editor). O id deve corresponder à solicitação.
Resposta de valor (seleção, entrada, editor)
{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}Resposta de confirmação (confirmar)
{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}Resposta de cancelamento (qualquer caixa de diálogo)
Ignore qualquer método de diálogo. A extensão recebe undefined (para seleção/entrada/editor) ou false (para confirmação).
{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}Tratamento de erros
Comandos com falha retornam uma resposta com success: false:
{
"type": "response",
"command": "set_model",
"success": false,
"error": "Model not found: invalid/model"
}Erros de análise:
{
"type": "response",
"command": "parse",
"success": false,
"error": "Failed to parse command: Unexpected token..."
}Tipos
Arquivos de origem:
packages/ai/src/types.ts-Model,UserMessage,AssistantMessage,ToolResultMessagepackages/agent/src/types.ts-AgentMessage,AgentEventsrc/core/messages.ts-BashExecutionMessagesrc/modes/json-event.ts-JsonAgentSessionEventsrc/modes/rpc/rpc-types.ts- RPC tipos de comando/resposta, tipos de solicitação/resposta da UI de extensão
Modelo
{
"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
}
}Mensagem do usuário
{
"role": "user",
"content": "Hello!",
"timestamp": 1733234567890,
"attachments": []
}O campo content pode ser uma string ou um array de blocos TextContent/ImageContent.
Mensagem do assistente
{
"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
}Motivos de parada: "stop", "length", "toolUse", "error", "aborted"
FerramentaResultMessage
{
"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 é opcional e relata o trabalho LLM aninhado realizado pela ferramenta. Quando presente, contribui para o token da sessão e para os totais de custos.
BashExecutionMessage
Criado pelo comando bash RPC (não por chamadas de ferramenta LLM):
{
"role": "bashExecution",
"command": "ls -la",
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false,
"fullOutputPath": null,
"timestamp": 1733234567890
}Anexo
{
"id": "img1",
"type": "image",
"fileName": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"content": "base64-encoded-data...",
"extractedText": null,
"preview": null
}Exemplo: Cliente Básico (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()
breakExemplo: Cliente Interativo (Node.js)
Veja test/rpc-example.ts para um exemplo interativo completo, ou src/modes/rpc/rpc-client.ts para uma implementação de cliente digitada.
Para obter um exemplo completo de como lidar com o protocolo UI de extensão, consulte examples/rpc-extension-ui.ts que emparelha com a extensão 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");
});