Skip to content

Commit b1c8ced

Browse files
committed
ops: keep tmux for agent sessions, remove bridge tmux guidance
1 parent a8bc265 commit b1c8ced

6 files changed

Lines changed: 111 additions & 54 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Baudbot is designed as shared engineering infrastructure, not a single-user desk
5252
| **CPU** | 2 vCPU | 4 vCPU |
5353
| **Disk** | 20 GB | 40 GB+ (repos, dependencies, Docker images) |
5454

55-
System package dependencies (installed by `baudbot install`): `git`, `curl`, `iptables`, `docker`, `gh`, `jq`, `sudo`.
55+
System package dependencies (installed by `baudbot install`): `git`, `curl`, `tmux`, `iptables`, `docker`, `gh`, `jq`, `sudo`.
5656

5757
## Quick Start
5858

bin/baudbot

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ usage() {
131131
echo " restart Restart the agent"
132132
echo " status Show agent status + deployed version + broker connection"
133133
echo " logs Tail agent logs"
134-
echo " attach Attach to a running pi session (defaults to control-agent)"
135-
echo " sessions List live pi sessions (name → id)"
134+
echo " attach Attach to control-agent by default; supports --pi/--tmux"
135+
echo " sessions List agent tmux and pi sessions (name → id)"
136136
echo ""
137137
echo -e "${BOLD}Setup:${RESET}"
138138
echo " install Bootstrap install from GitHub (download script, then escalate)"

bin/lib/baudbot-runtime.sh

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,8 @@ cmd_logs() {
305305
exec journalctl -u baudbot -f "$@"
306306
fi
307307

308-
echo "No systemd unit. Check process + logs:"
309-
echo " pgrep -u baudbot_agent -af 'pi --session-control'"
310-
echo " tail -n 200 /home/baudbot_agent/.pi/agent/logs/runtime.log"
308+
echo "No systemd unit. Check tmux sessions:"
309+
echo " sudo -u baudbot_agent tmux ls"
311310
}
312311

313312
cmd_sessions() {
@@ -317,6 +316,14 @@ cmd_sessions() {
317316
local found alias alias_name alias_uuid sock sess_id name status
318317
declare -A ALIASES
319318

319+
echo -e "${BOLD}tmux sessions:${RESET}"
320+
if sudo -u "$AGENT_USER" tmux ls 2>/dev/null; then
321+
:
322+
else
323+
echo " (none)"
324+
fi
325+
326+
echo ""
320327
echo -e "${BOLD}pi sessions:${RESET}"
321328
PI_CONTROL_DIR="$(pi_control_dir "$AGENT_USER")"
322329
if [ ! -d "$PI_CONTROL_DIR" ]; then
@@ -370,26 +377,28 @@ cmd_attach() {
370377

371378
local AGENT_USER="baudbot_agent"
372379
local AGENT_HOME="/home/$AGENT_USER"
380+
local ATTACH_MODE="auto"
373381
local TARGET=""
374-
local pi_target
382+
local tmux_target pi_target
375383

376384
while [ "$#" -gt 0 ]; do
377385
case "$1" in
378386
--pi)
379-
# Backward-compatible no-op (pi is the only supported mode now)
387+
ATTACH_MODE="pi"
380388
shift
381389
;;
382390
--tmux)
383-
echo "❌ --tmux is no longer supported. Use: sudo baudbot attach [session-name|session-id]"
384-
exit 1
391+
ATTACH_MODE="tmux"
392+
shift
385393
;;
386394
-h|--help)
387-
echo "Usage: sudo baudbot attach [session-name|session-id]"
395+
echo "Usage: sudo baudbot attach [--pi|--tmux] [session-name|session-id]"
388396
echo ""
389397
echo "Examples:"
390398
echo " sudo baudbot attach # defaults to control-agent"
391-
echo " sudo baudbot attach control-agent"
392-
echo " sudo baudbot attach <uuid>"
399+
echo " sudo baudbot attach --pi control-agent"
400+
echo " sudo baudbot attach --pi <uuid>"
401+
echo " sudo baudbot attach --tmux sentry-agent"
393402
exit 0
394403
;;
395404
*)
@@ -407,22 +416,82 @@ cmd_attach() {
407416
TARGET="control-agent"
408417
fi
409418

419+
attach_tmux_session() {
420+
local tmux_target="$1"
421+
echo -e "${BOLD}${CYAN}Attaching to tmux session:${RESET} $tmux_target"
422+
echo -e "${GREEN}Safe detach:${RESET} Ctrl+b, d ${DIM}(keeps agent running)${RESET}"
423+
echo ""
424+
pause_before_attach
425+
exec sudo -u "$AGENT_USER" tmux attach-session -t "$tmux_target"
426+
}
427+
410428
attach_pi_session() {
411-
local target_session="$1"
412-
echo -e "${BOLD}${CYAN}Attaching to pi session:${RESET} $target_session"
429+
local pi_target="$1"
430+
echo -e "${BOLD}${CYAN}Attaching to pi session:${RESET} $pi_target"
413431
echo -e "${BOLD}${YELLOW}Safe detach (does NOT stop the agent):${RESET}"
414432
echo -e " ${YELLOW}1)${RESET} Press Ctrl+C once to clear input/cancel local prompt"
415433
echo -e " ${YELLOW}2)${RESET} Press Ctrl+C again to exit this client"
416434
echo -e " ${GREEN}Agent keeps running under systemd in the background.${RESET}"
417435
echo ""
418436
pause_before_attach
419-
exec sudo -u "$AGENT_USER" bash -lc "export PATH='$AGENT_HOME/.varlock/bin:$AGENT_HOME/opt/node-v22.14.0-linux-x64/bin':\$PATH; cd ~; varlock run --path ~/.config/ -- pi --session '$target_session'"
437+
exec sudo -u "$AGENT_USER" bash -lc "export PATH='$AGENT_HOME/.varlock/bin:$AGENT_HOME/opt/node-v22.14.0-linux-x64/bin':\$PATH; cd ~; varlock run --path ~/.config/ -- pi --session '$pi_target'"
438+
}
439+
440+
choose_tmux_target() {
441+
local requested="${1:-}"
442+
local first
443+
444+
if [ -n "$requested" ]; then
445+
if sudo -u "$AGENT_USER" tmux has-session -t "$requested" 2>/dev/null; then
446+
echo "$requested"
447+
return 0
448+
fi
449+
return 1
450+
fi
451+
452+
first=$(sudo -u "$AGENT_USER" tmux ls -F '#{session_name}' 2>/dev/null | head -1)
453+
[ -n "$first" ] || return 1
454+
echo "$first"
455+
return 0
456+
}
457+
458+
choose_pi_target() {
459+
local requested="${1:-}"
460+
local resolved
461+
462+
if ! resolved=$(resolve_pi_session_id "$AGENT_USER" "$requested"); then
463+
return 1
464+
fi
465+
466+
[ -n "$resolved" ] || return 1
467+
echo "$resolved"
468+
return 0
420469
}
421470

422-
if pi_target=$(resolve_pi_session_id "$AGENT_USER" "$TARGET"); then
471+
if [ "$ATTACH_MODE" = "tmux" ]; then
472+
if tmux_target=$(choose_tmux_target "$TARGET"); then
473+
attach_tmux_session "$tmux_target"
474+
fi
475+
echo "❌ tmux session not found. See: sudo baudbot sessions"
476+
exit 1
477+
fi
478+
479+
if [ "$ATTACH_MODE" = "pi" ]; then
480+
if pi_target=$(choose_pi_target "$TARGET"); then
481+
attach_pi_session "$pi_target"
482+
fi
483+
echo "❌ pi session not found. See: sudo baudbot sessions"
484+
exit 1
485+
fi
486+
487+
if pi_target=$(choose_pi_target "$TARGET"); then
423488
attach_pi_session "$pi_target"
424489
fi
425490

426-
echo "❌ pi session not found. See: sudo baudbot sessions"
491+
if tmux_target=$(choose_tmux_target "$TARGET"); then
492+
attach_tmux_session "$tmux_target"
493+
fi
494+
495+
echo "❌ No matching tmux/pi session found. See: sudo baudbot sessions"
427496
exit 1
428497
}

