feat(tmux): implement 2-hour inactivity session TTL reaper and prune command

This commit is contained in:
operator
2026-10-05 16:23:45 +00:00
parent c13ee20ea4
commit 945e6bb481
4 changed files with 72 additions and 0 deletions
+26
View File
@@ -729,6 +729,27 @@ def _tmux_kill_build(a):
return [sys.executable, os.path.join(BIN_DIR, 'muse-tmux.py'), 'kill', a['session']]
def _tmux_prune_validate(raw):
if not isinstance(raw, dict):
raise OpError('args must be an object')
allowed = {'ttl'}
for k in raw:
if k not in allowed:
raise OpError(f'unknown arg: {k}')
ttl = raw.get('ttl', 7200)
try:
ttl = int(ttl)
if ttl < 60:
ttl = 60
except Exception:
ttl = 7200
return {'ttl': ttl}
def _tmux_prune_build(a):
return [sys.executable, os.path.join(BIN_DIR, 'muse-tmux.py'), 'prune', '--ttl', str(a['ttl'])]
def _vars_list_validate(raw):
if raw not in ({}, None):
raise OpError('vars.list takes no required args')
@@ -1174,6 +1195,11 @@ OPS = {
'timeout': 15, 'side_effecting': True,
'desc': 'Kill a session on shared Muse tmux socket (/tmp/tmux-muse.sock)',
},
'tmux.prune': {
'validate': _tmux_prune_validate, 'build': _tmux_prune_build,
'timeout': 15, 'side_effecting': True,
'desc': 'Reap stale unattached sessions inactive for >TTL (default 2h)',
},
'exec.ping': {
'validate': _health_validate,
'build': lambda a: ['/bin/echo', 'PONG'],
+39
View File
@@ -105,6 +105,40 @@ def cmd_kill(args):
print(f"Error: {err.strip() or out.strip()}", file=sys.stderr)
return rc
def cmd_prune(args):
ttl_seconds = getattr(args, "ttl", 7200) or 7200
rc, out, err = _run_tmux("list-sessions", "-F", "#{session_name} #{session_activity} #{session_attached}")
if rc != 0:
if "no server running" in (err or "").lower() or rc == 1:
print(f"No active tmux server on {SOCKET_PATH}.")
return 0
print(f"Error listing sessions: {err.strip() or out.strip()}", file=sys.stderr)
return rc
now = int(time.time())
reaped = []
kept = []
for line in out.strip().splitlines():
parts = line.split()
if len(parts) >= 3:
s_name, s_activity, s_attached = parts[0], int(parts[1]), int(parts[2])
idle_time = now - s_activity
# Only prune if unattached and idle > TTL
if s_attached == 0 and idle_time > ttl_seconds:
_run_tmux("kill-session", "-t", s_name)
reaped.append((s_name, idle_time))
else:
kept.append((s_name, idle_time, s_attached))
if reaped:
print(f"✔ Reaped {len(reaped)} stale unattached session(s) (idle > {ttl_seconds//3600}h):")
for s_name, idle in reaped:
print(f" • {s_name} (idle: {idle//60}m)")
else:
print(f"No stale unattached sessions to reap (all active within {ttl_seconds//3600}h).")
return 0
def cmd_attach(args):
session = args.session
cmd = [TMUX_BIN, "-S", SOCKET_PATH, "attach", "-t", session]
@@ -144,6 +178,11 @@ def main():
p_kill.add_argument("session", help="Session name")
p_kill.set_defaults(func=cmd_kill)
# prune
p_prune = subparsers.add_parser("prune", help="Reap stale unattached sessions inactive for >TTL (default: 7200s / 2h)")
p_prune.add_argument("--ttl", type=int, default=7200, help="Inactivity threshold in seconds (default: 7200)")
p_prune.set_defaults(func=cmd_prune)
# attach
p_att = subparsers.add_parser("attach", help="Attach to a session interactively")
p_att.add_argument("session", help="Session name")
+3
View File
@@ -22,3 +22,6 @@ done
find /tmp -name "*-smoke.log" -mtime +7 -delete 2>/dev/null
find /tmp -name "otp_*.py" -mtime +1 -delete 2>/dev/null
find /tmp -name "check_*.py" -mtime +1 -delete 2>/dev/null
# Prune unattached stale agent tmux sessions older than 2 hours (7200s)
python3 "$(cd "$(dirname "$0")" && pwd)/muse-tmux.py" prune --ttl 7200 2>/dev/null || true
+4
View File
@@ -89,6 +89,9 @@ muse <account> tmux capture <session> --lines 30
# Kill a session
muse <account> tmux kill <session>
# Prune unattached sessions inactive for >2 hours (or custom --ttl in seconds)
muse <account> tmux prune [--ttl 7200]
```
### Via Autonomous Agent Directives:
@@ -98,6 +101,7 @@ Agents can emit structured tool calls in sidechats:
[TOOL tmux.send {"session": "build-worker", "keys": "ls -la"}]
[TOOL tmux.capture {"session": "build-worker", "lines": 20}]
[TOOL tmux.list {}]
[TOOL tmux.prune {"ttl": 7200}]
[TOOL tmux.kill {"session": "build-worker"}]
```