Files
multi-agent-mux/.agents/skills/multi-agent-mux-resume/SKILL.md
T

11 KiB

name, description, version, author, license, platforms, environments, metadata
name description version author license platforms environments metadata
multi-agent-mux-resume Resume an existing agent (claude, antigravity/agy) conversation by UUID into a herdr session. Reads .mam/agent-sessions.yaml for the saved session/conversation id, spawns (or reuses) a herdr session of the matching name, and runs `claude -r <id>` or `agy --conversation <id>` inside. Use when you want to reattach to a previous session's context, or revive a session whose herdr died but the agent's conversation is still on disk. 4.9.9 godopu MIT
linux
macos
terminal
herdr
hermes
tags related_skills prereq_skills
agent
herdr
claude
antigravity
agy
multi-agent
context
resume
session-id
multi-agent-mux-create
multi-agent-mux-stop
multi-agent-mux-monitor
claude-code
multi-agent-mux-create

Multi-Agent Resume — Reattach to a Saved Conversation

Companion skills: multi-agent-mux-create (start a fresh agent), multi-agent-mux-stop (terminate), multi-agent-mux-monitor (live status). Herdr Isolation: HERDR_SESSION_NAME env var를 create에서 설정한 경우, 동일 서버에서 동작합니다. 자세한 격리 패턴은 multi-agent-mux-create/SKILL.md 참조. Single source of truth: ./.mam/agent-sessions.yaml.

What this skill does

Container + data reconstruction: spawn a herdr session (the container), then run the agent inside with a specific session id (the data) so the previous conversation's context is restored.

Three cases this skill handles:

  1. herdr is dead, conversation lives — agent-sessions.yaml has the UUID. The JSONL/db is on disk. Re-spawn the herdr session + run claude -r <id> / agy --conversation <id>.
  2. herdr is alive but empty — You started a session with multi-agent-mux-create but haven't sent a message yet (so no session id was assigned). The user can either send their first message (and the id is auto-assigned), or you can read the workspace's most recent conversation from $HOME_DIR/.gemini/antigravity-cli/cache/last_conversations.json (defaults to ~/.gemini/...) for agy, or the latest *.jsonl in $CLAUDE_PROJECT_DIR/<workspace-key>/ (defaults to ~/.claude/projects/) for claude.
  3. herdr is alive AND the agent inside is already running — resume_session.sh only updates the row and exits 0. It does not attach and does not check the agent's permission flags (#45); attach with herdr --session <server> agent attach <name>, where <server> is the row's herdr_session (the server it was resumed on).

Resuming a stopped session (stopped → running)

When a session was ended via multi-agent-mux-stop (which captures the ID and gracefully stops by default), its row is status: stopped with resumable: true and the conversation id already recorded in claude_session_id_own / agy_conversation_id_own. This is the ideal resume path:

  • tier-1, race-free: when the row has its own id recorded, session_ids.py resolves it directly from the per-row own id (after checking no other row claims it and revalidating on disk) — no reliance on the mtime-based disk scan, so a concurrent session in the same or another workspace can never shadow it.
  • On resume, update_yaml_resumed.sh transitions stopped → running and clears the stop metadata (stopped_at, stopped_at_epoch, stop_reason, resumable) along with the usual terminated_at* / termination_mode / archived_at, so the row reflects a clean running state with no stale end-of-session fields.

UUID resolution order

agent-sessions.yaml and on-disk discovery are used to resolve the UUID in this order:

  1. herdr_sessions[] row's per-row own id (claude_session_id_own / agy_conversation_id_own / hermes_conversation_id_own / grok_session_id_own / opencode_session_id_own / codex_session_id_own) — verified against disk and refused (exit 1) if another row also records it (tier-1, race-free).
  2. Without an own id: Class A (agy, hermes, opencode, codex) uses #40 pane capture (autopin.capture) when herdr is alive, starts a fresh conversation (FRESH_SPAWN=1 + NOTE: on stderr) when herdr is dead and 2+ same-type rows share the cwd, or scans the workspace when it is the only row; Class B (claude, grok) runs the workspace scan (find_workspace_uuid, skipping ids claimed by other rows).

If the resolved id is empty → for Class A agents (agy, hermes, opencode, codex), resume_session.sh uses FRESH_SPAWN=1 (spawn-spec). For Class B agents (claude, grok), fall back to multi-agent-mux-create.

Workflow

bash .agents/skills/multi-agent-mux-resume/scripts/resume_session.sh --workspace <path> --agent <agent> --session <name>

resume_session.sh does the whole flow: original-cwd check, control-plane binding, session-id resolution (resolve_session_id.sh), the herdr-alive check, the agent command (adapter resume-spec, or spawn-spec for a Class A fresh spawn), spawn (just before it, the session's own empty shell left by a server restart is closed: same session id, no agent, nothing running; lib_py/herdr_restore.py), start-up dialogs, and the YAML update (update_yaml_resumed.sh, which also publishes a progress "resumed" event when the row has a delegate_job_id). It never attaches; attaching is a separate step that names the server, because the server the script exports does not reach the calling shell and the native herdr binary does not route on environment variables: herdr --session <server> agent attach <name> (<server> = the --herdr-session you passed, or the row's herdr_session).

