# Runtime Operations ## Turn Execution Timeouts These settings control one prompt turn, not long-term tmux session cleanup. Current config points are: - `agents.defaults.stream.idleTimeoutMs` - `agents.defaults.stream.noOutputTimeoutMs` - `agents.defaults.stream.maxRuntimeMin` - `agents.defaults.stream.maxRuntimeSec` - `agents.list[].stream.*` Current meaning: - `idleTimeoutMs: 6000` - once a turn has already produced visible output, clisbot treats it as completed after 6 seconds with no further meaningful runner activity - `noOutputTimeoutMs: 20000` - internal diagnostic threshold only - it is logged for metrics or debugging, but it does not settle the turn or surface a timeout into chat - `maxRuntimeMin: 30` - default observation window of 30 minutes for one turn - if the session is still active after that window, clisbot stops live follow, leaves the session running, and still posts the final result here later - `maxRuntimeSec` - optional second-based observation window when you need tighter tests or shorter limits Important distinction: - these settings affect streaming settlement and turn completion - they do not decide whether the tmux session stays alive after the turn - stale tmux cleanup is controlled separately by `session.staleAfterMinutes` and `control.sessionCleanup.*` - a detached long-running session is exempt from stale cleanup until a later interactive turn or stop action clears that detached state ## Long-Running Session Commands When a run keeps going beyond the initial observation window, `clisbot` keeps monitoring it and can keep this thread attached in different ways. Current commands: - `/attach` - attach this thread to the active run - if the run is still processing, live updates resume here - if the run is already settled, you get one latest settled state - `/detach` - stop live updates for this thread - the underlying run keeps going - the final result is still posted here when the run completes - `/watch every 30s` - post the latest state here every 30 seconds until the run completes - `/watch every 30s for 10m` - same as above, but stop interval watch after the configured window Current prompt-admission rule: - if a session already has an active run, a new prompt is rejected until that run settles or is interrupted - use `/attach`, `/watch`, or `/stop` instead of sending a second prompt into the same still-running session Current observer-scope rule: - observer mode is currently scoped per thread for a routed conversation - running `/attach` or `/watch ...` again in the same thread replaces the earlier observer mode for that same thread Current status visibility: - `/status` now shows whether the routed session is `idle`, `running`, or `detached` - when available, `/status` also shows `run.startedAt` and `run.detachedAt` - `clisbot status` now lists active runs too, so detached autonomous sessions are visible without using `/transcript` or re-attaching a thread ## clisbot tmux Server `clisbot` does not use your default tmux server. It starts and manages its own tmux server through a dedicated socket: `~/.clisbot/state/clisbot.sock` That means normal tmux commands such as `tmux list-sessions` will not show the sessions created by `clisbot`. Use the socket-aware commands below instead. ## Common Commands Prefer the operator runner CLI first: ```bash clisbot runner list ``` `clisbot status` now includes the newest five runner sessions by default, with a `(n) sessions more` tail when the socket has more than five. ```bash clisbot runner inspect --latest ``` ```bash clisbot runner inspect --index 1 ``` ```bash clisbot runner watch --lines 20 --interval 1s ``` ```bash clisbot runner watch --index 1 --lines 20 --interval 1s ``` ```bash clisbot runner watch --latest --lines 20 --interval 1s ``` ```bash clisbot runner watch --next --timeout 120s --lines 20 --interval 1s ``` Meaning: - `inspect --latest`: snapshot from the session that most recently admitted a new prompt - `watch --latest`: session that most recently admitted a new prompt - `watch --next`: first newly admitted prompt after the command starts - `--index`: 1-based order shown by `clisbot runner list` - these commands choose sessions by logical prompt flow, not by tmux create time Top-level shorthand is also available: ```bash clisbot inspect --latest clisbot watch --latest ``` Raw tmux remains available as the lower-level fallback: ```bash tmux -S ~/.clisbot/state/clisbot.sock list-sessions ``` ```bash tmux -S ~/.clisbot/state/clisbot.sock attach-session -t ``` ```bash tmux -S ~/.clisbot/state/clisbot.sock kill-session -t ``` ```bash tmux -S ~/.clisbot/state/clisbot.sock kill-server ``` ## Runtime State Important runtime paths: - config: `~/.clisbot/clisbot.json` - tmux socket: `~/.clisbot/state/clisbot.sock` - monitor pid: `~/.clisbot/state/clisbot.pid` - monitor state: `~/.clisbot/state/clisbot-monitor.json` - runtime log: `~/.clisbot/state/clisbot.log` - session store: `~/.clisbot/state/sessions.json` - activity store: `~/.clisbot/state/activity.json` - pairing store: `~/.clisbot/state/pairing` Useful checks: ```bash clisbot runner list ``` ```bash clisbot inspect --latest ``` ```bash clisbot watch --latest --lines 20 --interval 1s ``` ```bash clisbot runner watch --next --timeout 120s --lines 20 --interval 1s ``` ```bash cat ~/.clisbot/state/sessions.json ``` ```bash cat ~/.clisbot/state/activity.json ``` ```bash ls -la ~/.clisbot/state/pairing ``` ```bash tail -f ~/.clisbot/state/clisbot.log ``` ## Runtime Monitor Detached `clisbot start` now runs under an app-owned runtime monitor. Current behavior: - `clisbot.pid` belongs to the monitor process - `clisbot status` shows monitor state, current runtime pid when one is active, and `next restart` when the service is in backoff - if the runtime worker crashes repeatedly, the monitor retries with bounded backoff instead of requiring an immediate manual restart - if the monitor finds a stale worker without a live monitor, `stop` and the next `start` clean that worker up before continuing - if `clisbot restart` ever reports a stop timeout during an update, first run `clisbot status` - when status already shows `running: no`, recover explicitly with `clisbot start` - current beta releases also retry status for a short bounded window before treating that stop-timeout as fatal Current config points: - `control.runtimeMonitor.restartBackoff.fastRetry.delaySeconds` - `control.runtimeMonitor.restartBackoff.fastRetry.maxRestarts` - `control.runtimeMonitor.restartBackoff.stages[].delayMinutes` - `control.runtimeMonitor.restartBackoff.stages[].maxRestarts` - `control.runtimeMonitor.ownerAlerts.enabled` - `control.runtimeMonitor.ownerAlerts.minIntervalMinutes` Current default policy: - retry every 10 seconds for the first 3 unexpected exits - then back off through a smoother stage ladder: 1 minute, 3 minutes, 5 minutes, 10 minutes, 15 minutes, and finally 30 minutes - when the configured ladder reaches the final stage, clisbot keeps retrying at that final-stage delay instead of stopping permanently - if an older config still uses the legacy `15m x4` then `30m x4` default ladder, the runtime now normalizes that legacy default into the smoother ladder; `0.1.50` update also removes the default backoff block from persisted config so future default tuning can apply Current owner alert rule: - if `app.auth.roles.owner.users` contains reachable principals, the monitor sends a direct alert when the service first enters restart backoff - if a configuration truly has no usable final retry stage, it can still send a later direct alert when the configured restart budget is exhausted - same-kind alerts are rate-limited by `control.runtimeMonitor.ownerAlerts.minIntervalMinutes` Telegram polling conflict behavior: - if another process is temporarily using the same bot token for `getUpdates`, the Telegram channel now stays inside the runtime and retries automatically with backoff instead of stopping permanently - channel health should flip to `failed` while the conflict is active, then return to `active` automatically after polling recovers - if the polling conflict is unintended, stop the other poller; otherwise clisbot can keep waiting and recover on its own Codex trust prompt troubleshooting: - clisbot already keeps `trustWorkspace: true` by default for Codex - fresh Codex runner startup waits for the interactive `›` prompt marker before sending the first routed prompt; if a tmux pane shows the routed prompt above the Codex header, the runner likely accepted startup output too early and should be updated - current beta releases also re-check and accept an active trust prompt again immediately before sending the first routed prompt or later steering input, so a delayed trust screen does not silently absorb `/status` or the user prompt after startup - the Codex ready pattern and Gemini startup handshake defaults are code-owned defaults; generated and updated configs omit them unless an operator intentionally adds a current-schema override - if Codex still shows `Do you trust the contents of this directory?`, also mark the clisbot workspace as trusted in `~/.codex/config.toml` Example: ```toml [projects."/home/node/.clisbot/workspaces/default"] trust_level = "trusted" ``` - if the trust screen is still visible, inspect or attach to the tmux session and continue from there: ```bash clisbot inspect --index 1 ``` ```bash tmux -S ~/.clisbot/state/clisbot.sock attach -t ``` - if Codex warns that `bubblewrap` is missing on Linux, install `bubblewrap` in the runtime environment Inside each agent workspace, inbound channel files are stored under: - `{workspace}/.attachments/{sessionKey}/{messageId}/...` Current prompt behavior is minimal: - `clisbot` prepends `@/absolute/path` mentions for stored files - then it appends the user message text ## Stale tmux Cleanup clisbot can reclaim idle tmux sessions without resetting the logical conversation. Current config points are: - `agents.defaults.session.staleAfterMinutes` - `agents.list[].session.staleAfterMinutes` - `control.sessionCleanup.enabled` - `control.sessionCleanup.intervalMinutes` Current meaning: - `staleAfterMinutes: 60` - kill a live tmux runner after 60 idle minutes - `staleAfterMinutes: 0` - disable stale cleanup for that agent - `control.sessionCleanup.intervalMinutes: 5` - scan every 5 minutes for stale tmux runners Important rule: - stale cleanup kills the live tmux session only - it does not delete the stored `sessionKey -> sessionId` mapping in `~/.clisbot/state/sessions.json` - automatic startup retry, prompt-delivery retry, and same-context recovery also preserve the stored mapping; if the native session id cannot be resumed, clisbot fails truthfully instead of silently creating a new conversation - `clisbot runner list` shows the saved `sessionId`; `sessionId: not stored` means clisbot has not saved one yet - use chat `/new` when you intentionally want to trigger a new runner conversation for the same routed session; Codex and Claude receive `/new`, Gemini receives `/clear`, and clisbot then saves the new `sessionId` - the next message on the same conversation can recreate tmux and resume the prior AI CLI session when the runner supports resume - idle is determined from clisbot session activity, not from tmux CPU or pane movement directly - the cleanup loop skips sessions that are currently busy in the clisbot queue - one old user message does not make a still-busy active run look stale Example: ```json { "agents": { "defaults": { "session": { "createIfMissing": true, "staleAfterMinutes": 60 } } }, "control": { "sessionCleanup": { "enabled": true, "intervalMinutes": 5 } } } ``` How to verify: 1. send one prompt so the conversation creates a tmux session 2. confirm the tmux session exists on `~/.clisbot/state/clisbot.sock` 3. wait past the configured stale threshold 4. confirm the session disappears from `tmux list-sessions` on that socket 5. send another prompt in the same channel or thread 6. confirm the conversation resumes instead of resetting when the runner supports `sessionId` resume ## Config Reload Config reload is controlled by: - `control.configReload.watch` - `control.configReload.watchDebounceMs` Meaning: - `watch: true` enables file watching for `~/.clisbot/clisbot.json` - `watchDebounceMs` delays reload slightly so one save operation does not trigger multiple reloads Important rule: - if watch is currently off, changing the file to turn watch on still needs one manual restart because there is no watcher yet - once watch is on, later config saves should reload automatically Example: ```json { "control": { "configReload": { "watch": true, "watchDebounceMs": 250 } } } ``` Operational behavior: - saving `~/.clisbot/clisbot.json` should trigger an in-process reload - the service should log `clisbot reloaded config ...` - later Slack messages should use the new config without a manual restart Safe way to verify: 1. change a visible Slack setting such as `bots.slack.defaults.ackReaction` 2. save the config file 3. confirm the reload log appears 4. send a Slack test message 5. confirm the new reaction or behavior is visible Runtime follow-up state is stored per `sessionKey` in: `~/.clisbot/state/sessions.json` Useful fields are: - `sessionId` - `followUp.overrideMode` - `followUp.lastBotReplyAt` - `updatedAt` Current default follow-up window is 5 minutes: - `bots.slack.defaults.followUp.participationTtlMin: 5` - `bots.telegram.defaults.followUp.participationTtlMin: 5` Optional second-based tuning is also supported: - `bots.slack.defaults.followUp.participationTtlSec` - `bots.telegram.defaults.followUp.participationTtlSec`