Merge-workspace review of PR #46 (codex R1-R3 and C1, claude O1 and O4). - R1: resume never attaches, and the server it exports does not reach the calling shell; the native herdr binary does not route on environment variables. The attach advice and the Verification block now name the server: `herdr --session <server> agent attach <name>`, with <server> the row's herdr_session. - R2: --dry-run in resume and create skips the spawn and the registry update, but the checks before it still run and on macOS remove the quarantine attribute from the agent binary. The option rows say so. - R3: test_no_deleted_body_came_back compares every current block with the deleted bodies by exact sha and by a whitespace-insensitive fingerprint (stored as base_fingerprint), independently of the inventory, so a deleted block cannot return by being listed again under class c. Replaying the review's probe (the deleted resume Workflow re-added and inventoried as c) fails the new test and passes the old one. - C1 / O1: the base create call block at 263-266 (class c) folded into the Workflow call is recorded under "superseded"; the 37 base blocks are all accounted for. - O4: create exit 42 also covers MAM_KILL_ON_READY_TIMEOUT=1; stop's no-op exit 0 does not apply with --purge-conversation. - The inventory's positions and shas are regenerated for the edited blocks; every classification is unchanged. No product code changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
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.8.2 | godopu | MIT |
|
|
|
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_NAMEenv 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:
- herdr is dead, conversation lives —
agent-sessions.yamlhas the UUID. The JSONL/db is on disk. Re-spawn the herdr session + runclaude -r <id>/agy --conversation <id>. - herdr is alive but empty — You started a session with
multi-agent-mux-createbut 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*.jsonlin$CLAUDE_PROJECT_DIR/<workspace-key>/(defaults to~/.claude/projects/) for claude. - herdr is alive AND the agent inside is already running —
resume_session.shonly updates the row and exits 0. It does not attach and does not check the agent's permission flags (#45); attach withherdr --session <server> agent attach <name>, where<server>is the row'sherdr_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: because the stop command wrote the id into the row at stop
time,
resolve_session_id.shresolves it viafind_workspace_uuidtier-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.shtransitionsstopped → runningand clears the stop metadata (stopped_at,stopped_at_epoch,stop_reason,resumable) along with the usualterminated_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:
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 bymulti-agent-mux-stopright before teardown (tier-1, race-free).- 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
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, 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), agent binary missing or 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-223).
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.
--workspacediffering frompane.cwd→ exit 2 with the path to use (claude/grok used to fail with exit 1 "No saved session").pane.cwdno longer exists (worktree removed) → exit 2; recreate the worktree at that path to resume.- Rows without
pane.cwdare 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 -rrequires 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
--conversationflag name varies by version — older versions used--resumeor-r. Checkagy --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.shre-passes them through the adapterresume-spec. - Don't resume if the session is brand new and empty —
multi-agent-mux-createalready set up an empty container; sending a probe message ("init") is the right way to materialize a session id, NOTclaude -rwith a placeholder. agy --conversation <id>will fail if the conversation was deleted from disk — check~/.gemini/antigravity-cli/conversations/<uuid>.dbexists before attempting resume. If missing, the conversation is gone; you need a fresh session viamulti-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-paneare tmux-compat pseudo-commands that only work aftersource .agents/skills/lib.sh— the realherdrbinary doesn't have those subcommands. This block uses only the realherdr agent get/herdr agent readcommands, 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'sherdr_session)