Configuração, personalização, ajustes de plataforma e referências de API para Pi.

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 (suporta provider/id e 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\n removendo um \r final
  • 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:

  1. A saída do Bash é incluída no contexto LLM no próximo prompt, não imediatamente
  2. 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 via pi.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 com skill:)
  • 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 um extension_ui_request em stdout e bloqueia até que o cliente envie de volta um extension_ui_response em stdin com o id correspondente.
  • Métodos disparar e esquecer (notify, setStatus, setWidget, setTitle, set_editor_text): emite um extension_ui_request em 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() retorna undefined
  • setWorkingMessage(), setWorkingIndicator(), setFooter(), setHeader(), setEditorComponent(), setToolsExpanded() são autônomos
  • getEditorText() retorna ""
  • getToolsExpanded() retorna false
  • pasteToEditor() delega para setEditorText() (sem manipulação de colar/recolher)
  • getAllThemes() retorna []
  • getTheme() retorna undefined
  • setTheme() 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}

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:

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()
        break

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