Skip to content

feat(session): raw-PTY terminal attach (connect --raw, start --connect)#35

Merged
hbrooks merged 1 commit into
mainfrom
interactive-connect
Jul 11, 2026
Merged

feat(session): raw-PTY terminal attach (connect --raw, start --connect)#35
hbrooks merged 1 commit into
mainfrom
interactive-connect

Conversation

@hbrooks

@hbrooks hbrooks commented Jul 11, 2026

Copy link
Copy Markdown
Member

What

The pixel-perfect terminal attach from the interactive-sessions handoff. Bridges the sandbox's ttyd terminal (over the agent's live tmux) to your local terminal, so the agent's Claude Code TUI runs in your shell — the SSH-like connect experience.

Backend companion: ellipsis-dev/ellipsis#5775 (WS /v1/sessions/{id}/terminal). Merge that first.

Security shape (pure /v1 Bearer client)

The CLI opens wss://api.ellipsis.dev/v1/sessions/{id}/terminal with its ordinary login token on the handshake — the same credential the REST API uses. The API authenticates it, mints the Modal connect token server-side, and relays ttyd byte-for-byte. The CLI never parses a code out of a URL, calls /__auth, holds a cookie, or sees a Modal token or sandbox origin. (This replaces the handoff doc's original browser-impersonation sketch, which was both factually wrong about the terminal endpoint and below the bar for a public repo — see #5775.)

Verb shape — please confirm

I kept the existing message-based connect as the default (single-writer-safe, works headless and from inside a sandbox) and added the raw PTY as a mode flag:

  • agent session connect --raw [sessionId] — raw attach (needs a real TTY).
  • agent session start --connect — start, wait for the sandbox to come live, then raw-attach. Not combinable with --json/--watch.

The handoff noted you leaned toward "connect = the pixel-perfect attach". I went with --raw (lowest-regret: no regression to the in-sandbox/headless message mode, fits the flags-not-sub-verbs grammar) and it's a one-line flip to make raw the default if you'd rather. Your call before merge.

Other decisions to confirm:

  • Detach key = Ctrl-] (0x1d) (telnet-style; Ctrl-C passes through to the agent).
  • Writable attach (drives the agent): reintroduces the multi-writer race vs the worker's inbox send-keys until the write-baton ships — accepted pre-GA per INTERACTIVE_SESSIONS.md §6.

Changes

  • lib/terminal.ts: ttyd wire-protocol framing (init/input/resize + server-frame parse), close-code → guidance, and the raw stdin<->ws<->stdout bridge (SIGWINCH resize, Ctrl-C passthrough, Ctrl-] detach, TTY restored on every exit path).
  • commands/connect.ts: --raw flag + shared runConnectRaw.
  • commands/session.tsx: start --connect (wait-until-live then attach).
  • No new deps (ws only) and no package.json change.

Gates

  • typecheck (strict tsc), full vitest suite (187 passed, incl. 14 new pure-helper tests in test/terminal.test.ts), tsup build, and the --compile binary smoke — all green.
  • Live end-to-end verification against a warm interactive session is still to do (needs a running sandbox); the pure protocol/URL/close-code helpers are unit-covered.

The pixel-perfect `agent session connect` from the interactive-sessions
handoff: bridge the sandbox's ttyd terminal to the local terminal so the
agent's live Claude Code TUI runs in your shell.

Pure /v1 client: opens WS /v1/sessions/{id}/terminal with the ordinary Bearer
login token; the API authenticates, mints the Modal token server-side, and
relays ttyd byte-for-byte (backend PR ellipsis-dev/ellipsis#5775). The CLI
never sees a sandbox URL, a Modal token, or a cookie.

- lib/terminal.ts: ttyd wire-protocol framing (init/input/resize + server-frame
  parse), close-code guidance, and the raw stdin<->ws<->stdout bridge (SIGWINCH
  resize, Ctrl-C passes through, Ctrl-] detaches, TTY restored on exit).
- session connect --raw: the raw attach, beside the existing message-based
  connect (which stays the default: single-writer-safe, works headless / in a
  sandbox). --raw needs a real TTY.
- session start --connect: wait for the sandbox to come live, then attach; not
  combinable with --json/--watch.

14 new pure-helper tests (test/terminal.test.ts); typecheck + full suite (187)
+ build + compile smoke all green. Live end-to-end verification against a warm
interactive session still to do (needs a running sandbox).
@hbrooks hbrooks merged commit d37e60d into main Jul 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant