Skip to content

Phase 14: Embedded terminals and agent sessions

Implemented

  • Added a provider-neutral terminal domain for typed shell, Compose service, database-client, coding-agent, and reviewed interactive-action sessions.
  • Added a daemon-owned Unix PTY adapter with explicit working directory, argument-array commands, terminal environment, resize, and process-group termination. Phase 18 subsequently added the native ConPTY adapter.
  • Added owner-scoped application lifecycle, bounded 1 MiB reconnect scrollback, non-blocking subscribers, a 30-minute detached idle deadline, explicit termination, daemon-shutdown interruption, and restart recovery.
  • Added SQLite schema version 11 for metadata-only session and audit records. Terminal bytes, input, commands, arguments, and environment values are not persisted.
  • Added generated REST and TypeScript contracts for terminal and agent session create/list/get/terminate operations.
  • Added authenticated same-origin terminal and agent WebSockets. Binary frames carry PTY bytes; bounded JSON frames carry resize control; unknown controls, oversized input, owner mismatch, and slow consumers are rejected.
  • Added typed resolution through accepted catalog, action, environment, and Compose facts. Custom commands require an accepted interactive action; privilege-escalating shell text and path-escaping Compose inputs are denied.
  • Added xterm.js with Fit and WebLinks addons, screen-reader mode, debounced resize, bounded presentation scrollback, modifier-gated HTTP(S) links, full session-state UI, reconnect controls, and explicit process termination.
  • Added project Terminal and Agents surfaces. Agent metadata includes provider, project, worktree, cwd, status, output byte count, and literal user_visible_terminal_output_only capture semantics.
  • Preserved trusted external-terminal handoff beside the embedded terminal. MCP deliberately retains no generic terminal, shell, SQL, or Docker tool.

Architecture decisions

No ADR changed. The implementation applies ADR-0004’s REST/WebSocket split, ADR-0005’s domain-owned SQLite boundary, ADR-0007’s process ownership, ADR-0010 and ADR-0011’s provider-neutral least-privilege agent integration, ADR-0013’s local transport security, and ADR-0015’s Unix-first platform order.

The persistence policy is an implementation choice within those accepted decisions: a browser disconnect detaches, the daemon keeps the PTY for a bounded idle window, and restart interrupts rather than fabricating resume.

Safety properties

  • Public APIs cannot submit a raw command. The resolver emits an argument array only after accepted project trust and selected-worktree validation.
  • Session ownership is checked below the transport for list, get, attach, and terminate. Browser authentication and exact same-origin validation happen before the WebSocket reaches that boundary.
  • A slow subscriber is detached without blocking PTY output. Client input, terminal dimensions, subscriber queues, reconnect output, and browser scrollback all have explicit bounds.
  • Audits contain lifecycle events, principals, dimensions, byte counts, and process identity only. They contain no terminal input/output or environment.
  • Terminal links accept only HTTP(S), require a modifier key, and use noopener,noreferrer.

Tests added

  • Typed launch validation, owner identity validation, start failure, request cancellation, detach persistence, reconnect scrollback, explicit termination, idle expiry, restart interruption, resize/input forwarding, and slow-subscriber fanout load.
  • Compose declaration/path resolution, custom-action risk, privilege escalation, working-directory containment, argument arrays, and environment forwarding.
  • Real Unix PTY Unicode, ANSI color, alternate-screen sequences, resize, exit, and process-group termination.
  • SQLite metadata/audit round trips, missing-session mapping, restart recovery, schema version 11, and absence of sensitive payload columns.
  • REST translation and filtering, browser cookie/origin protection on both PTY paths, WebSocket ready/snapshot/input/resize/output/exit protocol, and invalid control rejection.
  • Vue typed launcher and agent disclosure unit tests, live browser shell and reconnect E2E coverage, and terminal visual regression.

Acceptance criteria status

  • A real interactive shell opens at the trusted project or selected worktree directory.
  • Resize, Unicode, ANSI color, and alternate-screen/full-screen control sequences work through a real Unix PTY.
  • Browser close behavior is documented and implemented as bounded detach with reconnect, explicit termination, and idle expiry.
  • Missing/invalid browser sessions, cross-origin requests, and owner mismatches cannot attach to PTYs.
  • Agent records use a literal user-visible-output capture policy and never claim hidden-reasoning access.

Verification evidence

Verified on 2026-07-16 with make quality:

  • generated OpenAPI, TypeScript, JSON Schema, and SQL code had no drift;
  • gofmt, go vet, golangci-lint, architecture checks, ESLint, and TypeScript checks passed;
  • all Go tests, 20 Vue unit tests, the Go race suite, and a clean schema-11 migration passed;
  • govulncheck ./... reported no vulnerabilities;
  • four Chromium E2E scenarios passed against the live daemon, including a real sh PTY in the mixed-project fixture, UTF-8 output, browser detach and reconnect, explicit termination, native-process lifecycle, and Compose lifecycle;
  • nine Chromium visual regressions passed, including the typed terminal launcher and persistence disclosure; and
  • the production Vue bundle and trimmed Go binary built successfully.

The Unix adapter integration suite separately exercised ANSI color, alternate-screen sequences, resize propagation, Unicode, normal exit, and process-group termination through a real PTY.

Known limitations and deferred work

  • Unix PTYs are production-capable on macOS/Linux. Native Windows ConPTY is now supplied by Phase 18 rather than a silent pipe fallback.
  • Rich provider SDK orchestration is deferred by the scope guard. Codex and Claude Code run through their reliable user-visible PTY interfaces.
  • PTY scrollback is intentionally not durable across daemon restarts.

2026-07-21 terminal glyph evidence

  • The PTY continues to launch the user’s selected/default login shell, so zsh, Oh My Zsh, and Powerlevel10k startup behavior remains user-owned.
  • The xterm renderer now prefers the Powerlevel10k-recommended MesloLGS NF family and retains the existing portable monospace fallbacks. Systems without that installed family continue through the CSS font stack without assuming Powerlevel10k.
  • Vue unit coverage fixes the glyph-capable font order, and the real-daemon E2E terminal flow plus all 14 visual regression scenarios passed.
Use this pageLast verified Jul 21, 2026
Copy-ready MarkdownOpen sourceReport a docs issue