RPC Modo
El modo RPC permite el funcionamiento sin cabeza del agente de codificación a través de un protocolo JSON sobre stdin/stdout. Esto resulta útil para integrar el agente en otras aplicaciones, IDE o UI personalizadas.
Nota para usuarios de Node.js/TypeScript: si está creando una aplicación Node.js, considere usar AgentSession directamente desde @earendil-works/pi-coding-agent en lugar de generar un subproceso. Vea src/core/agent-session.ts para el API. Para un cliente TypeScript basado en subprocesos, consulte src/modes/rpc/rpc-client.ts.
Iniciando modo RPC
pi --mode rpc [options]Opciones comunes:
--provider <name>: establece el proveedor de LLM (anthropic, openai, google, etc.)--model <pattern>: Patrón de modelo o ID (admiteprovider/idy opcional:<thinking>)--name <name>/-n <name>: establece el nombre para mostrar de la sesión al inicio--no-session: Desactivar la persistencia de la sesión--session-dir <path>: Directorio de almacenamiento de sesión personalizado
Descripción general del protocolo
- Comandos: JSON objetos enviados a stdin, uno por línea
- Respuestas: JSON objetos con
type: "response"que indican el éxito/fracaso del comando - Eventos: los eventos del agente se transmiten a stdout como JSON líneas
Todos los comandos admiten un campo id opcional para la correlación de solicitud/respuesta. Si se proporciona, la respuesta correspondiente incluirá el mismo id. Los eventos bash_execution_update también incluyen el id de su comando bash de origen.
Enmarcado
El modo RPC utiliza una semántica JSONL estricta con LF (\n) como único delimitador de registros.
Esto es importante para los clientes:
- Dividir registros solo en
\n - Acepte la entrada
\r\nopcional eliminando un\rfinal - No utilice lectores de líneas genéricos que traten los separadores Unicode como nuevas líneas.
En particular, el nodo readline no cumple con el protocolo para el modo RPC porque también se divide en U+2028 y U+2029, que son válidos dentro de las cadenas JSON.
Comandos
Incitación
inmediato
Envíe un mensaje de usuario al agente. La respuesta del comando se emite después de aceptar, poner en cola o manejar el mensaje. Los eventos continúan transmitiéndose de forma asincrónica después de la aceptación.
{"id": "req-1", "type": "prompt", "message": "Hello, world!"}Con imágenes:
{"type": "prompt", "message": "What's in this image?", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}Durante la transmisión: si el agente ya está transmitiendo, debe especificar streamingBehavior para poner en cola el mensaje:
{"type": "prompt", "message": "New instruction", "streamingBehavior": "steer"}"steer": Poner en cola el mensaje mientras el agente se está ejecutando. Se entrega después de que el turno actual del asistente termina de ejecutar sus llamadas a herramientas, antes de la siguiente llamada LLM."followUp": Espere hasta que termine el agente. El mensaje se entrega solo cuando el agente se detiene.
Si el agente está transmitiendo y no se especifica ningún streamingBehavior, el comando devuelve un error.
Comandos de extensión: si el mensaje es un comando de extensión (por ejemplo, /mycommand), se ejecuta inmediatamente incluso durante la transmisión. Los comandos de extensión gestionan su propia interacción LLM a través de pi.sendMessage().
Expansión de entrada: los comandos de habilidad (/skill:name) y prompt templates (/template) se expanden antes de enviar/poner en cola.
Respuesta:
{"id": "req-1", "type": "response", "command": "prompt", "success": true}success: true significa que el mensaje fue aceptado, puesto en cola o manejado inmediatamente. success: false significa que la solicitud fue rechazada antes de ser aceptada. Las fallas después de la aceptación se informan a través del evento normal y el flujo de mensajes, no como un segundo response para la misma identificación de solicitud.
El campo images es opcional. Cada imagen utiliza el formato ImageContent: {"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}.
buey
Ponga en cola un mensaje de dirección mientras el agente se está ejecutando. Se entrega después de que el turno actual del asistente termina de ejecutar sus llamadas a herramientas, antes de la siguiente llamada LLM. Los comandos de habilidad y prompt templates se amplían. Los comandos de extensión no están permitidos (use prompt en su lugar).
{"type": "steer", "message": "Stop and do this instead"}Con imágenes:
{"type": "steer", "message": "Look at this instead", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}El campo images es opcional. Cada imagen utiliza el formato ImageContent (igual que prompt).
Respuesta:
{"type": "response", "command": "steer", "success": true}Consulte set_steering_mode para controlar cómo se procesan los mensajes de dirección.
hacer un seguimiento
Ponga en cola un mensaje de seguimiento para que se procese una vez que finalice el agente. Se entrega solo cuando el agente no tiene más llamadas de herramientas ni mensajes de dirección. Los comandos de habilidad y prompt templates se amplían. Los comandos de extensión no están permitidos (use prompt en su lugar).
{"type": "follow_up", "message": "After you're done, also do this"}Con imágenes:
{"type": "follow_up", "message": "Also check this image", "images": [{"type": "image", "data": "base64-encoded-data", "mimeType": "image/png"}]}El campo images es opcional. Cada imagen utiliza el formato ImageContent (igual que prompt).
Respuesta:
{"type": "response", "command": "follow_up", "success": true}Consulte set_follow_up_mode para controlar cómo se procesan los mensajes de seguimiento.
abortar
Cancele la operación del agente actual.
{"type": "abort"}Respuesta:
{"type": "response", "command": "abort", "success": true}nueva_sesión
Inicie una nueva sesión. Puede ser cancelado por un controlador de eventos de extensión session_before_switch.
{"type": "new_session"}Con seguimiento opcional de la sesión de los padres:
{"type": "new_session", "parentSession": "/path/to/parent-session.jsonl"}Respuesta:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": false}}Si se cancela una extensión:
{"type": "response", "command": "new_session", "success": true, "data": {"cancelled": true}}Estado
obtener_estado
Obtener el estado actual de la sesión.
{"type": "get_state"}Respuesta:
{
"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
}
}El campo model es un objeto Model completo o null. El campo sessionName es el nombre para mostrar establecido mediante set_session_name, o se omite si no está configurado.
obtener_mensajes
Recibe todos los mensajes de la conversación.
{"type": "get_messages"}Respuesta:
{
"type": "response",
"command": "get_messages",
"success": true,
"data": {"messages": [...]}
}Los mensajes son AgentMessage objetos (ver Message Types).
Modelo
establecer_modelo
Cambie a un modelo específico.
{"type": "set_model", "provider": "anthropic", "modelId": "claude-sonnet-4-20250514"}La respuesta contiene el objeto Model completo:
{
"type": "response",
"command": "set_model",
"success": true,
"data": {...}
}modelo_ciclo
Pasa al siguiente modelo disponible. Devuelve datos null si solo hay un modelo disponible.
{"type": "cycle_model"}Respuesta:
{
"type": "response",
"command": "cycle_model",
"success": true,
"data": {
"model": {...},
"thinkingLevel": "medium",
"isScoped": false
}
}El campo model es un objeto Model completo.
obtener_modelos_disponibles
Enumere todos los modelos configurados.
{"type": "get_available_models"}La respuesta contiene una serie de objetos Model completos:
{
"type": "response",
"command": "get_available_models",
"success": true,
"data": {
"models": [...]
}
}Pensamiento
establecer_nivel_de_pensamiento
Establezca el nivel de razonamiento/pensamiento para los modelos que lo respaldan.
{"type": "set_thinking_level", "level": "high"}Niveles: "off", "minimal", "low", "medium", "high", "xhigh", "max"
"xhigh" y "max" están expuestos solo cuando son compatibles con el modelo seleccionado. Algunos modelos, incluido el GPT-5.6, exponen ambos.
Respuesta:
{"type": "response", "command": "set_thinking_level", "success": true}nivel_de_pensamiento_ciclo
Recorra los niveles de pensamiento disponibles. Devuelve datos null si el modelo no admite el pensamiento.
{"type": "cycle_thinking_level"}Respuesta:
{
"type": "response",
"command": "cycle_thinking_level",
"success": true,
"data": {"level": "high"}
}get_available_thinking_levels
Enumere los niveles de pensamiento respaldados por el modelo actual. Devuelve ["off"] para un modelo sin soporte de razonamiento.
{"type": "get_available_thinking_levels"}Respuesta:
{
"type": "response",
"command": "get_available_thinking_levels",
"success": true,
"data": {
"levels": ["off", "minimal", "low", "medium", "high"]
}
}Modos de cola
establecer_modo_dirección
Controle cómo se entregan los mensajes de dirección (desde steer).
{"type": "set_steering_mode", "mode": "one-at-a-time"}Modos:
"all": entrega todos los mensajes de dirección después de que el turno del asistente actual termine de ejecutar sus llamadas a herramientas"one-at-a-time": envía un mensaje de dirección por cada turno completado del asistente (predeterminado)
Respuesta:
{"type": "response", "command": "set_steering_mode", "success": true}set_follow_up_mode
Controle cómo se entregan los mensajes de seguimiento (desde follow_up).
{"type": "set_follow_up_mode", "mode": "one-at-a-time"}Modos:
"all": entregar todos los mensajes de seguimiento cuando el agente finalice"one-at-a-time": Entregar un mensaje de seguimiento por finalización del agente (predeterminado)
Respuesta:
{"type": "response", "command": "set_follow_up_mode", "success": true}Compactación
compacto
Contexto de conversación compacto manualmente para reducir el uso de tokens.
{"type": "compact"}Con instrucciones personalizadas:
{"type": "compact", "customInstructions": "Focus on code changes"}Respuesta:
{
"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 es una estimación heurística sobre el contexto del mensaje reconstruido inmediatamente después de la compactación, no un recuento de tokens exacto del proveedor. usage informa la llamada o llamadas de LLM que generaron el resumen y los controladores de compactación personalizados pueden omitirlo.
set_auto_compactation
Habilite o deshabilite la compactación automática cuando el contexto esté casi lleno.
{"type": "set_auto_compaction", "enabled": true}Respuesta:
{"type": "response", "command": "set_auto_compaction", "success": true}Rever
set_auto_retry
Habilite o deshabilite el reintento automático en errores transitorios (sobrecarga, límite de velocidad, 5xx).
{"type": "set_auto_retry", "enabled": true}Respuesta:
{"type": "response", "command": "set_auto_retry", "success": true}abortar_reintentar
Cancelar un reintento en curso (cancelar el retraso y detener el reintento).
{"type": "abort_retry"}Respuesta:
{"type": "response", "command": "abort_retry", "success": true}Intento
bash
Ejecute un comando de shell y agregue resultados al contexto de la conversación. La salida se transmite como eventos bash_execution_update mientras se ejecuta el comando; la respuesta contiene el resultado final.
{"id": "req-1", "type": "bash", "command": "ls -la"}Incluya un id para asociar eventos bash_execution_update transmitidos con este comando.
Respuesta:
{
"id": "req-1",
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false
}
}Si la salida se truncó, incluye fullOutputPath:
{
"type": "response",
"command": "bash",
"success": true,
"data": {
"output": "truncated output...",
"exitCode": 0,
"cancelled": false,
"truncated": true,
"fullOutputPath": "/tmp/pi-bash-abc123.log"
}
}Cómo llegan los resultados bash al LLM:
El comando bash se ejecuta inmediatamente y devuelve un BashResult. Internamente, se crea un BashExecutionMessage y se almacena en el estado del mensaje del agente.
Cuando se envía el siguiente comando prompt, todos los mensajes (incluido BashExecutionMessage) se transforman antes de enviarse al LLM. El BashExecutionMessage se convierte en un UserMessage con este formato:
Ran `ls -la`
```
total 48
drwxr-xr-x...
```Esto significa:
- La salida de Bash se incluye en el contexto LLM en el siguiente mensaje, no inmediatamente
- Se pueden ejecutar varios comandos bash antes de un mensaje; todas las salidas serán incluidas
abortar_bash
Cancelar un comando bash en ejecución.
{"type": "abort_bash"}Respuesta:
{"type": "response", "command": "abort_bash", "success": true}Sesión
get_session_stats
Obtenga el uso de tokens, estadísticas de costos y uso de la ventana de contexto actual.
{"type": "get_session_stats"}Respuesta:
{
"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 y cost incluyen mensajes del asistente, uso informado por herramientas y generación de resumen de compactación/rama durante toda la sesión. contextUsage contiene la estimación actual de la ventana de contexto utilizada para la compactación y la visualización del pie de página.
contextUsage se omite cuando no hay ningún modelo o ventana de contexto disponible. contextUsage.tokens y contextUsage.percent son null inmediatamente después de la compactación hasta que una nueva respuesta del asistente posterior a la compactación proporcione datos de uso válidos.
exportar_html
Exportar sesión a un archivo HTML.
{"type": "export_html"}Con ruta personalizada:
{"type": "export_html", "outputPath": "/tmp/session.html"}Respuesta:
{
"type": "response",
"command": "export_html",
"success": true,
"data": {"path": "/tmp/session.html"}
}cambiar_sesión
Cargue un archivo de sesión diferente. Puede ser cancelado por un controlador de eventos de extensión session_before_switch.
{"type": "switch_session", "sessionPath": "/path/to/session.jsonl"}Respuesta:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": false}}Si una extensión canceló el cambio:
{"type": "response", "command": "switch_session", "success": true, "data": {"cancelled": true}}tenedor
Cree una nueva bifurcación a partir de un mensaje de usuario anterior en la rama activa. Puede ser cancelado por un controlador de eventos de extensión session_before_fork. Devuelve el texto del mensaje del que se bifurca.
{"type": "fork", "entryId": "abc123"}Respuesta:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": false}
}Si una extensión canceló la bifurcación:
{
"type": "response",
"command": "fork",
"success": true,
"data": {"text": "The original prompt text...", "cancelled": true}
}clon
Duplica la rama activa actual en una nueva sesión en la posición actual. Puede ser cancelado por un controlador de eventos de extensión session_before_fork.
{"type": "clone"}Respuesta:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": false}
}Si una extensión canceló el clon:
{
"type": "response",
"command": "clone",
"success": true,
"data": {"cancelled": true}
}get_fork_messages
Obtenga mensajes de usuario disponibles para bifurcar.
{"type": "get_fork_messages"}Respuesta:
{
"type": "response",
"command": "get_fork_messages",
"success": true,
"data": {
"messages": [
{"entryId": "abc123", "text": "First prompt..."},
{"entryId": "def456", "text": "Second prompt..."}
]
}
}obtener_entradas
Obtenga todas las entradas de la sesión en orden de adición (excluyendo el encabezado de la sesión). La sesión es un árbol de entradas de solo adición con identificadores estables, por lo que un identificador de entrada funciona como un cursor duradero: pase el último identificador de entrada que vio como since para obtener solo entradas estrictamente posteriores, incluso durante los reinicios del cliente. A diferencia de get_messages, esto incluye el historial de precompactación y las ramas abandonadas.
{"type": "get_entries"}Con un cursor:
{"type": "get_entries", "since": "abc123"}Respuesta:
{
"type": "response",
"command": "get_entries",
"success": true,
"data": {
"entries": [
{"type": "message", "id": "def456", "parentId": "abc123", "timestamp": "...", "message": {"role": "user", "...": "..."}}
],
"leafId": "def456"
}
}leafId es la identificación de la entrada de hoja actual (null para una sesión vacía), por lo que un cliente puede saber en un viaje de ida y vuelta si la rama activa se movió. Si since no coincide con ningún ID de entrada, la respuesta es success: false.
obtener_arbol
Obtenga la sesión como un árbol de entradas. Cada nodo es {entry, children, label?, labelTimestamp?}. Una sesión bien formada tiene una única raíz; Las entradas huérfanas (cadena principal rota) también aparecen como raíces.
{"type": "get_tree"}Respuesta:
{
"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
Obtenga el contenido de texto del último mensaje del asistente.
{"type": "get_last_assistant_text"}Respuesta:
{
"type": "response",
"command": "get_last_assistant_text",
"success": true,
"data": {"text": "The assistant's response..."}
}Devuelve {"text": null} si no existen mensajes del asistente.
establecer_nombre_sesión
Establezca un nombre para mostrar para la sesión actual. El nombre aparece en los listados de sesiones y ayuda a identificar las sesiones.
{"type": "set_session_name", "name": "my-feature-work"}Respuesta:
{
"type": "response",
"command": "set_session_name",
"success": true
}El nombre de la sesión actual está disponible a través de get_state en el campo sessionName. Para establecer el nombre inicial al iniciar el modo RPC, pase --name <name> o -n <name> al proceso pi --mode rpc.
Comandos
obtener_comandos
Obtenga comandos disponibles (comandos de extensión, prompt templates y habilidades). Estos se pueden invocar mediante el comando prompt con el prefijo /.
{"type": "get_commands"}Respuesta:
{
"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 tiene:
name: Nombre del comando (invocar con/name)description: descripción legible por humanos (opcional para comandos de extensión)source: Qué tipo de comando:"extension": Registrado víapi.registerCommand()en una extensión"prompt": Cargado desde un archivo de plantilla de aviso.md"skill": Cargado desde un directorio de habilidades (el nombre tiene el prefijoskill:)
location: Desde dónde se cargó (opcional, no presente para extensiones):"user": nivel de usuario (~/.pi/agent/)"project": Nivel de proyecto (./.pi/agent/)"path": ruta explícita a través de CLI o configuración
path: Ruta absoluta del archivo a la fuente del comando (opcional)
Nota: Los comandos TUI integrados (/settings, /hotkeys, etc.) no están incluidos. Se manejan únicamente en modo interactivo y no se ejecutarían si se enviaran a través de prompt.
Eventos
Los eventos se transmiten a stdout como JSON líneas durante la operación del agente. Los eventos generalmente no incluyen un campo id; bash_execution_update incluye el id de su comando bash de origen cuando se proporcionó uno.
Tipos de eventos
| Evento | Descripción |
|---|---|
agent_start |
El agente comienza a procesar |
agent_end |
Se completa una ejecución del agente de bajo nivel (aún puede ir seguida de reintentos, compactación o continuaciones en cola) |
agent_settled |
La ejecución del agente está completamente liquidada; no queda ningún reintento automático, reintento de compactación ni continuación en cola |
turn_start |
Comienza un nuevo turno |
turn_end |
Turno completado (incluye mensaje del asistente y resultados de la herramienta) |
message_start |
Comienza el mensaje |
message_update |
Actualización de transmisión (deltas de texto/pensamiento/llamada a herramientas) |
message_end |
Mensaje completo |
bash_execution_update |
Fragmento de salida del comando directo RPC bash |
tool_execution_start |
La herramienta comienza a ejecutarse |
tool_execution_update |
Progreso de ejecución de la herramienta (salida de transmisión) |
tool_execution_end |
Herramienta completa |
queue_update |
Cambio de cola de dirección/seguimiento pendiente |
compaction_start |
Comienza la compactación |
compaction_end |
La compactación se completa |
auto_retry_start |
Comienza el reintento automático (después de un error transitorio) |
auto_retry_end |
El reintento automático se completa (éxito o fracaso final) |
summarization_retry_scheduled |
Reintento programado para un error de resumen de resumen de rama o compactación transitoria |
summarization_retry_attempt_start |
Se inicia la solicitud de resumen reintentada |
summarization_retry_finished |
Se completa el ciclo de reintento de resumen |
extension_error |
La extensión arrojó un error |
inicio_agente
Se emite cuando el agente comienza a procesar un mensaje.
{"type": "agent_start"}agente_end
Se emite cuando se completa la ejecución de un agente de bajo nivel. Contiene todos los mensajes generados durante esta ejecución. Si willRetry es verdadero, se realizará un reintento automático.
{
"type": "agent_end",
"messages": [...],
"willRetry": false
}agente_resuelto
Emitido después de que se establece la ejecución completa del nivel de sesión. En este punto, Pi no continuará automáticamente mediante el reintento, el reintento de compactación ni los mensajes de seguimiento en cola.
{"type": "agent_settled"}turn_start / turn_end
Un turno consta de la respuesta de un asistente más las llamadas y resultados de las herramientas resultantes.
{"type": "turn_start"}{
"type": "turn_end",
"message": {...},
"toolResults": [...]
}inicio_mensaje / fin_mensaje
Se emite cuando un mensaje comienza y finaliza. El campo message contiene un AgentMessage.
{"type": "message_start", "message": {...}}
{"type": "message_end", "message": {...}}message_update (Transmisión)
Emitido durante la transmisión de mensajes del asistente. Contiene un evento delta sin una instantánea de mensaje acumulativo.
{
"type": "message_update",
"assistantMessageEvent": {
"type": "text_delta",
"contentIndex": 0,
"delta": "Hello "
}
}El campo assistantMessageEvent contiene uno de estos tipos delta:
| Tipo | Descripción |
|---|---|
text_start |
Bloque de contenido de texto iniciado |
text_delta |
Fragmento de contenido de texto |
text_end |
Bloque de contenido de texto finalizado |
thinking_start |
Bloqueo de pensamiento iniciado. |
thinking_delta |
Fragmento de contenido de pensamiento |
thinking_end |
El bloque de pensamiento terminó |
toolcall_start |
Llamada de herramienta iniciada |
toolcall_delta |
Fragmento de argumentos de llamada de herramienta |
toolcall_end |
La llamada a la herramienta finalizó (incluye el objeto toolCall completo) |
Ejemplo de transmisión de una respuesta 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 el antiguo campo acumulativo message y
assistantMessageEvent.partial. Los clientes que necesiten un mensaje parcial en vivo deben armarlo.
desde message_start y eventos posteriores usando contentIndex. Tratar message_end.message
como autoritario. Para llamadas a herramientas, buffer toolcall_delta.delta; toolcall_end.toolCall
contiene la llamada completada.
bash_execution_update
Emitido una vez por cada fragmento de salida de un comando bash directo. id coincide con el id del comando, lo que permite a los clientes asociar la salida con el comando correcto.
Los eventos transmiten todos los resultados mientras se ejecuta el comando, incluso si la respuesta bash final output está truncada.
{
"type": "bash_execution_update",
"id": "req-1",
"delta": "total 48\n"
}inicio_ejecución_herramienta / actualización_ejecución_herramienta / fin_ejecución_herramienta
Se emite cuando una herramienta comienza, transmite el progreso y completa la ejecución.
{
"type": "tool_execution_start",
"toolCallId": "call_abc123",
"toolName": "bash",
"args": {"command": "ls -la"}
}Durante la ejecución, los eventos tool_execution_update transmiten resultados parciales (p. ej., salida bash a medida que llega):
{
"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}
}
}Cuando esté completo:
{
"type": "tool_execution_end",
"toolCallId": "call_abc123",
"toolName": "bash",
"result": {
"content": [{"type": "text", "text": "total 48\n..."}],
"details": {...}
},
"isError": false
}Utilice toolCallId para correlacionar eventos. El partialResult en tool_execution_update contiene la salida acumulada hasta el momento (no solo el delta), lo que permite a los clientes simplemente reemplazar su pantalla en cada actualización.
actualización_cola
Se emite cada vez que cambia la dirección pendiente o la cola de seguimiento.
{
"type": "queue_update",
"steering": ["Focus on error handling"],
"followUp": ["After that, summarize the result"]
}inicio_compactación / fin_compactación
Emitido cuando se ejecuta la compactación, ya sea manual o automática.
{"type": "compaction_start", "reason": "threshold"}El campo reason es "manual", "threshold" o "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
}Si reason era "overflow" y la compactación se realiza correctamente, willRetry es true y el agente volverá a intentarlo automáticamente.
Si se canceló la compactación, result es null y aborted es true.
Si la compactación falló (por ejemplo, se superó la cuota API), result es null, aborted es false y errorMessage contiene la descripción del error.
auto_retry_start / auto_retry_end
Se emite cuando se activa el reintento automático después de un error transitorio (sobrecarga, límite de velocidad, 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
}En caso de fallo final (se superó el número máximo de reintentos):
{
"type": "auto_retry_end",
"success": false,
"attempt": 3,
"finalError": "529 overloaded_error: Overloaded"
}reintento_summarización_programado / reintento_summarización_inicio_inicio / reintento_summarización_terminado
Se emite cuando se reintenta la compactación o el resumen de resumen de ramas después de un error transitorio del proveedor. Estos eventos utilizan la misma configuración de reintento que los reintentos automáticos de turno del asistente.
{
"type": "summarization_retry_scheduled",
"attempt": 1,
"maxAttempts": 3,
"delayMs": 2000,
"errorMessage": "terminated"
}{
"type": "summarization_retry_attempt_start",
"source": "compaction",
"reason": "threshold"
}Para resúmenes de sucursales, source es "branchSummary" y no hay reason presente.
{
"type": "summarization_retry_finished"
}error_extensión
Emitido cuando una extensión arroja un error.
{
"type": "extension_error",
"extensionPath": "/path/to/extension.ts",
"event": "tool_call",
"error": "Error message..."
}Protocolo de interfaz de usuario de extensión
Extensions puede solicitar la interacción del usuario a través de ctx.ui.select(), ctx.ui.confirm(), etc. En el modo RPC, estos se traducen en un subprotocolo de solicitud/respuesta en la parte superior del flujo de comando/evento base.
Hay dos categorías de métodos de interfaz de usuario de extensión:
- Métodos de diálogo (
select,confirm,input,editor): emite unextension_ui_requesten stdout y bloquea hasta que el cliente devuelva unextension_ui_responseen stdin con elidcorrespondiente. - Métodos de disparar y olvidar (
notify,setStatus,setWidget,setTitle,set_editor_text): emite unextension_ui_requesten stdout pero no esperes una respuesta. El cliente puede mostrar la información o ignorarla.
Si un método de diálogo incluye un campo timeout, el lado del agente se resolverá automáticamente con un valor predeterminado cuando expire el tiempo de espera. El cliente no necesita realizar un seguimiento de los tiempos de espera.
Algunos métodos ExtensionUIContext no son compatibles o están degradados en el modo RPC porque requieren acceso directo TUI:
custom()devuelveundefinedsetWorkingMessage(),setWorkingIndicator(),setFooter(),setHeader(),setEditorComponent(),setToolsExpanded()no son operacionesgetEditorText()devuelve""getToolsExpanded()devuelvefalsepasteToEditor()delega asetEditorText()(sin manejo de pegado/colapso)getAllThemes()devuelve[]getTheme()devuelveundefinedsetTheme()devuelve{ success: false, error: "..." }
Nota: ctx.mode es "rpc" y ctx.hasUI es true en el modo RPC porque el diálogo y los métodos de disparar y olvidar funcionan a través del subprotocolo de la interfaz de usuario de extensión. Utilice ctx.mode === "tui" para proteger funciones específicas de TUI como custom() que requieren una terminal real.
Solicitudes de interfaz de usuario de extensión (stdout)
Todas las solicitudes tienen type: "extension_ui_request", un campo id único y un campo method.
seleccionar
Solicite al usuario que elija de una lista. Los métodos de diálogo con un campo timeout incluyen el tiempo de espera en milisegundos; el agente se resuelve automáticamente con undefined si el cliente no responde a tiempo.
{
"type": "extension_ui_request",
"id": "uuid-1",
"method": "select",
"title": "Allow dangerous command?",
"options": ["Allow", "Block"],
"timeout": 10000
}Respuesta esperada: extension_ui_response con value (la cadena de opción seleccionada) o cancelled: true.
confirmar
Solicite al usuario una confirmación de sí/no.
{
"type": "extension_ui_request",
"id": "uuid-2",
"method": "confirm",
"title": "Clear session?",
"message": "All messages will be lost.",
"timeout": 5000
}Respuesta esperada: extension_ui_response con confirmed: true/false o cancelled: true.
aporte
Solicite al usuario texto de formato libre.
{
"type": "extension_ui_request",
"id": "uuid-3",
"method": "input",
"title": "Enter a value",
"placeholder": "type something..."
}Respuesta esperada: extension_ui_response con value (el texto ingresado) o cancelled: true.
editor
Abra un editor de texto de varias líneas con contenido precargado opcional.
{
"type": "extension_ui_request",
"id": "uuid-4",
"method": "editor",
"title": "Edit some text",
"prefill": "Line 1\nLine 2\nLine 3"
}Respuesta esperada: extension_ui_response con value (el texto editado) o cancelled: true.
notificar
Mostrar una notificación. Dispara y olvida, no se espera respuesta.
{
"type": "extension_ui_request",
"id": "uuid-5",
"method": "notify",
"message": "Command blocked by user",
"notifyType": "warning"
}El campo notifyType es "info", "warning" o "error". El valor predeterminado es "info" si se omite.
establecer estado
Establezca o borre una entrada de estado en el pie de página/barra de estado. Dispara y olvida.
{
"type": "extension_ui_request",
"id": "uuid-6",
"method": "setStatus",
"statusKey": "my-ext",
"statusText": "Turn 3 running..."
}Envíe statusText: undefined (u omítalo) para borrar la entrada de estado de esa clave.
establecerWidget
Configure o borre un widget (bloque de líneas de texto) que se muestra encima o debajo del editor. Dispara y olvida.
{
"type": "extension_ui_request",
"id": "uuid-7",
"method": "setWidget",
"widgetKey": "my-ext",
"widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
"widgetPlacement": "aboveEditor"
}Envíe widgetLines: undefined (u omítalo) para borrar el widget. El campo widgetPlacement es "aboveEditor" (predeterminado) o "belowEditor". Solo se admiten matrices de cadenas en el modo RPC; Se ignoran las fábricas de componentes.
establecer título
Establezca el título de la ventana/pestaña del terminal. Dispara y olvida.
{
"type": "extension_ui_request",
"id": "uuid-8",
"method": "setTitle",
"title": "pi - my project"
}establecer_editor_texto
Configure el texto en el editor de entrada. Dispara y olvida.
{
"type": "extension_ui_request",
"id": "uuid-9",
"method": "set_editor_text",
"text": "prefilled text for the user"
}Respuestas de la interfaz de usuario de extensión (stdin)
Las respuestas se envían únicamente para los métodos de diálogo (select, confirm, input, editor). El id debe coincidir con la solicitud.
Respuesta de valor (selección, entrada, editor)
{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}Respuesta de confirmación (confirmar)
{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}Respuesta de cancelación (cualquier diálogo)
Descarta cualquier método de diálogo. La extensión recibe undefined (para seleccionar/entrada/editor) o false (para confirmar).
{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}Manejo de errores
Los comandos fallidos devuelven una respuesta con success: false:
{
"type": "response",
"command": "set_model",
"success": false,
"error": "Model not found: invalid/model"
}Errores de análisis:
{
"type": "response",
"command": "parse",
"success": false,
"error": "Failed to parse command: Unexpected token..."
}Tipos
Archivos fuente:
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/respuesta, tipos de solicitud/respuesta de interfaz de usuario de extensión
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
}
}Mensaje de usuario
{
"role": "user",
"content": "Hello!",
"timestamp": 1733234567890,
"attachments": []
}El campo content puede ser una cadena o una matriz de bloques TextContent/ImageContent.
Mensaje del asistente
{
"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"
HerramientaResultadoMensaje
{
"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 es opcional e informa el trabajo de LLM anidado realizado por la herramienta. Cuando está presente, contribuye al token de sesión y a los costos totales.
Mensaje de ejecución de Bash
Creado por el comando bash RPC (no mediante llamadas a la herramienta LLM):
{
"role": "bashExecution",
"command": "ls -la",
"output": "total 48\ndrwxr-xr-x ...",
"exitCode": 0,
"cancelled": false,
"truncated": false,
"fullOutputPath": null,
"timestamp": 1733234567890
}Adjunto
{
"id": "img1",
"type": "image",
"fileName": "photo.jpg",
"mimeType": "image/jpeg",
"size": 102400,
"content": "base64-encoded-data...",
"extractedText": null,
"preview": null
}Ejemplo: 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()
breakEjemplo: Cliente interactivo (Node.js)
Consulte test/rpc-example.ts para ver un ejemplo interactivo completo o src/modes/rpc/rpc-client.ts para ver una implementación de cliente escrita.
Para ver un ejemplo completo de cómo manejar el protocolo UI de extensión, consulte examples/rpc-extension-ui.ts, que se combina con la extensión 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");
});