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
140 lines
11 KiB
Markdown
140 lines
11 KiB
Markdown
---
|
|
name: multi-agent-mux-resume
|
|
description: "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."
|
|
version: 4.8.2
|
|
author: godopu
|
|
license: MIT
|
|
platforms: [linux, macos]
|
|
environments: [terminal, herdr]
|
|
metadata:
|
|
hermes:
|
|
tags: [agent, herdr, claude, antigravity, agy, multi-agent, context, resume, session-id]
|
|
related_skills: [multi-agent-mux-create, multi-agent-mux-stop, multi-agent-mux-monitor, claude-code]
|
|
prereq_skills: [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](../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**: 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
|
|
|
|
```bash
|
|
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.
|
|
|
|
- `--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
|
|
|
|
```bash
|
|
# 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`)
|