Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions docs/user/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,27 @@ If the child is resident, this attaches to that process. After a gateway restart

The remote TUI supports normal prompting, model/config selection advertised by the host, steering, and explicit interruption. `Esc` or `Ctrl+C` during a running turn interrupts it. Quitting with `Ctrl+D` on an empty prompt, or losing the connection, detaches without interruption. One attachment controls a session at a time. Clean disconnection releases the attachment. An explicit resume replaces the previous controller, so a lost HTTP stream does not prevent reconnection. The old controller cannot submit new work once replaced.

### Automatic recovery and uncertain requests

After a transient transport failure, the bridge keeps the terminal open and makes a bounded series of fresh ACP connections. The TUI shows **Reconnecting** while it initializes and resumes the same confirmed session ID. It does not assume reopening SSE recovers missed events: full session replay builds a private replacement view, followed by the authoritative current configuration and turn state. Only a successful coherent attachment replaces the visible transcript; a failed attempt leaves the previous view intact. Replay replaces rather than appends history, and stale attachment generations cannot update the recovered view.

Recovery does **not** retry prompts, session creation, configuration changes, steering, or cancellation. A submitted operation can have an **unknown outcome** after a lost response. Inspect the recovered transcript and state before deciding what to send next; retained composer text is not evidence that its earlier submission was rejected. New submissions during recovery are rejected rather than saved for automatic execution. If creation may have succeeded but no session ID was received, use `kit gateway list` and select `--remote-session` explicitly; the bridge never creates another session to guess the outcome.

Authentication, protocol, project, and replaced-controller errors stop automatic recovery. Automatic recovery is conditional on the previous attachment generation and cannot repeatedly steal control from a newer explicit attachment. Repeated transient failures exhaust a finite attempt/time budget and leave a visible disconnected state with recovery guidance.

### Recovering without historical replay

A full history that exceeds replay or transport limits is not silently truncated, and automatic recovery never silently switches to no-replay. To explicitly regain an existing session without its historical transcript, restart with:

```sh
kit tui --remote http://127.0.0.1:7766 \
--remote-credential-file "$HOME/.kit-gateway-token" \
--root /absolute/server/project --remote-session SESSION_ID \
--remote-no-replay
```

This option requires an existing session ID. The TUI marks **History unavailable**; an empty historical pane is not a claim that the session has no history. The attachment still requires the authoritative current configuration and active/idle state before enabling control. The durable transcript stays on the host, and in-flight work is not restarted. Direct ACP clients can request the same no-replay behavior by omitting `replayFrom` from `session/resume`; full replay uses `{"type":"start"}`.

To start another session, exit and omit `--remote-session` on the next invocation. To switch sessions, exit, list them, and supply the desired ID. In-place `/new`, session listing/resume/rename commands, local provider login/usage, and voice are disabled in a remote attachment. This milestone does not provide remote session deletion or a close command. Unsupported ACP close requests fail explicitly; they are not reported as successful no-ops.

