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.