Ctrl K
ComunidadGuías…

docs/transport-protocol.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Transport Protocol

The gateway exposes agent execution under `/api/agents/{agentId}`. All agent routes require a valid auth context unless explicitly noted. Admin bearer tokens, user JWTs, and channel tokens are all accepted by the auth resolver where appropriate.

## Auth

| Flow | Endpoint / token | Use |
|------|------------------|-----|
| Admin bearer | `Authorization: Bearer $GATEWAY_ADMIN_TOKEN` | agent lifecycle, admin APIs, full agent route access |
| User JWT | `POST /api/auth/token` | browser/web user auth (identifies a person, not an agent) |
| Admin-minted user JWT | `POST /api/admin/auth/issue-token` | admin-only mint of a user JWT for an external identity (`channel`, `channelUserId`). `purpose: 'session'` (default) returns a normal 24h credential; `purpose: 'exchange'` returns a single-use short-lived token (≤600s, default 120s) for the `/connect` SSO deep link. |
| Connect SSO exchange | `POST /api/auth/exchange` | anonymous: swap an `exchange`-purpose JWT for a normal session JWT. Each `jti` is recorded on success so a replay is rejected with 401. Backs the web `/connect#token=…` deep link. |
| Channel bearer | generated or configured channel token | built-in/external channel adapters scoped to an agent/channel namespace |

`protected` agents require `agent_token` during device-token exchange (members self-join). `private` agents reject any sender without an explicit membership row at session-open time.

**JWT claims.** Every signed user JWT carries:

- `sub` — `${channel}:${channelUserId}` for the resolved identity
- `channel`, `channelUserId` — the same fields split out for convenience
- `purpose` — `'session'` (default, all API access) or `'exchange'` (only `POST /api/auth/exchange`)
- `issuer` — audit-only: `'device-key'` (from `POST /api/auth/token`) or `'admin-issued'` (from `POST /api/admin/auth/issue-token`)
- `jti` — unique token id; recorded by the exchange endpoint to enforce single-use

Legacy tokens minted before this claim set was added are treated as `purpose: 'session'`.

