Skip to content

Protocolo (legacy) CLI <-> Desktop, version v1 ​

Fecha: 2026-05-03
Version del protocolo: v1 (alineado con --output-format stream-json actual del CLI Context Code).

Este documento describe el contrato NDJSON para la ruta legacy en la que el Desktop conversa con el runtime CLI/headless.

El Desktop actual usa un motor agéntico nativo (Rust) para el chat y mantiene este protocolo solo por compatibilidad/rollback y ejecuciones headless que aún lo requieran.

1. Spawn ​

Comando completo:

context -p \
  --output-format stream-json \
  --verbose \
  --input-format stream-json \
  --model <alias-o-nombre> \
  [--permission-mode <mode>] \
  [--allowed-tools <lista>] \
  [--disallowed-tools <lista>] \
  [--system-prompt <texto>]

Notas:

  • --verbose es OBLIGATORIO cuando --output-format=stream-json. El CLI rechaza el spawn sin el (CLI/src/main.tsx:1862-1867).
  • cwd del proceso = ruta del workspace.
  • En Windows el desktop wrappea con cmd /C context ... para resolver el shim .cmd de npm.
  • No setear CONTEXT_PROVIDER ni CONTEXT_MODEL: el CLI no los lee. El modelo se pasa por --model.

2. Input - stdin (NDJSON) ​

Cada turno del usuario es una linea JSON terminada en \n. Forma SDKUserMessage:

json
{
  "type": "user",
  "message": {
    "role": "user",
    "content": [{ "type": "text", "text": "Tu prompt aqui" }]
  },
  "parent_tool_use_id": null,
  "session_id": "<chat_id>"
}
  • Multi-turn: stdin se mantiene abierto entre turnos. EOF en stdin = fin de sesion.
  • El desktop no escribe texto plano: siempre serializa el envelope.

3. Output - stdout (NDJSON) ​

Cada linea de stdout es un objeto JSON. Tipos posibles, agrupados por type y subtype.

3.1 system / init ​

Emitido al inicio de cada sesion.

json
{
  "type": "system",
  "subtype": "init",
  "cwd": "/ruta/del/workspace",
  "tools": ["Bash", "Edit", "Read", "..."],
  "mcp_servers": [{ "name": "...", "status": "connected" }],
  "model": "claude-sonnet-4-6",
  "permissionMode": "default",
  "session_id": "abc123",
  "uuid": "..."
}

3.2 system / status ​

Cambios de estado del runtime (compactacion, etc.).

json
{ "type": "system", "subtype": "status", "status": "compacting", "session_id": "abc123" }

3.3 system / session_state_changed ​

json
{ "type": "system", "subtype": "session_state_changed", "state": "running", "session_id": "abc123" }

3.4 system / hook_* ​

Hooks del ciclo de vida (SessionStart, PreToolUse, etc.).

json
{
  "type": "system",
  "subtype": "hook_started",
  "hook_id": "hook_123",
  "hook_name": "my-hook",
  "hook_event": "SessionStart",
  "session_id": "abc123"
}

Subtypes: hook_started, hook_progress, hook_response. hook_response incluye exit_code, outcome, stdout, stderr.

3.5 system / task_* (subagentes / AgentTool) ​

Cuando el modelo invoca el AgentTool/Task, el CLI emite eventos estructurados por subagente. La clave de agrupacion es task_id.

json
{
  "type": "system",
  "subtype": "task_started",
  "task_id": "task_abc",
  "tool_use_id": "tool_use_xyz",
  "description": "Reviewing code",
  "task_type": "agent_task",
  "workflow_name": "code_review",
  "prompt": "Review the code and...",
  "session_id": "abc123"
}
json
{
  "type": "system",
  "subtype": "task_progress",
  "task_id": "task_abc",
  "tool_use_id": "tool_use_xyz",
  "summary": "Analyzed 5 files",
  "last_tool_name": "Read",
  "usage": { "total_tokens": 1024, "tool_uses": 3, "duration_ms": 2000 },
  "session_id": "abc123"
}
json
{
  "type": "system",
  "subtype": "task_notification",
  "task_id": "task_abc",
  "tool_use_id": "tool_use_xyz",
  "status": "completed",
  "summary": "Code review complete: 3 issues found",
  "usage": { "total_tokens": 2048, "tool_uses": 5, "duration_ms": 5000 },
  "output_file": "/tmp/task_output.jsonl",
  "session_id": "abc123"
}

