Skip to main content

Persistent terminal sessions for AI agents

Project description

agent-tty

Persistent terminal sessions for AI agents. Drives tmux, returns JSON.

The package is agent-tty. The CLI command is k, intentionally short to minimise token overhead in agent tool calls. km is the companion event monitor.

Requires tmux 3.0+ — k drives tmux for PTY multiplexing; it does not bundle or replace it.

Quick Start

k new work bash
k run -j work "echo hello"
# {"cell_id":"...","status":"done","output":"hello"}

k new py python3 -i                         # Python 3.12 and below
k new py "env PYTHON_BASIC_REPL=1 python3 -i"  # Python 3.13+ (disables _pyrepl auto-indent)
k run -j py "print(42)"

Install

Requires: Python 3.8+, tmux 3.0+

pip install agent-tty            # → k, km, agent-tty in PATH

Or without pip:

git clone <repo> && cd agent-tty
./scripts/k --help               # works immediately (dev shim)

Or symlink into PATH:

ln -sf "$(pwd)/scripts/k"  /usr/local/bin/k
ln -sf "$(pwd)/scripts/km" /usr/local/bin/km

Commands

k new    <session> [cmd...] [--prompt="x"]     spawn (default: bash)
k new    <session> <cmd> --prompt=./hook        hook mode
k fire   [-t N] [session] <code>               async fire (default 300s)
k poll   [session] [cell_id]                   poll (O(1))
k run    [-j] [-t N] [session] <code>          sync (default 30s)
k await  ...                                   alias for run
k notify [session] <message>                   notification
k int    [session]                             ctrl-c
k kill   <session>                             kill + cleanup
k ls                                           list sessions
k status [session]                             health check
k watch  [session]                             live filtered view
k history [-n N] [session]                     last N×5 lines (default 5)

Frame Detection

Three modes via --prompt:

--prompt= mode how
(not set) repeat 5 empty Enters → 5 identical lines → done
"(gdb)" exact match prompt string
./hook.py hook stdin lines → hook exit → done

Hook protocol: k feeds ANSI-stripped lines to stdin. Hook exits = frame end. Hook paths must include a path separator (/, or \ on Windows). Path is canonicalised to absolute at k new time; hook must exist and be executable.

How It Works

k fire "echo hello"
  |
  +-- acquires lock (rejected fire = zero side effects)
  +-- sends code via paste-buffer (atomic)
  +-- sends 5 frame Enters (repeat mode only)
  +-- starts background stream processor
  |
  stream processor tails log:
    ECHOING: skip echo_count lines
    OUTPUT:  collect lines
    DONE:    5 identical lines / prompt match / hook exit
  |
  writes result file -> exits
  |
k poll
  +-- checks result file (O(1))
  +-- returns JSON

Safety

invariant mechanism
one cell per session O_EXCL lock, acquired before send
timeout keeps lock lock marked timed_out; subsequent polls say use k int or k kill
orphan recovery bg PID in lock, poll checks os.kill(pid, 0) (POSIX)
no line-wrap skew tmux width 10000
atomic send per-session named paste-buffer k_{session}
ctrl-c safe kills watcher, writes {"status": "error", "output": "interrupted"}, re-sends frame enters (repeat only)
session name validation [A-Za-z0-9_.-]+, no .., no path traversal
idempotent pipe restart pipe-pane replaced on every fire/run
atomic result writes tmp + fsync +os.replace — poll never reads partial JSON
no output classification "done" = prompt appeared, not success

JSON Schema (k)

fired:        {"cell_id": "...", "status": "fired"}
running:      {"cell_id": "...", "status": "running"}
done:         {"cell_id": "...", "status": "done", "output": "..."}
timeout:      {"cell_id": "...", "status": "timeout", "output": ""}
timeout(2+):  {"cell_id": "...", "status": "timeout", "output": "use k int or k kill"}
error:        {"status": "error", "output": "..."}
cell error:   {"cell_id": "...", "status": "error", "output": "..."}

Errors without cell_id: no session 'x', active cell 'x', pipe failed: ..., send failed: ..., no active cell on 'x'. Errors with cell_id: interrupted, unknown cell, watcher died, lock update failed; use k int or k kill, interrupt failed; use k kill.

km — event monitor

km <session> [cell_id] [-1]

Watches a session via pipe-pane. Each stdout line is one JSON event. -1 exits after first completion (one-shot .then()).

fired:   {"cell_id": "...", "session": "...", "status": "fired",  "ts": "..."}
done:    {"cell_id": "...", "session": "...", "status": "done",   "ts": "..."}
notify:  {"session": "...", "status": "notify", "from": "...", "message": "...", "ts": "..."}
closed:  {"session": "...", "status": "closed", "ts": "..."}
error:   {"session": "...", "status": "error",  "message": "...", "ts": "..."}

Testing

python tests/test_contracts.py      # static code contracts, no tmux
python tests/test_docs.py           # README/SKILL drift, no tmux
bash tests/test.sh                  # 34 tests (32 without gdb), runtime smoke suite
python tests/test_regressions.py    # targeted audit regressions
python tests/run_all.py             # all suites

Files

src/agent_tty/cli.py       k — main script
src/agent_tty/monitor.py   km — event monitor
scripts/k, scripts/km      dev shims (no pip install needed)
pyproject.toml             pip install agent-tty → agent-tty, k, km in PATH
tests/test.sh              runtime smoke suite
tests/*.py                 static, docs, and regression suites
SKILL.md                   agent reference
EXAMPLES.md                patterns + philosophy

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_tty-0.1.0.tar.gz (33.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_tty-0.1.0-py3-none-any.whl (15.9 kB view details)

Uploaded Python 3

File details

Details for the file agent_tty-0.1.0.tar.gz.

File metadata

  • Download URL: agent_tty-0.1.0.tar.gz
  • Upload date:
  • Size: 33.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_tty-0.1.0.tar.gz
Algorithm Hash digest
SHA256 45079c9b36f90c4c51e6751c50dbd8b1523378ca815b272c3f95ca88ea014597
MD5 5ef046ebe189afd2cefc36abcc97085b
BLAKE2b-256 7984af7cfc80cc20ee89beef4e8eb34379295c38355725a720e0b5117d902111

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_tty-0.1.0.tar.gz:

Publisher: release.yml on rangersui/agent-tty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agent_tty-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: agent_tty-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for agent_tty-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f476afaadb4abe1389de32a160782808d0770783d74d684f3b36b14d627803af
MD5 2705bc80716ed0daaee0e21c5d75b48a
BLAKE2b-256 62edba6e081dad05375652e2d5f4dc250ea54acb2a18ad19f86f8bb250d5c00a

See more details on using hashes here.

Provenance

The following attestation bundles were made for agent_tty-0.1.0-py3-none-any.whl:

Publisher: release.yml on rangersui/agent-tty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page