## Agent Routes

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/health` | gateway health |
| `GET` | `/api/agents` | list registered agents, admin only |
| `POST` | `/api/agents` | create an agent, admin only |
| `GET` | `/api/agents/{agentId}/health` | running/stopped health |
| `POST` | `/api/agents/{agentId}/manage/{start|stop|restart|delete}` | lifecycle, admin only |
| `POST` | `/api/agents/{agentId}/sessions` | open or resume a session |
| `GET` | `/api/agents/{agentId}/sessions` | list visible sessions |
| `POST` | `/api/agents/{agentId}/sessions/{sessionId}/messages` | post a message |
| `GET` | `/api/agents/{agentId}/sessions/{sessionId}/messages` | read history |
| `DELETE` | `/api/agents/{agentId}/sessions/{sessionId}` | delete a non-running direct session |
| `POST` | `/api/agents/{agentId}/sessions/{sessionId}/approve` | answer a pending tool approval |
| `POST` | `/api/agents/{agentId}/sessions/{sessionId}/checkpoint` | run session introspection checkpoint |
| `GET` | `/api/agents/{agentId}/sessions/{sessionId}/events` | durable SSE event stream |
| `WS` | `/api/agents/{agentId}/ws` | WebSocket RPC and event subscription |

Session list filters:

- `kind`
- `platform`
- `interactive=true|false`
- `limit`
- `channel`
- `metadata.{key}=value`

Channel tokens are namespace-scoped. A channel token with namespace `telegram` can only access session IDs prefixed with `telegram:`.

## Session Payloads

Open session:

```json
{
  "sessionId": "cli:default",
  "source": {
    "kind": "cli",
    "interactive": true,
    "platform": "cli",
    "type": "direct"
  },
  "metadata": {
    "username": "william"
  }
}
```

Post message:

```json
{
  "text": "Summarize this repo",
  "sender": {
    "channel": "cli",
    "channelUserId": "william",
    "displayName": "William"
  },
  "mentioned": true,
  "metadata": {}
}
```

Attachments are supported as `{ "type": "...", "url": "...", "data": "..." }`.

## Message Modes

### Fire And Forget

```http
POST /api/agents/main/sessions/cli%3Adefault/messages
```

Returns after the message is accepted:

```json
{ "sessionId": "cli:default", "messageId": "msg-...", "triggered": true }
```

### Append/Inject

```http
POST /api/agents/main/sessions/telegram%3A123/messages?append=true
```

Stores the message and publishes `user_message` without triggering the agent. `inject=true` is accepted as an alias.

### Wait

```http
POST /api/agents/main/sessions/cli%3Adefault/messages?wait=true&timeout=300000
```

Returns one JSON result when the turn ends:

```json
{
  "sessionId": "cli:default",
  "messageId": "msg-...",
  "text": "Done.",
  "toolCalls": [
    { "tool": "web_search", "isError": false, "text": "..." }
  ]
}
```

If group routing decides not to trigger the model, the response includes `triggered: false`.

### Inline SSE Stream

```http
POST /api/agents/main/sessions/cli%3Adefault/messages?stream=true
```

The response is an SSE stream. A `message_ack` frame may be emitted first, followed by normal outbound events until `agent_end`.

### Durable Events

```http
GET /api/agents/main/sessions/cli%3Adefault/events
```

This emits backlog events first, then live events, with `ready` and periodic `ping` frames.

## Outbound Events

Current event types:

- `thinking_delta`
- `thinking_final`
- `text_delta`
- `text_final`
- `tool_call`
- `tool_result`
- `approval_requested` — new request opened (`mode: 'realtime' | 'async'`)
- `approval_pending` — async-only notification routed to the owner's session
- `approval_resolved` — request closed (`decision`, optional `resolution`)
- `user_message`
- `agent_start`
- `agent_end`
- `error`
- `research_progress` — Deep Research operational progress (`phase`, `status`, `message`, optional `stepId` for deduplication against durable timeline rows and `counts`)
- `research_plan_ready` — a research plan version is awaiting review
- `research_source_update` — a research source changed state (`candidate`/`fetched`/`blocked`/`failed`/`unsupported`/`duplicate`)
- `research_report_ready` — final report persisted (`terminalStatus: 'completed' | 'budget_exhausted'`)

Events are persisted in `session_events` and broadcast through the in-memory `SessionEventBroker`.

Research events are live-only (the broker keeps a 100-event backlog per
session): on reconnect, clients reload the durable run/steps/sources over HTTP
first, then subscribe — live events only patch state and signal when to
refetch.

## Deep Research Routes

Nested under sessions; the same auth and participant checks apply:

```
POST   /api/agents/:agentId/sessions/:sessionId/research-runs            # 202, idempotent via clientRequestId
GET    /api/agents/:agentId/sessions/:sessionId/research-runs
GET    /api/agents/:agentId/sessions/:sessionId/research-runs/:runId
PATCH  /api/agents/:agentId/sessions/:sessionId/research-runs/:runId/plan     # optimistic expectedVersion; 409 on conflict
POST   /api/agents/:agentId/sessions/:sessionId/research-runs/:runId/actions  # approve_plan | pause | resume | cancel | refine | retry | increase_budget
GET    /api/agents/:agentId/sessions/:sessionId/research-runs/:runId/steps
GET    /api/agents/:agentId/sessions/:sessionId/research-runs/:runId/sources
GET    /api/agents/:agentId/sessions/:sessionId/research-runs/:runId/sources/:sourceId  # metadata + evidence excerpts
```

`POST /messages` returns `409 research_run_active` while the session's run is
actively planning/researching/synthesizing; chat resumes while the run awaits
plan approval or is paused. WebSocket parity methods: `research.start`,
`research.list`, `research.get`, `research.plan.update`, `research.action`,
`research.steps`, `research.sources` — streaming stays on `session.subscribe`.

## WebSocket

Connect to:

```text
ws://127.0.0.1:4000/api/agents/main/ws?token=<jwt-or-admin-token>
```

Supported methods:

- `session.open`
- `session.message`
- `session.approve`
- `session.checkpoint`
- `session.delete`
- `session.list`
- `session.history`
- `session.subscribe`
- `session.unsubscribe`

Requests are JSON-RPC-like:

```json
{
  "id": "1",
  "method": "session.message",
  "params": {
    "sessionId": "web:device",
    "message": { "text": "Hello" }
  }
}
```

Responses are either:

```json
{ "id": "1", "result": { "sessionId": "web:device" } }
```

or:

```json
{ "id": "1", "error": { "code": "validation_error", "message": "..." } }
```

Subscribed events are delivered as server messages containing the session ID, event ID, and event payload.

## Management APIs

Owner or admin routes:

- `/api/agents/{agentId}/info`
- `/api/agents/{agentId}/config`
- `/api/agents/{agentId}/secrets`
- `/api/agents/{agentId}/skills`
- `/api/agents/{agentId}/mcp-servers`
- `/api/agents/{agentId}/channels`
- `/api/agents/{agentId}/schedules`
- `/api/agents/{agentId}/sandboxes` — list / add / remove sandbox rows. See [sandbox-model.md](sandbox-model.md).
- `/api/agents/{agentId}/members` — list / add / remove memberships. See [user-model.md](user-model.md#adding-members).
- `/api/agents/{agentId}/security` — read / overwrite the agent security policy (`access`, autonomy, approvals).
- `/api/agents/{agentId}/instructions` — CRUD on per-agent system-prompt sections (owner or admin)

Admin fan-out:

- `POST /api/admin/instructions/fanout` — body `{ mode: 'set' | 'append' | 'remove', key, content? }`. Applies the same mutation to every registered agent's row at `key`. `set` replaces; `append` reads-then-appends a newline + content (creating the row if missing); `remove` deletes. Returns `{ ok, mode, key, agents: [...] }`.

Admin routes:

- `/api/admin/stats`
- `/api/admin/logs`
- `/api/admin/skills`
- `/api/admin/mcp-servers`
- `/api/admin/schedules`
- `/api/sandbox-presets` — read-only registry of sandbox presets defined in `gateway.json` (any authenticated user)

See the focused docs for payload details:

- [skills.md](skills.md)
- [mcp-servers.md](mcp-servers.md)
- [channel-adapter.md](channel-adapter.md)
- [session-model.md](session-model.md)