This document defines the concrete v1 CLI contract.
The contract is intentionally optimized for:
- machine execution,
- AI-agent use,
- shell scripting,
- and human debugging.
V1 public commands:
createlistinspecttypepastesend-keysresizesignalwaitsnapshotscreenshotrecord exportdestroygcdoctorversion
Every command must support --json.
Automation consumers should always pass --json.
Human-readable output is allowed, but it is not the primary contract.
Every JSON response uses the same top-level envelope fields in the shipped implementation: ok, command, timestamp, and either result or error. Session-specific identifiers currently live inside command-specific result payloads or error.details rather than as a top-level envelope sessionId field.
{
"ok": true,
"command": "inspect",
"timestamp": "2026-03-25T15:00:00.000Z",
"result": {}
}Failure envelope:
{
"ok": false,
"command": "inspect",
"timestamp": "2026-03-25T15:00:00.000Z",
"error": {
"code": "SESSION_NOT_FOUND",
"message": "Session \"missing-session\" was not found.",
"retryable": false,
"details": {
"sessionId": "missing-session",
"manifestPath": "/tmp/agent-tty/sessions/missing-session/session.json"
}
}
}Recommended exit codes:
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Generic command failure |
2 |
Usage / validation error |
3 |
Session not found |
4 |
Session not running / invalid state |
5 |
Wait timed out |
6 |
Renderer unavailable |
7 |
Artifact export failure |
8 |
Environment / dependency failure |
All public commands should support these where sensible:
| Flag | Meaning |
|---|---|
--json |
Emit JSON envelope |
--home <path> |
Override ~/.agent-tty |
--log-level <level> |
error, warn, info, debug, trace |
--timeout-ms <n> |
Command timeout |
--profile <name> |
Render profile override for render-related commands |
--no-color |
Disable colored human output |
Resolved config order:
- command-line flags,
- environment variables,
- config file,
- built-in defaults.
| Variable | Meaning |
|---|---|
AGENT_TTY_HOME |
Override home directory |
AGENT_TTY_LOG_LEVEL |
Logging default |
AGENT_TTY_PROFILE |
Default render profile |
AGENT_TTY_BROWSER_PATH |
Override Playwright browser executable |
AGENT_TTY_KEEP_TEMP |
Preserve temp replay outputs |
Use ULID-like or lexicographically sortable IDs.
Recommended prefix:
sess_<ulid>
Recommended prefixes:
snap_<ulid>shot_<ulid>rec_<ulid>vid_<ulid>
These IDs should appear in manifest entries and JSON outputs.
Create a new session host and spawn a PTY child.
agent-tty create [options] [command...]
agent-tty create [options]
agent-tty create [options] --shell /bin/zsh- Allocate a new session ID.
- Create the session directory.
- Spawn the detached session host.
- Wait until the host reports ready or startup fails.
- Return session metadata.
| Flag | Required | Meaning |
|---|---|---|
--cwd <path> |
No | Working directory |
--rows <n> |
No | Initial rows; default 40 |
--cols <n> |
No | Initial cols; default 120 |
--env KEY=VALUE |
No, repeatable | Additional environment variables |
--term <value> |
No | Terminal type; default xterm-256color |
--name <name> |
No | Human-friendly label |
--shell <path> |
No | Shell executable path; default system shell |
--idle-timeout-ms <n> |
No | Optional inactivity timeout |
The shipped CLI also keeps --command <path> as a legacy alias for --shell.
{
"sessionId": "session-01",
"createdAt": "2026-03-23T12:00:00.000Z",
"cols": 120,
"rows": 40,
"shell": "/bin/bash",
"env": {
"FOO": "bar",
"BAZ": "qux"
},
"idleTimeoutMs": 5000
}For the full merged session record, use inspect <session-id> --json. create currently returns only the minimal creation summary above.
- rows/cols must be positive integers.
--envmust reject malformed entries without=.--shelland legacy--commandmust resolve to a non-empty shell executable path.- when no positional
command...is provided,createlaunches the resolved shell path directly.
List sessions known in the home directory.
agent-tty list [--all] [--json]- Enumerate session directories.
- Reconcile stale active-session metadata where possible.
- Return running sessions by default; include terminal sessions only when
--allis set. - Preserve the current directory-enumeration order; the shipped CLI does not promise an explicit sort.
Each item currently includes:
sessionIdstatuscommandcreatedAt- optional
name pid
Return the shipped merged session summary for one session.
agent-tty inspect <session-id>
agent-tty inspect <session-id> --json- Read persisted metadata from the session manifest.
- If the host is alive, ask it for the latest in-memory session record.
- If the host is unreachable for a non-terminal session, reconcile the session directory, re-read the manifest, and mark
usedOfflineReplay: truein the result. - Count event-log entries and compute artifact health from the artifact manifest plus on-disk files.
- Derive a read-time
terminationCategoryfrom the session status, exit fields, and any persistedfailureOrigin.
The current result object includes:
session: the persistedSessionRecord, includingstatus, size, command,hostPid,childPid,exitCode,exitSignal, and optionalfailureReason/failureOrigin,eventCount: total number of persisted event-log entries,uptime: milliseconds fromcreatedAtto now for running sessions, or toupdatedAtfor terminal sessions,lastEventSeq: the last contiguous event sequence number when the event log is non-empty,terminationCategory: a derived category such asrunning,clean-exit,nonzero-exit,signal-exit,host-death,renderer-failure,destroyed, orunknown,artifacts: artifact-health summary withtotal,byKind,missingCount,health, and optionalmissingdetails,- and
usedOfflineReplay:trueonly wheninspecthad to fall back to reconciled on-disk state after a host-unreachable path.
Example inspect --json success envelope:
{
"ok": true,
"command": "inspect",
"timestamp": "2026-03-25T15:00:00.000Z",
"result": {
"session": {
"version": 1,
"sessionId": "session-01",
"createdAt": "2026-03-19T12:00:00.000Z",
"updatedAt": "2026-03-19T12:00:01.000Z",
"status": "exited",
"command": ["/bin/sh", "-lc", "echo hello"],
"cwd": "/tmp/workspace",
"cols": 80,
"rows": 24,
"hostPid": null,
"childPid": null,
"exitCode": 0,
"exitSignal": null
},
"eventCount": 2,
"uptime": 1000,
"lastEventSeq": 1,
"terminationCategory": "clean-exit",
"artifacts": {
"total": 2,
"byKind": {
"screenshot": 1,
"snapshot": 1
},
"missingCount": 0,
"health": "healthy"
},
"usedOfflineReplay": true
}
}session.failureOrigin is persisted manifest state and only appears when a failed session has a known structured origin such as host-death or renderer-failure.
result.terminationCategory is derived every time inspect runs. It is broader than failureOrigin: it also covers non-failure terminal states such as clean-exit, nonzero-exit, signal-exit, and destroyed. Automation should therefore treat failureOrigin as low-level persisted evidence and terminationCategory as the stable high-level summary.
The current inspect surface does not yet expose runtime renderer capability discovery or a richer live renderer-state block. Those remain future scope.
Write raw UTF-8 text bytes into the PTY.
agent-tty type <session-id> 'hello world'
agent-tty type <session-id> --file ./payload.txt
agent-tty type <session-id> 'hello world' --append-newline| Flag / arg | Required | Meaning |
|---|---|---|
Positional [text] |
Exactly one of [text] / --file |
Literal text to write |
--file <path> |
Exactly one of [text] / --file |
Read payload from file |
--append-newline |
No | Append \n |
- The shipped CLI takes literal text as the optional positional
[text]argument; the earlier--textexample in this doc was historical and is not a supported flag. typeis not bracketed paste.- The exact byte payload written should be represented in the event log.
- Large payloads should be supported.
Write text as a paste operation.
agent-tty paste <session-id> 'multiline\ninput'
agent-tty paste <session-id> --file ./payload.txt- The shipped CLI takes literal text as the optional positional
[text]argument or--file <path>; there is no separate--textflag. - The shipped implementation always encodes
pasteas bracketed paste sequences. - There is no
--force-bracketedflag in the shipped CLI.
Many TUIs treat paste and typing differently.
V1 should preserve that distinction in both the CLI and the event log.
Send named keys and chords.
agent-tty send-keys <session-id> Enter
agent-tty send-keys <session-id> ctrl+l g g
agent-tty send-keys <session-id> alt+shift+f10Supported forms:
EnterTabEscapeBackspaceDeleteInsertUp,Down,Left,RightHome,End,PageUp,PageDownF1...F12ctrl+<key>alt+<key>shift+<key>- combinations such as
ctrl+shift+p
- key names are case-insensitive,
- output JSON currently echoes the accepted keys as supplied,
- unsupported chords should return a structured validation error,
- the event log should record both symbolic keys and emitted byte sequences when known.
{
"accepted": ["ctrl+l", "g", "g"],
"bytesWritten": 5,
"seq": 42
}Resize the terminal.
agent-tty resize <session-id> --rows 50 --cols 140- update PTY size,
- append a
resizeevent, - notify live render workers,
- and update persisted session metadata.
rowscols
The shipped CLI does not currently expose seq, settled, or --wait-for-settle-ms; those remain future scope.
Send a process signal to the PTY child.
agent-tty signal <session-id> INT
agent-tty signal <session-id> TERM- validate signal against platform support,
- log the signal event,
- and surface a clear error on unsupported platforms.
Wait until the session reaches a condition.
V1 should support:
--text <literal>--regex <pattern>--exit--idle-ms <n>--screen-stable-ms <n>--cursor-row <n> --cursor-col <n>
agent-tty wait <session-id> --text 'Ready'
agent-tty wait <session-id> --regex 'Connected: .*'
agent-tty wait <session-id> --idle-ms 250
agent-tty wait <session-id> --screen-stable-ms 300
agent-tty wait <session-id> --exit--textand--regexoperate on the current visible screen text when a renderer is available.- before renderer initialization, the host may either:
- initialize a renderer lazily, or
- explicitly report that the selected wait mode requires a renderer.
--idle-msoperates on PTY output timing.--screen-stable-msrequires renderer state and measures no visible-screen changes.
- default timeout should be finite, e.g.
30000 ms, - timeout should surface exit code
5, - JSON output should include the last observed state summary.
The shipped CLI currently has two wait result shapes:
- legacy
--exit/--idle-mswaits return{ timedOut, exitCode? }, - render waits such as
--text,--regex,--screen-stable-ms, and cursor waits return{ matched, timedOut, matchedText?, cursorRow?, cursorCol?, capturedAtSeq }.
Example render-wait result:
{
"matched": true,
"timedOut": false,
"matchedText": "Ready",
"cursorRow": 12,
"cursorCol": 3,
"capturedAtSeq": 84
}Capture semantic terminal state.
agent-tty snapshot <session-id>
agent-tty snapshot <session-id> --format text
agent-tty snapshot <session-id> --include-scrollback
agent-tty snapshot <session-id> --include-scrollback --include-cells --jsonShipped output modes:
structured(default)text
--json controls whether the command emits the standard JSON envelope. Per-cell data is controlled by --include-cells on structured snapshots rather than by a separate cells format.
- default: visible viewport only
--include-scrollback: addscrollbackLines--include-cells: add per-cell style data incells
Every structured snapshot currently includes:
sessionId,capturedAtSeq,rows/cols,- cursor position,
isAltScreen,visibleLines,- optional
scrollbackLines, - and optional
cells.
The shipped snapshot result does not currently include renderer-backend/profile fields or a separate capture timestamp.
Capture a PNG screenshot from the selected renderer backend.
agent-tty screenshot <session-id>
agent-tty screenshot <session-id> --profile reference-light
agent-tty screenshot <session-id> --show-cursor
agent-tty screenshot <session-id> --hide-cursor- lazily initialize the renderer if needed,
- ensure the renderer has replayed through the current event sequence,
- capture a deterministic PNG,
- write artifact metadata,
- return the artifact path and dimensions.
{
"sessionId": "session-01",
"capturedAtSeq": 85,
"profileName": "reference-dark",
"cols": 120,
"rows": 40,
"artifactPath": "/home/user/.agent-tty/sessions/.../artifacts/screenshot-85-reference-dark.png",
"pngSizeBytes": 123456,
"cursorVisible": false,
"rendererBackend": "ghostty-web",
"pixelWidth": 1920,
"pixelHeight": 1280,
"sha256": "...",
"renderProfileHash": "..."
}Export a replay artifact.
asciicastwebm
agent-tty record export <session-id> --format asciicast --out ./run.cast
agent-tty record export <session-id> --format webm --out ./run.webmasciicastexport is derived from the event log.webmexport is derived from deterministic replay through the reference renderer unless a native backend explicitly supports video export later.- exports should be reproducible from the same event log + render profile.
Terminate session control.
agent-tty destroy <session-id>
agent-tty destroy <session-id> --force- default behavior asks the session host to shut down gracefully and keeps the session directory/artifacts.
--forceskips graceful shutdown and force-kills the host/child processes before reconciliation.- if the PTY child already exited,
destroystill reconciles the session todestroyed. - artifact/session-directory purge is future scope; the shipped CLI does not support
--purge.
{
"sessionId": "session-01",
"destroyed": true
}Garbage-collect old sessions and temp artifacts.
agent-tty gc --older-than 7d
agent-tty gc --stale-only- never delete running sessions,
- default to temp files and explicitly stale sessions,
--older-thanmay remove destroyed/exited sessions with artifacts only when explicitly requested.
Validate local prerequisites.
The product depends on native pieces and browser automation.
doctor should verify:
- session home directory permissions,
- socket / named-pipe viability,
- PTY spawn viability,
- Playwright browser availability,
- renderer harness startup,
- screenshot capture viability,
- bundled font load success,
- and optional
TERM/ terminfo warnings.
{
"checks": [
{ "name": "pty-spawn", "ok": true },
{ "name": "playwright-browser", "ok": true },
{ "name": "ghostty-web-render", "ok": true },
{ "name": "screenshot-export", "ok": true }
]
}The shipped version --json surface reports:
cliVersion,protocolVersion,rendererBackends,- and
runtimewithnode,platform, andarch.
Example version --json success envelope:
{
"ok": true,
"command": "version",
"timestamp": "2026-03-25T15:00:00.000Z",
"result": {
"cliVersion": "0.1.0",
"protocolVersion": "0.1.0",
"rendererBackends": ["ghostty-web"],
"runtime": {
"node": "v24.0.0",
"platform": "linux",
"arch": "x64"
}
}
}As of 2026-03-25, rendererBackends is a static compiled-in list containing only ghostty-web. Runtime capability discovery beyond that static list remains future scope and should not be inferred from the current output.
Current shipped structured error codes (src/protocol/errors.ts) are:
SESSION_NOT_FOUNDSESSION_NOT_RUNNINGSESSION_ALREADY_DESTROYEDHOST_UNREACHABLEHOST_TIMEOUTINVALID_SESSION_IDINVALID_DIMENSIONSINVALID_SIGNALINVALID_KEYSINVALID_DURATIONINVALID_INPUTSTORAGE_READ_ERRORSTORAGE_WRITE_ERRORMANIFEST_VALIDATION_ERRORRPC_ERRORPROTOCOL_ERROREXPORT_ERRORREPLAY_ERRORINTERNAL_ERROR
That shipped catalog is intentionally narrower and more implementation-shaped than some earlier design-language examples. Additional taxonomy such as explicit unsupported-platform or invalid-render-profile codes should be treated as future scope until they exist in src/protocol/errors.ts.
Human output should be concise.
Examples:
Created session sess_01JQ... (bun run dev:tui)Resize applied: 50x140Matched text \"Ready\" after 913 msScreenshot saved: ./artifacts/screen.png
But JSON remains the stable automation surface.
The CLI contract is complete when:
- every public command above is implemented,
- every command supports
--json, - failure envelopes are structured and stable,
- key grammar is canonicalized and tested,
doctorcatches missing browser/render dependencies,- and the CLI can be driven end-to-end by a non-interactive agent process.
As of 2026-03-25, the repository has closed the highest-value Week 4–6 CLI-contract gaps that were still affecting automation and review flows:
- global root flags
--home,--timeout-ms,--no-color,--log-level, and--profileare wired through shared CLI context handling, createsupports--env,--term,--name,--shell, and--idle-timeout-ms,typesupports file-backed input plus--append-newline, andpastesupports file-backed input,- renderer-backed cursor waits ship via
wait --cursor-row/--cursor-col, inspect --jsonnow exposeslastEventSeq,terminationCategory, artifact health, andusedOfflineReplay,version --jsonnow reports the compiled-in renderer backend list as['ghostty-web'],- and golden-envelope tests now lock the shipped
inspect,version, and representative error envelopes.
The remaining contract work is now narrower:
- full result-shape parity with every design example in this document is still not complete,
- runtime renderer capability discovery beyond the static
rendererBackendslist remains future scope, - and richer live renderer-state reporting in
inspectremains future scope.