A persistent TTY for your AI agent. Shared live terminal for humans. bash_tool is curl. k is a socket.
pip install agent-tty
Requires POSIX, Python 3.10+, and tmux 3.0+. Linux, macOS, and WSL are supported; native Windows fails fast.
v0.1.5 · MIT
bash
# agent creates a persistent session$ k new work bashOK work# agent runs code, gets JSON$ k run -j work "echo hello"{"cell_id": "a1b2c3d4e5f6", "status": "done", "output": "hello"}# agent writes complex code to a file, loads it through k$ cat > /tmp/task.sh << 'EOF'
cd /tmp
export MODE=warm
printf 'mode=%s pwd=%s\n' "$MODE" "$PWD"
EOF$ k run -j work "source /tmp/task.sh"{"cell_id": "d4e5f6a7b8c9", "status": "done", "output": "mode=warm pwd=/tmp"}# cwd/env persisted because source ran in the live session$ k run -j work 'pwd; echo $MODE'{"cell_id": "e7f8a9b0c1d2", "status": "done", "output": "/tmp\nwarm"}
Shared live TTY
The agent works in a real tmux session — not hidden inside a background task. You watch it live, interrupt with k int, or take over with tmux attach.
agent view
# agent fires a long cell via its shell tool$ k fire work "cd app && pytest -q && tail -20 logs/server.log && export FIX_READY=1"{"cell_id": "a1b2c3d4e5f6", "status": "fired"}$ km work a1b2c3d4e5f6 -1{"cell_id": "a1b2c3d4e5f6", "session": "work", "status": "done", "ts": "..."}
human view
# you watch: filtered live view of what the agent is doing$ k watch workwatching work (ctrl-c to stop)
── a1b2c3d4 ──
$ cd app
$ pytest -q
..F
test_api.py::test_health failed
$ tail -20 logs/server.log
missing DATABASE_URL
$ export FIX_READY=1
── ✓ ──# you interrupt the agent; tmux attach for native raw takeover$ k int workOK$ tmux attach -t work
Why give your agent a TTY
👁️
You can watch
You see everything the agent does. k watch for filtered live view, tmux attach for raw TTY takeover.
🧠
Agent state survives
Variables, cwd, imports, live connections, SSH sessions, and debugger state stay alive across agent turns.
🧵
Zero escaping
Write code to a file, load with source/exec. The heredoc is literal, the REPL command is one line, and live session state persists. No quoting layers to fight.
🔔
Callback completion
km wakes the agent when a long cell finishes. No poll loop when the runtime can monitor stdout.
🔄
JSON cell API
run -j for inline cells, fire/poll for long cells. Structured JSON with cell_id, status, and output.
🎯
Line-oriented REPLs
bash, python3 -i, sqlite3, gdb, redis-cli, ssh — anything with a stable line prompt. Three frame detection modes adapt to each.
🛡️
Safe recovery
One cell at a time. Timeout recovery hints, k status next actions, and k int Ctrl-C — you or the agent can always recover.
Agent workflow
k new work bash
+-- agent creates one persistent TTY
+-- cwd, env, imports, sockets, ssh, debuggers stay alive
k run -j work "echo hello"
+-- short cell
+-- returns JSON directly
k fire work "pytest -q && tail -20 logs/server.log"
+-- long cell
+-- returns a cell_id immediately
km work <cell_id> -1
+-- wait once
+-- wake the agent on completion
k status work
+-- repair the log pipe if needed
+-- print the next useful command
k watch work you watch, filtered live view
tmux attach -t work you take over, raw terminal
k int work you interrupt the agent
km — callback monitor
Wakes your agent when a long cell finishes. No polling, no sleep loops.
Each stdout line is a JSON event. Any host with background-notification support can consume them directly.
agent orchestration
# poll is a fallback when the agent runtime cannot monitor stdout$ k poll work# → "running"... call again... "running"... again...# with km: agent fires a long cell, gets woken on completion$ k fire work "make build"{"cell_id": "a1b2c3d4e5f6", "status": "fired"}$ km work a1b2c3d4e5f6 -1{"cell_id": "a1b2c3d4e5f6", "session": "work", "status": "done", "ts": "..."}# continuous mode: watch all events across a session$ km work{"status": "fired", ...}{"status": "done", ...}{"status": "notify", "message": "deploy complete", ...}{"status": "fired", ...}{"status": "done", ...}