Files
multi-agent-mux/.agents/skills/multi-agent-mux-resume/SKILL.md
T
GodopuandClaude Opus 5 387479732e release(v4.4.0): bump framework and eight skills to 4.4.0 in lockstep
Issue #5 (Python core migration, CAS state transitions, shadow watcher,
auto-completion gate, eager-import immutable runtime, B-13 freeze removal)
is complete and merged to main. This stamps the release.

SemVer: MINOR, not MAJOR. The change is large but breaks nothing. Across the
whole Issue #5 range there are zero file deletions and zero renames, the event
wire schema stays at SCHEMA_VERSION 1, all four literal script paths survive as
compatibility shims, the 968-line legacy shell loop is still in run_loop.sh,
and every default change reverts through a single environment variable
(MAM_DELEGATION_PY / MAM_ENGINE_CAS / MAM_LOOP_PY / MAM_LOOP_FREEZE). The
riskiest new feature, auto-completion, ships default-off. No operator has
anything to migrate, which is the decisive reason not to call it 5.0.0. The
reasoning is recorded in the VERSIONS.md release notes, not just here.

Close a gap in the lockstep guard. lib.sh exports MAM_VERSION and eager_load()
refuses to start the loop when it disagrees with FRAMEWORK_VERSION, so eager.py
is a load-bearing fourth leg — but test_version_consistency.py only checked
three, and the sole check on eager.py was a hardcoded literal in the phase 4
test that a release bump has to edit anyway. The lockstep test now covers
eager.py, a new test pins the fail-closed gate itself, and the phase 4 test no
longer carries a version literal.

Fix a latent test-isolation defect the bump exposed. The first full run failed
four tests with "MAM_VERSION mismatch: expected 4.4.0, got 4.3.0 in
environment" — 4.3.0 was not in the tree, it was inherited from the shell that
launched pytest and had sourced the pre-bump lib.sh. Those tests were measuring
the runner's shell rather than the tree under test, and only passed until now
because the two values always happened to agree. A session-scoped fixture drops
the ambient value. Production is unaffected: run_loop.sh:17 sources lib.sh
before anything else and lib.sh re-exports MAM_VERSION unconditionally, so a
stale inherited value is always overwritten before Python starts.

Full suite: 750 passed in 912.50s, 0 failed, 0 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
2026-09-07 15:55:26 +09:00

9.3 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.4.0 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 — Just attach. No re-spawn needed.

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: because the stop command wrote the id into the row at stop time, resolve_session_id.sh resolves it via find_workspace_uuid tier-1 (the per-row own id) — no reliance on the mtime-based disk scan, so a concurrent session in 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) — explicitly saved by multi-agent-mux-stop right before teardown (tier-1, race-free).
  2. Workspace-scoped on-disk scan (adapter discover())

If both are empty → for Class A agents (agy, hermes, opencode, codex), resume_session.sh automatically falls back to FRESH_SPAWN=1 (spawn-spec). For Class B agents (claude, grok), fall back to multi-agent-mux-create.

Workflow

WORKSPACE=/path/to/project
AGENT=claude  # claude | agy | hermes | grok | opencode | codex — pass it explicitly
SESSION_NAME=<workspace>-creator-<agent>  # same convention as multi-agent-mux-create

# Resolve the isolated herdr server name & load common utils
source .agents/skills/lib.sh

# 1. Resolve the session id (pass session name to prefer target-row recorded id)
UUID=$(bash .agents/skills/multi-agent-mux-resume/scripts/resolve_session_id.sh \
  --workspace "$WORKSPACE" --agent "$AGENT" --session "$SESSION_NAME")

if [ -z "$UUID" ]; then
  case "$AGENT" in
    agy|hermes|opencode|codex)
      : # resume_session.sh handles fresh-spawn fallback automatically (FRESH_SPAWN=1)
      ;;
    *)
      echo "ERROR: No saved session for $WORKSPACE ($AGENT). Use multi-agent-mux-create first." >&2
      exit 1
      ;;
  esac
fi

export HERDR_SESSION_NAME="$(resolve_herdr_session "$SESSION_NAME" "$WORKSPACE")"
export MAM_WS_LABEL="${HERDR_WORKSPACE_OPT:-$(resolve_herdr_workspace "$SESSION_NAME" "$WORKSPACE")}"

# 2. If herdr is alive, attach. Done.
if herdr has-session -t "$SESSION_NAME" 2>/dev/null; then
  echo "herdr '$SESSION_NAME' already running. Attaching..."
  exec herdr agent attach "$SESSION_NAME"
fi

# 3. Determine CMD_FULL
# For claude: if assigned UUID transcript .jsonl is unmaterialized on disk, use --session-id <uuid>; otherwise use -r <uuid>
case "$AGENT" in
  claude)   CMD_FULL="claude --dangerously-skip-permissions $CLAUDE_ID_FLAG $UUID" ;;
  agy)      CMD_FULL="agy --dangerously-skip-permissions --conversation $UUID" ;;
  hermes)   CMD_FULL="hermes --resume $UUID --no-restore-cwd --yolo --accept-hooks" ;;
  grok)     CMD_FULL="grok --resume $UUID --permission-mode bypassPermissions" ;;
  opencode) CMD_FULL="opencode --session $UUID --auto --agent build" ;;
  codex)    CMD_FULL="codex resume --dangerously-bypass-approvals-and-sandbox $UUID" ;;
esac

# 4. Spawn new herdr session + run agent with the saved id
case "$AGENT" in
  claude)
    START_CMD="herdr new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"$CMD_FULL\""
    eval "$START_CMD"
    # auto-handle trust / bypass dialogs
    handle_startup_dialogs "$SESSION_NAME" 20
    ;;
  agy|hermes|grok|opencode|codex)
    eval "herdr new-session -d -s \"$SESSION_NAME\" -x 140 -y 40 -c \"$WORKSPACE\" \"$CMD_FULL\""
    ;;
esac

# 4. Update agent-sessions.yaml: status running, last_visible_status
# (Also automatically publishes a `progress --detail "resumed"` event to the multi-agent-mux-delegate-job registry if a delegate_job_id exists)
bash .agents/skills/multi-agent-mux-resume/scripts/update_yaml_resumed.sh \
  --session "$SESSION_NAME" --uuid "$UUID"

# 5. Attach (real native command — "attach" isn't in the lib.sh tmux-compat shim,
#    and real herdr has no `-t` flag here, only a positional target)
herdr agent attach "$SESSION_NAME"

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. The script above re-passes them.
  • 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

# 1. herdr alive with the right cmd (real native command, no lib.sh needed)
herdr 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 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 here are tmux-compat pseudo-commands that only work after source .agents/skills/lib.sh (as done in Workflow above) — the real herdr binary doesn't have those subcommands. This block uses the real herdr agent get/herdr agent read equivalents instead so it also 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 agent attach <name> (no skill needed)