install.sh

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ install_prereqs_ubuntu() {
163163

164164
for attempt in $(seq 1 5); do
165165
if DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=120 update -qq \
166-
&& DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=120 install -y -qq git curl iptables docker.io gh jq sudo 2>&1 | tail -3; then
166+
&& DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=120 install -y -qq git curl tmux iptables docker.io gh jq sudo 2>&1 | tail -3; then
167167
return 0
168168
fi
169169

@@ -179,10 +179,10 @@ install_prereqs_ubuntu() {
179179
}
180180

181181
install_prereqs_arch() {
182-
pacman -Syu --noconfirm --needed git curl iptables docker github-cli jq sudo 2>&1 | tail -5
182+
pacman -Syu --noconfirm --needed git curl tmux iptables docker github-cli jq sudo 2>&1 | tail -5
183183
}
184184

185-
info "Installing: git, curl, iptables, docker, gh, jq, sudo"
185+
info "Installing: git, curl, tmux, iptables, docker, gh, jq, sudo"
186186
"install_prereqs_$DISTRO"
187187
info "Prerequisites installed"
188188

@@ -318,15 +318,12 @@ else
318318
warn "Agent didn't start — check: baudbot logs"
319319
fi
320320
else
321-
RUNTIME_LOG_DIR="$BAUDBOT_HOME/.pi/agent/logs"
322-
RUNTIME_LOG_FILE="$RUNTIME_LOG_DIR/runtime.log"
323-
sudo -u baudbot_agent mkdir -p "$RUNTIME_LOG_DIR"
324-
sudo -u baudbot_agent bash -lc "nohup '$BAUDBOT_HOME/runtime/start.sh' >> '$RUNTIME_LOG_FILE' 2>&1 &"
321+
sudo -u baudbot_agent tmux new-session -d -s baudbot "$BAUDBOT_HOME/runtime/start.sh" 2>/dev/null || true
325322
sleep 2
326-
if pgrep -u baudbot_agent -f "pi --session-control" >/dev/null 2>&1; then
323+
if sudo -u baudbot_agent tmux has-session -t baudbot 2>/dev/null; then
327324
info "Agent is running ✓"
328325
else
329-
warn "Agent didn't start — check: $RUNTIME_LOG_FILE"
326+
warn "Agent didn't start — try: baudbot start --direct"
330327
fi
331328
fi
332329
else

pi/skills/control-agent/SKILL.md

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -198,22 +198,19 @@ git fetch origin
198198
git worktree add ~/workspace/worktrees/$BRANCH -b $BRANCH origin/main
199199

200200
# 2. Launch the agent IN the worktree
201-
mkdir -p ~/.pi/agent/logs
202-
nohup bash -lc "cd ~/workspace/worktrees/$BRANCH && \
203-
export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && \
204-
export PI_SESSION_NAME=$SESSION_NAME && \
205-
exec varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/dev-agent --model <MODEL_FROM_TABLE_ABOVE>" \
206-
> ~/.pi/agent/logs/$SESSION_NAME.log 2>&1 &
207-
DEV_PID=$!
208-
echo $DEV_PID > ~/.pi/agent/$SESSION_NAME.pid
201+
tmux new-session -d -s $SESSION_NAME \
202+
"cd ~/workspace/worktrees/$BRANCH && \
203+
export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && \
204+
export PI_SESSION_NAME=$SESSION_NAME && \
205+
exec varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/dev-agent --model <MODEL_FROM_TABLE_ABOVE>"
209206
```
210207

211208
**Important notes:**
212209
- `cd` into the worktree BEFORE launching pi — this ensures pi discovers project context from the repo's CWD
210+
- Use `exec` so the tmux session exits when pi exits
213211
- Use `varlock run --path ~/.config/` to validate and inject env vars
214212
- Set `PI_SESSION_NAME` so the auto-name extension registers it
215213
- Include `--session-control` for `send_to_session` / `list_sessions`
216-
- Read logs with `tail -f ~/.pi/agent/logs/$SESSION_NAME.log`
217214
- Wait **~10 seconds** after spawning before sending messages (agent needs time to initialize)
218215
- Do NOT use `--name` (not a real pi CLI flag)
219216

@@ -228,13 +225,8 @@ SESSION_NAME=dev-agent-myapp-a8b7b331
228225
REPO=myapp
229226
BRANCH=fix/some-descriptive-name
230227

231-
# 1. Kill the process if still running
232-
PID_FILE=~/.pi/agent/$SESSION_NAME.pid
233-
if [ -f "$PID_FILE" ]; then
234-
PID=$(cat "$PID_FILE" 2>/dev/null || true)
235-
[ -n "$PID" ] && kill "$PID" 2>/dev/null || true
236-
rm -f "$PID_FILE"
237-
fi
228+
# 1. Kill the tmux session (agent should have already exited, but ensure it)
229+
tmux kill-session -t $SESSION_NAME 2>/dev/null || true
238230

239231
# 2. Remove the worktree
240232
cd ~/workspace/$REPO
@@ -317,10 +309,10 @@ This removes stale `.sock` files, cleans dead aliases, and restarts the Slack br
317309
- [ ] Find or create sentry-agent:
318310
1. Use `list_sessions` to look for a session named `sentry-agent`
319311
2. If found, use that session
320-
3. If not found, launch as a normal background process (see Sentry Agent section)
312+
3. If not found, launch with tmux (see Sentry Agent section)
321313
4. Wait ~8 seconds, then send role assignment
322314
- [ ] Send role assignment to the `sentry-agent` session
323-
- [ ] Clean up any stale dev-agent worktrees/background processes from previous runs
315+
- [ ] Clean up any stale dev-agent worktrees/tmux sessions from previous runs
324316

325317
**Note**: Dev agents are NOT started at startup. They are spawned on-demand when tasks arrive.
326318

@@ -338,9 +330,7 @@ The sentry-agent triages Sentry alerts and investigates critical issues via the
338330
| `OPENCODE_ZEN_API_KEY` | `opencode-zen/claude-haiku-4-5` |
339331

340332
```bash
341-
mkdir -p ~/.pi/agent/logs
342-
nohup bash -lc "export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && export PI_SESSION_NAME=sentry-agent && exec varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/sentry-agent --model <MODEL_FROM_TABLE_ABOVE>" > ~/.pi/agent/logs/sentry-agent.log 2>&1 &
343-
echo $! > ~/.pi/agent/sentry-agent.pid
333+
tmux new-session -d -s sentry-agent "export PATH=\$HOME/.varlock/bin:\$HOME/opt/node-v22.14.0-linux-x64/bin:\$PATH && export PI_SESSION_NAME=sentry-agent && varlock run --path ~/.config/ -- pi --session-control --skill ~/.pi/agent/skills/sentry-agent --model <MODEL_FROM_TABLE_ABOVE>"
344334
```
345335

346336
**Model note**: `github-copilot/*` models reject Personal Access Tokens and will fail in non-interactive sessions.
@@ -351,12 +341,13 @@ The sentry-agent operates in **on-demand mode** — it does NOT poll. Sentry ale
351341

352342
The `startup-cleanup.sh` script handles bridge (re)start automatically — it detects broker vs Socket Mode, reads the control-agent UUID, and starts the bridge as a normal background process.
353343

354-
If you need to restart the bridge manually, run `startup-cleanup.sh` again and then inspect logs:
344+
If you need to restart the bridge manually, rerun startup cleanup and then inspect logs:
355345
```bash
346+
bash ~/.pi/agent/skills/control-agent/startup-cleanup.sh UUID1 UUID2 UUID3
356347
tail -n 200 ~/.pi/agent/logs/slack-bridge.log
357348
```
358349

359-
Verify API readiness: `curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:7890/send -H 'Content-Type: application/json' -d '{}'` → should return `400`.
350+
Verify: `curl -s -o /dev/null -w '%{http_code}' -X POST http://127.0.0.1:7890/send -H 'Content-Type: application/json' -d '{}'` → should return `400`.
360351

361352
The bridge forwards:
362353
- **Human @mentions and DMs** from allowed users → delivered to you with security boundaries for handling
@@ -369,9 +360,9 @@ Health checks run automatically every ~10 minutes via the `heartbeat.ts` extensi
369360
If you need to check manually, use `heartbeat trigger` to run all checks immediately.
370361

371362
When the heartbeat reports a failure, take the appropriate action:
372-
1. **Missing sentry-agent**: Respawn as a background process and re-send role assignment.
373-
2. **Orphaned dev-agents**: Kill stale process + remove worktree.
374-
3. **Bridge down**: Restart via `startup-cleanup.sh` and read `~/.pi/agent/logs/slack-bridge.log`.
363+
1. **Missing sentry-agent**: Respawn with tmux and re-send role assignment.
364+
2. **Orphaned dev-agents**: Kill tmux session and remove worktree.
365+
3. **Bridge down**: Restart via `startup-cleanup.sh`, then check `~/.pi/agent/logs/slack-bridge.log`.
375366
4. **Stale worktrees**: `git worktree remove --force` + `rmdir` empty parents.
376367
5. **Stuck todos**: Escalate to user via Slack.
377368

pi/skills/control-agent/memory/operational.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ Add entries under dated headings. Keep entries concise — one line per learning
88
<!-- Example:
99
## 2026-02-17
1010
- Stale `.sock` files cause bridge "connect ENOENT" errors. Always run `startup-cleanup.sh` with live UUIDs on boot.
11-
- `varlock run` must be used (not `source .env`) when launching agents in background processes — ensures schema validation.
11+
- `varlock run` must be used (not `source .env`) when launching agents in tmux — ensures schema validation.
1212
-->

0 commit comments

Comments
 (0)