Remote Sessions
A remote session is a single-homed execution-state object in the execution-state continuity layer: a persistent channel on a target machine that survives across many tool calls. Open once, then send and read repeatedly through a state machine (opening → ready → busy → closing → closed). A 1 MiB output buffer captures output so callers poll instead of stream, and an idle reaper closes quiet sessions after 30 minutes (override by disabling the idle TTL).
A remote session is a persistent shell-like channel on a target machine that survives
across many tool calls. Where cmdop connect exec is one-shot, a persistent session is
opened once, lets the agent issue many commands against it, and is torn down when idle. This
page describes the contract.
What a persistent session gives you
- Many commands, one channel. The agent can
openonce, thensendandreadrepeatedly within a chat turn — and across turns. - Output buffering. A 1 MiB rolling output buffer per session captures stdout/stderr so the agent can poll instead of streaming.
- State machine. Every session walks
opening → ready → busy → closing → closed, so the agent can reason about what is happening without races. - Idle reaper. Sessions that go quiet for 30 minutes are closed automatically.
Lifecycle
Session-manager defaults:
- Max sessions per process: 64.
- Output buffer per session: 1 MiB.
- Idle TTL: 30 minutes.
Public API
The agent-facing surface is the connect_session builtin tool with operations:
| Operation | Purpose |
|---|---|
open | Reserve a slot; returns a session ID. |
send | Issue a command on an existing session. |
read | Pull buffered output since an offset. |
close | Tear the session down explicitly. |
list | Enumerate live sessions. |
For the full tool catalogue see Tools.
The output buffer
Long-running commands easily print megabytes. The rolling output buffer caps memory at 1 MiB by dropping the oldest bytes when full. The contract:
- New output is appended; once the window is full the oldest bytes are dropped and the buffer is flagged as truncated.
- A reader asks for everything since a given offset and gets back the bytes, a cursor to resume from, and a truncated flag. A truncated answer means the requested offset fell outside the current window — the agent has missed bytes between this read and the previous one.
The agent uses truncated as a signal to back off and read in smaller windows, or to
surface a warning to the user that some output was lost.
Idle TTL and the reaper
A background reaper closes sessions that have been idle for the manager’s TTL (30 min by default). The idle TTL can be overridden per session at open time:
| Use case | Idle TTL |
|---|---|
| Quick interactive shell | leave default (30 min) |
| Long build / deploy | 0 (no auto-close) |
| Tail a slow log | 0 |
| Short-lived health probe | small (5–10 min) |
An idle TTL of 0 disables the reaper for that session — close it explicitly when done.
Set the idle TTL to 0 for builds, deploys, or any task expected to take longer than 30 minutes.
Transport-agnostic
The session manager runs the same way against a real remote machine or an in-memory test
double, so its behavior is identical in production and under test. Commands run either as a
single request (for send) or over a bidirectional stream (for interactive operations).
Truncation handling pattern
When an agent reads since an offset and gets a truncated result, the recommended pattern is:
let offset = 0
loop {
let (data, next, truncated) = read(session, offset)
if truncated {
warn("output truncated; consider increasing read frequency")
}
process(data)
offset = next
if done(data) { break }
}The agent exposes truncation in the chat as a warning so the user knows some bytes were dropped.
Example flow
// 1. open
{ "tool": "connect_session", "args": { "hostname": "prod-1", "operation": "open" } }
// → { "session_id": "prod-1:slot3" }
// 2. send
{ "tool": "connect_session", "args": {
"session_id": "prod-1:slot3",
"operation": "send",
"command": "tail -F /var/log/nginx/access.log"
}}
// 3. read (poll)
{ "tool": "connect_session", "args": {
"session_id": "prod-1:slot3",
"operation": "read",
"offset": 0
}}
// → { "data": "...", "next_cursor": 4096, "truncated": false }
// 4. close
{ "tool": "connect_session", "args": {
"session_id": "prod-1:slot3",
"operation": "close"
}}Limits and tuning
- 64 live sessions per daemon process. Board automations that fan out heavily can hit this cap — coalesce or reuse sessions where possible.
- 1 MiB buffer per session. Total output-buffer memory at the cap is ~64 MiB.
- Per-session timeouts are independent of the manager’s idle TTL.
Persistent sessions reserve memory (1 MiB output buffer each). Default cap is 64 per process; tune for board automations that fan out heavily.