All CAO agent sessions run in tmux. You can attach directly to a session to watch or interact with agents in real time.
# List all sessions
tmux list-sessions
# Attach to a session
tmux attach -t <session-name>
# Detach from session (inside tmux)
Ctrl+b, then d
# Switch between windows (inside tmux)
Ctrl+b, then n # Next window
Ctrl+b, then p # Previous window
Ctrl+b, then <number> # Go to window number (0-9)
Ctrl+b, then w # List all windows (interactive selector)
# Delete a session (cleanly, via CAO)
cao shutdown --session <session-name>List all windows (Ctrl+b, w):
By default, only a tight allowlist of env vars (HOME, PATH, SHELL, plus CAO_* / KIRO_* / MISE_* / AWS_* prefixes) reaches agents spawned inside tmux. The filter keeps the tmux new-session -e argv under the kernel limit and prevents nested-session loops when CAO itself runs inside a provider.
To forward additional vars to the supervisor and every worker spawned later in the same session (via assign / handoff / the web UI), pass --env KEY=VALUE to cao launch:
cao launch --agents code_supervisor \
--env MNEMOSYNE_DIR=/root/mnemosyne \
--env ISAAC_CHANNEL=room:engineeringThe flag is repeatable. Values travel in the request body, not the URL, so secrets do not land in cao-server's HTTP access log.
Rejected at the CLI boundary:
- Keys matching
CLAUDE/CODEX_/__MISE_(reserved for provider auth — the 6CLAUDE_CODE_USE_*/CLAUDE_CODE_SKIP_*auth flags are explicitly allowlisted). - Keys that decide what the pane runs before the provider CLI's first tool call: the
LD_*andDYLD_*loader families andGCONV_PATH;PATH,HOMEandSHELL, which pick the program and rc files the shell starts with; the shell hooksBASH_ENV,ENV,ZDOTDIR,PROMPT_COMMAND,PS0,PS1,PS2,PS4; the interpreter hooksPYTHONSTARTUP,PYTHONPATH,PYTHONHOME,PYTHONUSERBASE,PERL5OPT,PERL5LIB,PERLLIB,NODE_OPTIONS,NODE_PATH,RUBYOPT,RUBYLIB; andAWS_CONFIG_FILE/AWS_SHARED_CREDENTIALS_FILE, which the AWS SDK reads when the provider CLI authenticates at startup (a profile'scredential_processruns the command the file names). A value in any of these would execute as the operator before the agent's first tool call, so there is no allowlist. This is a denylist and will trail new providers and startup hooks. Variables that only act when the agent itself runs a program (GIT_SSH_COMMAND,EDITOR,PAGER) are not refused: whether the agent may run programs is the tool policy's decision. - Keys outside
[A-Za-z_][A-Za-z0-9_]*(non-POSIX names break the shell). - Values ≥ 2048 bytes (per-var cap that keeps the tmux argv under the kernel limit — see PR #246).
Forwarded vars are held in process memory on cao-server and dropped when the session is deleted; restarting cao-server wipes them.
An external agent driving CAO through the cao-ops MCP server forwards the same
vars via an env_vars mapping on launch_session — the identical mechanism,
validation, and request-body delivery as cao launch --env:
launch_session(
agent_profile="code_supervisor",
env_vars={
"MNEMOSYNE_DIR": "/root/mnemosyne",
"ISAAC_CHANNEL": "room:engineering",
},
)The same rules are enforced at the tool boundary — blocked
CLAUDE / CODEX_ / __MISE_ prefixes (with the 6 CLAUDE_CODE_USE_* /
CLAUDE_CODE_SKIP_* flags allowlisted), the loader/shell/interpreter startup
keys above, non-POSIX keys, and values ≥ 2048 bytes — so an entry the server
would refuse fails the tool call loudly instead of vanishing. The CLI, the
ops-MCP tool and POST /sessions itself share one validator
(utils/forwarded_env.py) so the paths cannot drift; a direct HTTP caller gets
a 422 naming the key.
CAO shares your default tmux server, and before it creates a pane it raises that
server's global history-limit to 50000 lines if the current value is lower.
It never lowers a value you already set higher, and if it cannot read the current
value it leaves the option alone.
Why: a worker's reply is read back out of the pane's scrollback. tmux keeps 2000
lines by default, which is shallower than the deepest read CAO attempts (5000
lines), so a long reply plus a TUI's end-of-turn redraw could push the response
out of scrollback entirely and the supervisor received a truncated reply or none
(#728). 50000 matches the repository's recommended .tmux.conf.
What it touches: because the option is global, your own new windows and sessions on that server also get the raised value. Panes that already exist keep the scrollback they have; whether their limit changes afterwards depends on the tmux version (from 3.7 the raised value applies to output that arrives later; before 3.7 an existing pane keeps the limit it was created with). Memory is allocated lazily as output arrives and only by panes that actually fill up: about 38 MB of tmux memory for a pane that holds 50000 lines of 100 characters, against about 1.8 MB at 2000.
To change the floor, set CAO_TMUX_HISTORY_LIMIT in cao-server's environment.
CAO_TMUX_HISTORY_LIMIT=0 disables the raise entirely; a value below 5000 means
the deepest extraction reads cannot be served and CAO clips them to what the
pane can hold.
- CAO session names are automatically prefixed with
cao-. Use the prefixed name (e.g.cao-my-task) when referencing a session intmux attach,cao session send, orcao shutdown. Teardown never leaves that namespace:cao shutdown --session my-taskandDELETE /sessions/my-taskact oncao-my-task, and CAO refuses to kill a tmux session whose name lacks the prefix, so a personal session that shares the operator's tmux server is out of reach. - Prefer
cao shutdownovertmux kill-session:cao shutdownexits each provider cleanly before tearing down the tmux session, which avoids leaked CLI processes.