Option Meaning
--workspace Required. The directory the session ran in (its row's pane.cwd; a different path is exit 2)
--agent Required. claude, agy, hermes, grok, opencode, codex
--session Required. Session name
--herdr-session (--herdr-server) Isolated herdr server name
--herdr-workspace herdr workspace label
--auto-trust / --no-auto-trust Answer a start-up directory-trust dialog or leave it to a human (MAM_AUTO_TRUST; default: answer)
--dry-run Resolve the command and print it; no spawn and no registry (YAML/DB) update. The checks before it still run, and on macOS they remove the quarantine attribute from the agent binary
Exit Meaning
0 Resumed; or already running, so only the row was updated (no attach); or --dry-run
1 From the checks before spawn, nothing was changed: no saved session for claude/grok (use multi-agent-mux-create first), the row's id is also claimed by another row, a same-cwd Class A session did not record its id within MAM_ID_ORDER_WAIT_S (default 600), or agent binary missing/not executable. The YAML update after spawn (update_yaml_resumed.sh) also exits 1 (YAML not found)
2 From the checks before spawn, nothing was changed: bad arguments, --workspace differs from the original cwd or it no longer exists, control-plane binding refused, whitespace in the control-plane path. The YAML update after spawn also exits 2 (bad arguments)
other A later step failed (herdr spawn, start-up dialogs, YAML update) and its code is passed through. resume does not roll back

A non-zero exit after the spawn can leave the herdr session running while the row is still stopped. Before retrying or creating a new session, check with multi-agent-mux-status. The ERROR: line on stderr tells the two apart: the pre-spawn checks' messages are in resume_session.sh (lines 50-243).

Original cwd only (Issue #21 phase 2)

A session is resumed only in the directory it ran in (its row's pane.cwd): the conversation is keyed by that directory, and a class A agent (agy/hermes/opencode/codex) would otherwise start a new conversation in the wrong tree.

  • --workspace differing from pane.cwd → exit 2 with the path to use (claude/grok used to fail with exit 1 "No saved session").
  • pane.cwd no longer exists (worktree removed) → exit 2; recreate the worktree at that path to resume.
  • Rows without pane.cwd are not checked. The check runs before session-id resolution and also under --dry-run.
  • The control-plane binding is computed before the herdr-alive check, so an already-running session's row gets it too; a binding that cannot be computed is refused (exit 2) for a worktree and warned about for the control root.

Pitfalls

  • claude -r requires the SAME project directory — if the workspace path differs from when the session was created, claude will create a new project dir key (-home-...-different-name) and put the resume in a different location. Always -c (cd to workspace) before running.
  • agy's --conversation flag name varies by version — older versions used --resume or -r. Check agy --help | grep -E "conversation|resume" and use the right flag. v1.0.x: --conversation.
  • The first message after resume might re-trigger TUI dialogs — if the original session was created with --dangerously-skip-permissions, those flags are NOT persisted; you must re-apply them on resume. resume_session.sh re-passes them through the adapter resume-spec.
  • Don't resume if the session is brand new and empty — multi-agent-mux-create already set up an empty container; sending a probe message ("init") is the right way to materialize a session id, NOT claude -r with a placeholder.
  • agy --conversation <id> will fail if the conversation was deleted from disk — check ~/.gemini/antigravity-cli/conversations/<uuid>.db exists before attempting resume. If missing, the conversation is gone; you need a fresh session via multi-agent-mux-create.

Verification

# The server the session runs on (its row's herdr_session); the native herdr
# binary needs it as --session, an exported variable is not enough.
SERVER=$(python3 -c "import yaml; d = yaml.safe_load(open('.mam/agent-sessions.yaml')); print([s for s in d['herdr_sessions'] if s['name'] == '$SESSION_NAME'][0].get('herdr_session') or 'default')")

# 1. herdr alive with the right cmd (real native command, no lib.sh needed)
herdr --session "$SERVER" agent get "$SESSION_NAME" | python3 -c "
import sys, json
a = json.load(sys.stdin)['result']['agent']
print(f\"cmd={a['agent']} cwd={a['cwd']}\")
"

# 2. agent-sessions.yaml updated
python3 -c "
import yaml
d = yaml.safe_load(open('.mam/agent-sessions.yaml'))
s = [s for s in d['herdr_sessions'] if s['name'] == '$SESSION_NAME'][0]
print(f'  status: {s[\"status\"]}')
print(f'  pane.cmd_full: {s[\"pane\"][\"cmd_full\"]}')
"

# 3. TUI shows resumed conversation (real native command, no lib.sh needed)
sleep 5
herdr --session "$SERVER" agent read "$SESSION_NAME" --source visible --lines 30
# look for the previous message at top of the buffer (claude) or last_visible_status set (agy)

herdr list-panes / herdr capture-pane are tmux-compat pseudo-commands that only work after source .agents/skills/lib.sh — the real herdr binary doesn't have those subcommands. This block uses only the real herdr agent get/herdr agent read commands, so it works standalone.

When NOT to use this skill

  • No saved session yet → multi-agent-mux-create
  • Killing an existing session → multi-agent-mux-stop
  • Just attaching → herdr --session <server> agent attach <name> (no skill needed; <server> = the row's herdr_session)