Auto-send a follow-up prompt to Codex in tmux after each completed turn.
# Add an alias (optional but recommended)
alias acw='python3 /path/to/codex-auto-continue/bin/auto_continue_watchd.py'
acw start 2 # window index — opens $EDITOR for the message
acw start %6 # or pane id
acw start uvm # or tmux window nameThat's it. The watcher discovers the Codex thread for the pane and sends your
message whenever a turn completes. If a thread-id cannot be discovered, start
fails instead of running with an unknown thread.
start only accepts live tmux targets: pane id, window index, session:window,
or exact tmux window name. If you already know the Codex thread id, pass it as
the second positional argument: acw start uvm <thread-id>.
For all-watcher operations, prefer the no-target forms:
acw pause
acw resume
acw restartQuoted '*' is still accepted as an explicit synonym, but an unquoted *
will be expanded by your shell before acw sees it.
start <target> [thread-id] [--message TEXT | --message-file FILE]
stop [target]
edit <target>
pause [target|*]
resume [target|*]
restart [target|*]
cleanup [target]
status [target]
For start, <target> is a pane id (%6), window index (2), session:window (0:2), or a tmux window name (uvm).
stop with no target stops all running watchers.
pause, resume, and restart with no target act on all running watchers.
cleanup with no target removes stale watcher files; with a target it removes a
single thread-keyed session file selected by exact window name or thread-id
prefix.
When starting without --message or --message-file, your $EDITOR opens so you can write a multi-line message interactively. An empty message cancels the start.
You can also provide a message inline or via file:
acw start %0 --message "continue and focus on tests"
acw start 2 --message-file /path/to/msg.txtTo edit the message for a running watcher:
acw edit 2 # opens $EDITOR with the current messageDefault message location: ~/.codex/auto_continue.message.txt, falling back to examples/messages/default_continue_message.txt.
The watcher primarily tails ~/.codex/log/codex-tui.log for completion
signals:
- old Codex logs
post sampling token usage ... needs_follow_up=false - current Codex logs
codex_core::tasks: close
It also checks rollout JSONL files under ~/.codex/sessions/ when available so
startup can notice a pending completed turn that was already written before the
watcher attached.
When a watched thread completes a turn, the watcher sends the continue message to the tmux pane.
The watch daemon discovers which Codex thread belongs to each pane by inspecting
the pane's live process tree, Codex's local state DB, and thread-keyed session
state. This works for both codex resume ... panes and plain codex --full-auto panes running on different tmux windows at the same time.
Watcher processes also record the tmux socket they were started against. That keeps watcher discovery scoped to the current tmux server and avoids pane-id collisions between your live server and the isolated real-Codex test harness.
Automatic thread discovery is pane-local only. If acw cannot prove which
thread belongs to the target pane yet, it waits instead of guessing from the
most recent global Codex log or rollout activity.
Pid-based thread discovery is also bounded to the current Codex process
lifetime, so acw will not reuse stale thread ids from an older process that
happened to share the same Linux pid later.
Canonical session state is stored as ~/.codex/acw_session.<thread-id>.json.
If the Codex session restarts with a new thread, the watcher re-discovers it during periodic health checks.
Window names are synchronized via a tmux window-renamed hook (no periodic
rename polling).
acw status resolves thread→pane mappings dynamically so the WINDOW and PANE
columns reflect the current tmux layout.
If tmux is still running but its socket path disappears, acw now prints a
recovery hint instead of only reporting a generic tmux failure. In that case,
recreate the socket with the suggested command, for example:
kill -USR1 1996933Each watcher tracks completion-source health, with codex-tui.log as the
primary source and rollout JSONL as a supplemental source when present:
- ok — the watcher has a usable completion source
- stale — a rollout file exists but has stopped updating before Codex log completion has been proven
- warn — no rollout file has been found yet, or rollout recording closed and the watcher is continuing via
codex-tui.log - error — rollout recording closed before the watcher observed a matching completion in
codex-tui.log
View health with acw status.
$ acw status
Sessions: 3
WINDOW STATE STARTED LAST_MSG LAST_ACW MESSAGE
----------- -------- ---------- ---------- ---------- -------
0:1:api running 2d14h ago 0s ago 4m ago msg:please continue working on the API...
0:2:tests running 1d08h ago 12s ago 8m ago msg:continue writing tests for the auth...
0:3:refac paused 3d02h ago 1h22m ago 1h22m ago msg:keep refactoring the database layer...
The columns show:
- WINDOW —
session:index:name(uses live tmux state, survives window reordering) - STARTED — when the Codex thread was created
- LAST_MSG — last activity in the Codex session (rollout file mtime)
- LAST_ACW — when the watcher last sent a continue prompt
$ acw start tests
# $EDITOR opens with an empty buffer — write your multi-line continue message.
# Save and quit to start the watcher. Empty message = cancel.
resolved: target=tests pane=%2
started: pid=48305 pane=%2 thread_id=01a2b3c6-d5e6-7f80-9a1b-2c3d4e5f6a7b
$ acw start api --message "continue and focus on tests"
resolved: target=api pane=%1
started: pid=49122 pane=%1 thread_id=01a2b3ca-d5e6-7f80-9a1b-2c3d4e5f6a7b
$ acw pause tests
paused: pane=%2 pid=48305
$ acw resume tests
resumed: pane=%2 pid=48305
$ acw pause
paused: pane=%1 pid=48201
paused: pane=%2 pid=48305
Watchers also auto-pause when the pane shows a Codex-level interruption or
account error banner, such as Conversation interrupted, Model interrupted to submit steer instructions, auth failures, or quota/usage-limit errors. Resume
them explicitly with acw resume <target> after you have handled the issue in
the pane.
If a watcher process is gone, acw status now reports it as dead even if the
last persisted health snapshot was ok.
$ acw edit tests
# $EDITOR opens pre-filled with the current message.
# Save and quit to update. The watcher restarts with the new message.
resolved: target=tests pane=%2
stopped: pane=%2 pid=48305
started: pid=49501 pane=%2 thread_id=01a2b3c6-d5e6-7f80-9a1b-2c3d4e5f6a7b
python3,tmux,pstree- Codex CLI
- Linux (uses
/procfor thread discovery)
Run bash test/test_rollout_e2e.sh to execute the real-Codex integration suite.
The shell script is a thin wrapper around a shared Python harness and currently
runs seven real-Codex tests:
- a Codex contract test that proves which completion signals the current Codex build emits
- a watcher integration test that verifies
auto_continue_logwatch.pysends the continue prompt - a watcher regression test that ensures
rollout channel closedis not reported as a hard error whencodex-tui.logis still driving completion - a watcher regression test that sends
Escapein the isolated tmux pane and verifies the watcher auto-pauses on the real interrupt banner - a manager integration test that starts a watcher against a plain
codex --full-autopane - a manager integration test that reports
deadafter a real watcher process exits - a manager integration test that recreates a private tmux socket with
kill -USR1and then starts successfully
The harness always uses a dedicated tmux server on its own socket, so it does
not interfere with your existing tmux sessions. It also always uses an isolated
test home seeded from your existing Codex auth/config files, so tmux state,
watcher state, and Codex session artifacts stay out of your live ~/.codex.
If a real-Codex test fails, the harness archives pane capture, watcher logs,
Codex log tail, and state files under ~/.codex/auto-continue-e2e-tmp/failures/
for postmortem debugging.