## Standard ACP HTTP clients
Expand Down Expand Up @@ -106,7 +127,7 @@ Unsupported requests fail explicitly. Client-to-agent request cancellation (`$/c

### Pinned wire fixture

`tests/gateway.rs::pinned_sdk_http_initialize_capabilities_and_session_stream_contract` is an executable HTTP fixture for the SDK pinned at `2f039993d1d6ed8da35b38c31f54a7cbb7338c70`. It validates these payloads and headers against a real gateway without any control-plane service.
`tests/gateway.rs::pinned_sdk_http_initialize_capabilities_and_session_stream_contract` is an executable HTTP fixture for the SDK pinned at `423ba77cd555a09f68472b93c142c8f0baaabf43`. It validates these payloads and headers against a real gateway without any control-plane service.

Initialize POST body (send `Authorization: Bearer TOKEN` and `Content-Type: application/json`):

Expand All @@ -123,7 +144,7 @@ The response is HTTP 200 JSON with `Acp-Connection-Id`. Its `result` contains `p
Its `result._meta` explicitly identifies the experimental limits:

```json
{"kit/gateway":{"experimental":true,"transport":"bounded-http","maxFrameBytes":1048576,"coreBufferedBytesPerDirection":16777216,"httpEgressBytesPerConnection":4194304,"liveReplayLimitBytes":8388608,"liveReplayLimitEvents":4094}}
{"kit/gateway":{"experimental":true,"transport":"bounded-http","maxFrameBytes":1048576,"coreBufferedBytesPerDirection":16777216,"httpEgressBytesPerConnection":4194304,"liveReplayLimitBytes":8388608,"liveReplayLimitEvents":4093}}
```

Both connection and session SSE GETs return HTTP 200 with `Content-Type: text/event-stream`. The session stream echoes both ACP ID headers. Subsequent POST and transport DELETE return HTTP 202. SSE `data:` contains JSON-RPC replies or notifications, for example:
Expand All @@ -142,10 +163,10 @@ The error above illustrates a session-routed revoke of an unknown pending messag
- Client redirects are disabled so the bearer token cannot follow a redirect. Use a trusted URL and transport; HTTPS can be provided by a separately secured proxy, not by this command itself. The bundled client uses bounded HTTP/SSE parsing and charged core mailboxes. Transport limits do not make an untrusted gateway safe: it still supplies protocol data and can terminate the connection.
- A failed send can have an **unknown outcome**. There is no automatic prompt retry. Reconnect and inspect the transcript before resubmitting, or you may duplicate work. A lost create response can leave a resident session; list sessions before creating another.
- Live reattachment replays the resident child's notifications and current configuration, not old request responses. Replies are scoped to the attachment that submitted the request, so a stale response cannot resolve a new terminal's request ID. ACP v2 state-update notifications carry active/idle state across reconnects.
- Replay is an in-memory convenience, not a second durable session format. It is limited to 8 MiB and approximately 4,096 events per session/attachment. Overflow expires an attachment or rejects a full live reattach rather than silently claiming complete replay. For large histories, reconnect with `session/resume` and omit `replayFrom` (or set it to null) to regain control without replay. The bundled TUI requests full replay and can therefore fail to reconnect beyond these limits; a no-replay ACP client is required in that case. A no-replay resume does not currently provide an authoritative active/idle snapshot; durable assistant content alone does not prove the turn is idle. Do not automatically submit another prompt based on either. Restarting the gateway does not guarantee oversized full replay will fit, and burst replay can exhaust transport budgets even below the history limit.
- Replay is an in-memory convenience, not a second durable session format. It is limited to 8 MiB and approximately 4,096 events per session/attachment, including room for a current snapshot. Overflow rejects a full live reattach rather than falsely claiming complete replay. Use explicit `--remote-no-replay` only when historical omission is acceptable. Current configuration and actual ACP turn state follow historical events before resume completes; durable assistant text alone is never used as evidence that a turn is idle. Restarting the gateway does not guarantee oversized full replay will fit, and burst replay can exhaust transport budgets even below the history limit.
- Healthy HTTP connections have no cumulative output cap. The SDK instead limits each core direction to 16 MiB / 256 frames and each connection's HTTP egress to 4 MiB / 64 frames. These are independent encoded-data reservations, not an aggregate heap limit. Charged envelopes remain owned through local decoding and serialization. Admission saturation fails explicitly and can terminate the connection; it does not silently drop output while claiming the stream is complete. Accepted resident work is not cancelled by transport failure. There is no delivery acknowledgement, and HTTP body handoff does not prove peer consumption. The bridge's stdin mailbox holds at most 16 capped frames; each frame is limited to 1 MiB before parsing.
- The server admits at most 64 connections, 32 concurrent SDK POSTs, and 8 MiB of aggregate body reservations. Each connection permits at most 128 batch entries, 256 pending routes, 64 session registrations, and 65 streams. HTTP/core frames are limited to 1 MiB; SSE encoding and the client's 1 MiB parser limits can reject a near-limit frame. The bundled client independently caps HTTP reservations at 64 MiB / 128 slots, pending RPCs at 128, each POST lane at 32, and SSE streams at 8. Transport exhaustion is terminal rather than an unbounded wait. The gateway's outer admission layer admits at most 32 concurrent POST/DELETE operations before buffering or waiting on per-connection teardown.
- These bounds exclude allocator overhead, transient JSON conversion, arbitrary application state, and HTTP/TLS/socket buffers. They are not a hard process-memory ceiling or a denial-of-service-hardening claim. The bounded transport supports HTTP/SSE, not WebSocket upgrades. SSE has no replay cursor: reopening a stream alone cannot recover lost events. Automatic reconnect and authoritative TUI transcript replacement are not implemented. There is no idle connection expiration or body-read deadline: disconnected peers that do not DELETE or otherwise terminate their transport can retain connection slots. Slot exhaustion may require restarting the gateway; do not expose it to untrusted clients.
- These bounds exclude allocator overhead, transient JSON conversion, arbitrary application state, and HTTP/TLS/socket buffers. They are not a hard process-memory ceiling or a denial-of-service-hardening claim. The bounded transport supports HTTP/SSE, not WebSocket upgrades. SSE has no replay cursor: reopening a stream alone cannot recover lost events. Recovery uses fresh initialization and session-level resume, not SSE event IDs. There is no idle connection expiration or body-read deadline: disconnected peers that do not DELETE or otherwise terminate their transport can retain connection slots. Slot exhaustion may require restarting the gateway; do not expose it to untrusted clients.
- At most 64 active or stopping actors occupy session slots. Listing or creating sessions reclaims completed actor slots without restarting the gateway or deleting durable transcripts. Detached actors, including idle actors, are not automatically terminated; accepted work continues. HTTP POST bodies are limited to 1 MiB inside SDK admission, including streamed bodies; supervisor command queues and pending child requests are also bounded. Child stdout frames are limited to 8 MiB before JSON parsing; oversized or malformed frames stop that child.
- Graceful `Ctrl+C` shutdown stops serving, releases resident actor ownership, closes each ACP child’s input, drains its output, and waits for cleanup and exit. A 10-second timeout falls back to killing and reaping the child. That fallback can leave a stale transcript lock requiring operator intervention. Hard process termination has operating-system-dependent cleanup behavior; arbitrary tool descendants are not a managed process group.
- Client-side ACP services, arbitrary extra directories, remote MCP injection, browser callbacks, and arbitrary ACP methods are not supported. Files, tool execution, and provider authentication belong to the gateway host.
Expand Down
Loading
Loading