Mapeo a estado en UI: task_started -> running, task_progress -> running (acumula lastTool, summary, usage), task_notification -> completed | failed | waiting | queued segun status.

3.6 user ​

Replay del input del usuario (con --replay-user-messages) o tool_results sinteticos.

json
{
  "type": "user",
  "message": {
    "role": "user",
    "content": [
      { "type": "tool_result", "tool_use_id": "tool_use_xyz", "content": "Resultado", "is_error": false }
    ]
  },
  "parent_tool_use_id": null,
  "session_id": "abc123"
}

3.7 assistant ​

Mensaje completo del modelo (no streaming por caracteres). message.content es un array que mezcla bloques text y tool_use.

json
{
  "type": "assistant",
  "message": {
    "role": "assistant",
    "content": [
      { "type": "text", "text": "Voy a revisar el archivo." },
      { "type": "tool_use", "id": "tool_use_xyz", "name": "Read", "input": { "file_path": "src/foo.ts" } }
    ]
  },
  "parent_tool_use_id": null,
  "session_id": "abc123"
}

3.8 tool_progress ​

Heartbeat de una herramienta en ejecucion.

json
{
  "type": "tool_progress",
  "tool_use_id": "tool_use_xyz",
  "tool_name": "Bash",
  "elapsed_time_seconds": 2.5,
  "task_id": "task_abc",
  "session_id": "abc123"
}

3.9 result ​

Cierre del turno. Subtypes:

  • success
  • error_during_execution
  • error_max_turns
  • error_max_budget_usd
  • error_max_structured_output_retries
json
{
  "type": "result",
  "subtype": "success",
  "duration_ms": 5000,
  "duration_api_ms": 4500,
  "is_error": false,
  "num_turns": 1,
  "result": "Archivo creado",
  "stop_reason": "end_turn",
  "total_cost_usd": 0.00123,
  "usage": {
    "inputTokens": 512,
    "outputTokens": 256,
    "cacheReadInputTokens": 0,
    "cacheCreationInputTokens": 0
  },
  "session_id": "abc123"
}

3.10 stream_event ​

Solo si se invoca con --include-partial-messages. Contiene RawMessageStreamEvent de la SDK Anthropic. El desktop hoy no lo usa (mensajes completos via assistant).

3.11 rate_limit_event ​

json
{
  "type": "rate_limit_event",
  "rate_limit_info": {
    "status": "allowed",
    "resetsAt": 1704067200000,
    "rateLimitType": "five_hour",
    "utilization": 0.5
  },
  "session_id": "abc123"
}

4. Asociacion tool_use <-> tool_result ​

  • El bloque tool_use en assistant.message.content[] lleva id.
  • El correspondiente tool_result aparece en el siguiente user.message.content[] con tool_use_id igual al id anterior.
  • Para subagentes, el tool_use_id raiz se mantiene en los eventos system/task_* para correlacionar.

5. Permisos y filtrado de herramientas ​

--permission-mode <mode> admite (segun CLI/src/entrypoints/sdk/coreSchemas.ts:339):

  • default - comportamiento estandar; pide confirmacion para acciones peligrosas.
  • acceptEdits - auto-acepta ediciones de archivos.
  • bypassPermissions - omite todos los permisos. Requiere ademas --allow-dangerously-skip-permissions.
  • plan - modo planificacion; no ejecuta herramientas.
  • dontAsk - no pregunta; deniega lo no preaprobado.

--allowed-tools <lista> permite reglas granulares como "Bash(git:*)" o "Edit". --disallowed-tools <lista> lista herramientas a bloquear (ej. "WebFetch,WebSearch" para deshabilitar red).

6. Stderr ​

stderr puede contener warnings de bootstrap, errores de carga de plugins, etc. El desktop los muestra como eventos rojos sin parsear.

7. Compatibilidad y versionado ​

  • v1 es el contrato actual. Cualquier cambio incompatible (renombre de campos, eliminacion de type/subtype) bumpea a v2.
  • Cambios aditivos (nuevos type o subtype, nuevos campos opcionales) siguen siendo v1 y son backward compatible. El desktop debe ignorar campos desconocidos sin romper.
  • Tests de contrato (Fase 6 del Roadmap): grabar transcripts NDJSON reales y replicarlos en CI para detectar regresiones.

Desarrollado con pasión e Inteligencia Artificial.