From f0a2103edf06433e7f3f9b3247df628400fe6ebb Mon Sep 17 00:00:00 2001 From: Godopu Date: Thu, 23 Jul 2026 22:05:29 +0900 Subject: [PATCH] refactor(isolation): simplify agent session isolation and remove legacy home-isolation helpers --- .agents/INSTALL.md | 8 +- .agents/skills/lib.sh | 191 +++--------------- .../scripts/create_session.sh | 90 +++------ .../scripts/reconcile.sh | 26 ++- .../skills/multi-agent-mux-resume/SKILL.md | 58 +----- .../scripts/resume_session.sh | 57 +----- deploy/INSTALL.md | 8 +- 7 files changed, 91 insertions(+), 347 deletions(-) diff --git a/.agents/INSTALL.md b/.agents/INSTALL.md index d75c6fb..f3069c7 100644 --- a/.agents/INSTALL.md +++ b/.agents/INSTALL.md @@ -53,10 +53,9 @@ $ bash .agents/skills/multi-agent-mux-create/scripts/create_session.sh \ --agent claude \ --role developer \ --session my-project-dev-claude \ - --isolate \ - --herdr-workspace multi-agent-mux + --herdr-server multi-agent-mux ``` -* `--isolate` 옵션을 주면 `.mam/agent_homes//` 하위에 로그인 및 설정은 유지하되 대화 내역은 격리되는 홈이 형성됩니다. +* 모든 세션은 사용자의 전역 에이전트 설정(Global Config)을 공유하며, herdr 프로세스 격리 및 대화 UUID 단위로 독립 구동됩니다. ### 2) 세션 접속 (Attach) 백그라운드에서 구동된 에이전트 TUI 화면에 들어갑니다. @@ -66,7 +65,7 @@ $ herdr session attach my-project-dev-claude * **화면 탈출**: 대화 중 세션을 유지한 채 터미널로 돌아오려면 `Ctrl + B`를 누른 뒤 `D` 키를 차례로 입력합니다. ### 3) 에이전트 상태 복원 (Resume) -세션이 중지되었거나, 호스트 재기동으로 tmux가 소멸한 경우에도 이전 대화 ID 및 격리 디렉토리를 원자적으로 이어받아 다시 기동할 수 있습니다. +세션이 중지되었거나, 호스트 재기동으로 tmux가 소멸한 경우에도 이전 대화 ID를 원자적으로 이어받아 다시 기동할 수 있습니다. ```bash # 1단계: 복원 대상 세션의 UUID 자동 조회 (DB/YAML 레지스트리 기반) $ WORKSPACE="/path/to/your/project" @@ -80,7 +79,6 @@ $ UUID=$(bash .agents/skills/multi-agent-mux-resume/scripts/resolve_session_id.s $ [ -n "$UUID" ] || { echo "[ERROR] 매칭되는 활성 세션 이력이 없습니다. create_session.sh를 통해 먼저 세션을 생성해 주세요."; exit 1; } # 2단계: 세션 재기동 (이전 대화 컨텍스트 복원 기동) -# (주의: 만약 create 시 격리(--isolate) 세션으로 생성했다면, CLAUDE_CONFIG_DIR 환경변수를 YAML에 기록된 isolation.root 경로로 지정하여 띄워야 합니다. 상세 격리 복원 커맨드는 .agents/skills/multi-agent-mux-resume/SKILL.md 문서를 필독해 주세요.) $ herdr run -d --name "$SESSION_NAME" --workspace "$WORKSPACE" -- \ "claude --dangerously-skip-permissions -r $UUID" diff --git a/.agents/skills/lib.sh b/.agents/skills/lib.sh index f8193e7..78e1dda 100644 --- a/.agents/skills/lib.sh +++ b/.agents/skills/lib.sh @@ -546,7 +546,11 @@ for s in d.get('herdr_sessions', []): if s.get('name') == name: print(s.get('herdr_workspace') or s.get('herdr_server') or 'default') sys.exit(0) -print(os.environ.get('HERDR_SERVER_NAME', 'default')) +fallback = os.environ.get('HERDR_SERVER_NAME', '') +if not fallback or fallback == 'default': + pwd = os.path.abspath(os.getcwd()) + fallback = os.path.basename(pwd) +print(fallback or 'default') " } @@ -890,13 +894,12 @@ def verify_session_uuid(ws, agent, uuid, row=None, home_dir=None, claude_dir=Non row = row or {} epoch = row.get("herdr_session_epoch", 0) cwd = row.get("pane", {}).get("cwd", "") or ws - iso = row.get("isolation", {}).get("root") if isinstance(row.get("isolation"), dict) else None if workspace_key(cwd) != workspace_key(ws): return False if agent == "claude": - base = f"{iso}/projects" if iso else c_dir + base = c_dir key = workspace_key(ws) path = f"{base}/{key}/{uuid}.jsonl" if not os.path.exists(path): @@ -917,21 +920,24 @@ def verify_session_uuid(ws, agent, uuid, row=None, home_dir=None, claude_dir=Non return False elif agent == "agy": - base = f"{iso}/.gemini/antigravity-cli/conversations" if iso else f"{home}/.gemini/antigravity-cli/conversations" + base = f"{home}/.gemini/antigravity-cli/conversations" path = f"{base}/{uuid}.db" if not os.path.exists(path): return False if epoch and os.path.getmtime(path) < epoch: return False if mode == "discover": - lc = f"{iso}/.gemini/antigravity-cli/cache/last_conversations.json" if iso else f"{home}/.gemini/antigravity-cli/cache/last_conversations.json" + lc = f"{home}/.gemini/antigravity-cli/cache/last_conversations.json" + cache_match = False if os.path.exists(lc): try: with open(lc) as f: lc_data = json.load(f) - if lc_data.get(cwd) != uuid: - return False + cache_match = (lc_data.get(cwd) == uuid) except Exception: + cache_match = False + if not cache_match: + if uuid in (row.get("_sibling_claimed_uuids") or []): return False try: conn = sqlite3.connect(path) @@ -943,7 +949,7 @@ def verify_session_uuid(ws, agent, uuid, row=None, home_dir=None, claude_dir=Non return False elif agent == "hermes": - hdb = f"{iso}/.hermes/state.db" if iso else f"{home}/.hermes/state.db" + hdb = f"{home}/.hermes/state.db" if not os.path.exists(hdb): return False if epoch and os.path.getmtime(hdb) < epoch: @@ -958,7 +964,7 @@ def verify_session_uuid(ws, agent, uuid, row=None, home_dir=None, claude_dir=Non return False elif agent == "cline": - base = f"{iso}/sessions" if iso else f"{home}/.cline/data/sessions" + base = f"{home}/.cline/data/sessions" path = f"{base}/{uuid}/{uuid}.json" if not os.path.exists(path): return False @@ -1265,169 +1271,34 @@ capture_conversation_id() { } # --------------------------------------------------------------------------- -# Session isolation (all-L2) — implementation_plan.session_isolation.md Rev.3 +# Session isolation — Universal Global Config (Option A, Job 536a6625) # -# Every isolated session gets its own state home `.mam/agent_homes//`; -# auth/config files are SYMLINKED into it (never copied — token refresh must -# converge on the real files). Levers per agent (Phase 0 measured matrix): -# claude: CLAUDE_CONFIG_DIR= conv: /projects//.jsonl -# cline: --data-dir conv: /sessions//.json -# agy: HOME= conv: /.gemini/antigravity-cli/conversations/ -# hermes: HOME= conv: /.hermes/state.db +# Config-home isolation (.mam/agent_homes//) was removed in favor of: +# 1. Universal Global Config: all agents read/write standard ~/.claude, ~/.gemini, +# ~/.hermes, ~/.cline user configuration and credential stores. +# 2. Process Isolation: each agent-workspace pair runs in its own herdr pane. +# 3. Conversation Isolation: session UUIDs discriminate conversation history. # -# provision_isolation — mkdir + seed; prints comma-joined seeded list -# isolation_lever — claude_config_dir|cline_data_dir|home -# isolation_env_prefix — "VAR= " spawn prefix ('' for cline) -# isolation_cmd_args — extra spawn CLI args ('' unless cline) +# Stubbed isolation functions kept for backward compatibility: # --------------------------------------------------------------------------- provision_isolation() { - local agent="$1" root="$2" seeded="" f base - mkdir -p "$root" - case "$agent" in - claude) - if [ -e "$HOME/.claude.json" ]; then ln -sfn "$HOME/.claude.json" "$root/.claude.json"; seeded=".claude.json"; fi - if [ -e "$HOME/.claude/.credentials.json" ]; then ln -sfn "$HOME/.claude/.credentials.json" "$root/.credentials.json"; seeded="${seeded:+$seeded,}.credentials.json"; fi - if [ -e "$HOME/.claude/settings.json" ]; then - if [ ! -e "$root/settings.json" ]; then cp -a "$HOME/.claude/settings.json" "$root/settings.json"; fi - seeded="${seeded:+$seeded,}settings.json" - fi - if [ -d "$HOME/.claude/plugins" ]; then ln -sfn "$HOME/.claude/plugins" "$root/plugins"; seeded="${seeded:+$seeded,}plugins"; fi - if [ -d "$HOME/.claude/session-env" ]; then ln -sfn "$HOME/.claude/session-env" "$root/session-env"; seeded="${seeded:+$seeded,}session-env"; fi - if [ -d "$HOME/.claude/sessions" ]; then ln -sfn "$HOME/.claude/sessions" "$root/sessions"; seeded="${seeded:+$seeded,}sessions"; fi - if [ -d "$HOME/.claude/cache" ]; then ln -sfn "$HOME/.claude/cache" "$root/cache"; seeded="${seeded:+$seeded,}cache"; fi - ;; - cline) - # CLI 가 --data-dir 를 읽을 때 최상위 루트 하위에서 설정을 찾으므로 다이렉트 맵핑 - mkdir -p "$root/settings" - for f in "$HOME/.cline/data/settings/"*; do - [ -e "$f" ] || continue - base="$(basename "$f")" - ln -sfn "$f" "$root/settings/$base"; seeded="${seeded:+$seeded,}settings/$base" - done - if [ -e "$HOME/.cline/data/globalState.json" ]; then - ln -sfn "$HOME/.cline/data/globalState.json" "$root/globalState.json"; seeded="${seeded:+$seeded,}globalState.json" - fi - # 인증과 DB 바인딩을 연계하여 Welcome Screen 튕김 방지 - mkdir -p "$root/db" - for f in "$HOME/.cline/data/db/"*; do - [ -e "$f" ] || continue - base="$(basename "$f")" - # DB 락 경쟁 크래시 방지를 위해 물리 복사(cp -p)로 직접 주입 - cp -p "$f" "$root/db/$base" - seeded="${seeded:+$seeded,}db/$base" - done - ;; - agy) - mkdir -p "$root/.gemini/antigravity-cli" - for f in oauth_creds.json google_accounts.json installation_id settings.json state.json; do - if [ -e "$HOME/.gemini/$f" ]; then ln -sfn "$HOME/.gemini/$f" "$root/.gemini/$f"; seeded="${seeded:+$seeded,}.gemini/$f"; fi - done - for f in antigravity-oauth-token installation_id settings.json conversation_summaries.db jetski_state.pbtxt; do - if [ -e "$HOME/.gemini/antigravity-cli/$f" ]; then ln -sfn "$HOME/.gemini/antigravity-cli/$f" "$root/.gemini/antigravity-cli/$f"; seeded="${seeded:+$seeded,}.gemini/antigravity-cli/$f"; fi - done - if [ -d "$HOME/.gemini/antigravity" ]; then - ln -sfn "$HOME/.gemini/antigravity" "$root/.gemini/antigravity" - seeded="${seeded:+$seeded,}.gemini/antigravity" - fi - if [ -d "$HOME/.gemini/antigravity-ide" ]; then - if [ ! -d "$root/.gemini/antigravity-ide" ]; then cp -a "$HOME/.gemini/antigravity-ide" "$root/.gemini/antigravity-ide"; fi - seeded="${seeded:+$seeded,}.gemini/antigravity-ide" - fi - if [ -d "$HOME/.gemini/config" ]; then - ln -sfn "$HOME/.gemini/config" "$root/.gemini/config" - seeded="${seeded:+$seeded,}.gemini/config" - fi - # On macOS, seed ~/Library/Keychains to allow isolated agy to query Keychain Access credentials - # Also seed Preferences and Application Support for Antigravity settings/TOS/Theme (using cp -a for write isolation) - if [ "$(uname)" = "Darwin" ]; then - mkdir -p "$root/Library" - if [ -d "$HOME/Library/Keychains" ]; then - ln -sfn "$HOME/Library/Keychains" "$root/Library/Keychains" - seeded="${seeded:+$seeded,}Library/Keychains" - fi - mkdir -p "$root/Library/Preferences" - for plist in com.google.antigravity.plist com.google.antigravity-ide.plist com.google.GeminiMacOS.plist com.google.GeminiMacOS.shareddata.plist com.google.GeminiMacOS.launcher.plist; do - if [ -f "$HOME/Library/Preferences/$plist" ]; then - if [ ! -f "$root/Library/Preferences/$plist" ]; then cp -a "$HOME/Library/Preferences/$plist" "$root/Library/Preferences/$plist"; fi - seeded="${seeded:+$seeded,}Library/Preferences/$plist" - fi - done - mkdir -p "$root/Library/Application Support" - if [ -d "$HOME/Library/Application Support/Antigravity" ]; then - if [ ! -d "$root/Library/Application Support/Antigravity" ]; then cp -a "$HOME/Library/Application Support/Antigravity" "$root/Library/Application Support/Antigravity"; fi - seeded="${seeded:+$seeded,}Library/Application Support/Antigravity" - fi - if [ -d "$HOME/Library/Application Support/Antigravity IDE" ]; then - if [ ! -d "$root/Library/Application Support/Antigravity IDE" ]; then cp -a "$HOME/Library/Application Support/Antigravity IDE" "$root/Library/Application Support/Antigravity IDE"; fi - seeded="${seeded:+$seeded,}Library/Application Support/Antigravity IDE" - fi - if [ -d "$HOME/Library/Application Support/com.google.GeminiMacOS" ]; then - if [ ! -d "$root/Library/Application Support/com.google.GeminiMacOS" ]; then cp -a "$HOME/Library/Application Support/com.google.GeminiMacOS" "$root/Library/Application Support/com.google.GeminiMacOS"; fi - seeded="${seeded:+$seeded,}Library/Application Support/com.google.GeminiMacOS" - fi - if [ -d "$HOME/Library/Group Containers/group.com.google.gemini" ]; then - mkdir -p "$root/Library/Group Containers" - ln -sfn "$HOME/Library/Group Containers/group.com.google.gemini" "$root/Library/Group Containers/group.com.google.gemini" - seeded="${seeded:+$seeded,}Library/Group Containers/group.com.google.gemini" - fi - else - # On Linux/Unix, seed XDG config and local data dirs for Antigravity settings/TOS (using cp -a for write isolation) - local xdg_config="${XDG_CONFIG_HOME:-$HOME/.config}" - local xdg_data="${XDG_DATA_HOME:-$HOME/.local/share}" - if [ -d "$xdg_config/Antigravity" ]; then - mkdir -p "$root/.config" - if [ ! -d "$root/.config/Antigravity" ]; then cp -a "$xdg_config/Antigravity" "$root/.config/Antigravity"; fi - seeded="${seeded:+$seeded,}.config/Antigravity" - elif [ -d "$xdg_config/antigravity" ]; then - mkdir -p "$root/.config" - if [ ! -d "$root/.config/antigravity" ]; then cp -a "$xdg_config/antigravity" "$root/.config/antigravity"; fi - seeded="${seeded:+$seeded,}.config/antigravity" - fi - if [ -d "$xdg_data/Antigravity" ]; then - mkdir -p "$root/.local/share" - if [ ! -d "$root/.local/share/Antigravity" ]; then cp -a "$xdg_data/Antigravity" "$root/.local/share/Antigravity"; fi - seeded="${seeded:+$seeded,}.local/share/Antigravity" - elif [ -d "$xdg_data/antigravity" ]; then - mkdir -p "$root/.local/share" - if [ ! -d "$root/.local/share/antigravity" ]; then cp -a "$xdg_data/antigravity" "$root/.local/share/antigravity"; fi - seeded="${seeded:+$seeded,}.local/share/antigravity" - fi - fi - ;; - hermes) - mkdir -p "$root/.hermes" - for f in auth.json config.yaml .env; do - if [ -e "$HOME/.hermes/$f" ]; then ln -sfn "$HOME/.hermes/$f" "$root/.hermes/$f"; seeded="${seeded:+$seeded,}.hermes/$f"; fi - done - ;; - esac - printf '%s\n' "$seeded" + local agent="$1" root="$2" + printf '' } isolation_lever() { case "$1" in - claude) echo "claude_config_dir" ;; - cline) echo "cline_data_dir" ;; - agy|hermes) echo "home" ;; - *) echo "" ;; + claude|agy|hermes|cline) echo "none" ;; + *) echo "" ;; esac } isolation_env_prefix() { - local agent="$1" root="$2" - case "$agent" in - claude) printf 'CLAUDE_CONFIG_DIR=%q ' "$root" ;; - agy|hermes) printf 'HOME=%q ' "$root" ;; - *) : ;; - esac + : } isolation_cmd_args() { - local agent="$1" root="$2" - case "$agent" in - cline) printf -- '--data-dir %q' "$root" ;; - *) : ;; - esac + : } # --------------------------------------------------------------------------- @@ -1596,11 +1467,15 @@ wait_for_tui_ready() { for i in {1..30}; do if _pane_dialog_open "$sess"; then + if printf '%s\n' "$(_pane_tail "$sess" 5)" | grep -q 'Press Enter to continue'; then + _sks_herdr send-keys -t "$sess" Enter || true + sleep 1 + fi sleep 1 continue fi local content - content=$($local_herdr capture-pane -p -t "$sess" 2>/dev/null || echo "") + content=$(_sks_herdr capture-pane -p -t "$sess" 2>/dev/null || echo "") if [ -n "$content" ]; then case "$agent" in claude) diff --git a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh index 10d5381..4085d2e 100755 --- a/.agents/skills/multi-agent-mux-create/scripts/create_session.sh +++ b/.agents/skills/multi-agent-mux-create/scripts/create_session.sh @@ -36,8 +36,8 @@ Options: --submit-job PROMPT submit a job to multi-agent-mux-delegate-job registry with the given prompt --onboard automatically submit a project alignment/orientation job to the new agent --no-onboard disable automatic onboarding job submission - --no-isolate disable state isolation (shares global configuration/history) - [default: isolated mode is always active] + --no-isolate legacy flag (no-op; all sessions use global configuration) + --isolate legacy flag (no-op; all sessions use global configuration) -h, --help this help EOF } @@ -65,8 +65,8 @@ while [ $# -gt 0 ]; do --submit-job) SUBMIT_JOB_PROMPT="$2"; shift 2 ;; --onboard) ONBOARD=1; shift ;; --no-onboard) ONBOARD=0; shift ;; - --isolate) ISOLATE=1; shift ;; # legacy compatibility - --no-isolate) ISOLATE=0; shift ;; + --isolate) echo "NOTE: --isolate/--no-isolate is a no-op — config-home isolation was removed; sessions always use global config." >&2; shift ;; # legacy compatibility + --no-isolate) echo "NOTE: --isolate/--no-isolate is a no-op — config-home isolation was removed; sessions always use global config." >&2; shift ;; -h|--help) usage; exit 0 ;; *) echo "ERROR: unknown arg: $1" >&2; usage; exit 2 ;; esac @@ -121,37 +121,14 @@ if _herdr has-session -t "$SESSION_NAME" 2>/dev/null; then exit 3 fi -# T3: 세션 격리 프로비저닝 (all-L2) — 격리 홈 생성 + auth/설정 심링크 시딩 -# (implementation_plan.session_isolation.md Rev.3 / Phase 0 실측 매트릭스 기준) -ISOLATION_UUID="" -ISOLATION_ROOT="" -ISOLATION_LEVER="" -ISOLATION_SEEDED="" -if [ "$ISOLATE" = "1" ]; then - command -v uuidgen >/dev/null || { echo "ERROR: uuidgen not found (required for --isolate)" >&2; exit 1; } - WORKSPACE_ABS="$(cd "$WORKSPACE" && pwd)" - ISOLATION_UUID="$(uuidgen)" - ISOLATION_ROOT="$WORKSPACE_ABS/.mam/agent_homes/$ISOLATION_UUID" - ISOLATION_LEVER="$(isolation_lever "$AGENT")" - if [ "$DRY_RUN" = "1" ]; then - echo "[dry-run] would provision isolation: lever=$ISOLATION_LEVER root=$ISOLATION_ROOT" - else - ISOLATION_SEEDED="$(provision_isolation "$AGENT" "$ISOLATION_ROOT")" - echo "isolation: lever=$ISOLATION_LEVER root=$ISOLATION_ROOT" - fi -fi +# Config-home isolation was removed in favor of global config + process/UUID isolation. # herdr 세션 띄우기 LOCAL_BIN="${LOCAL_BIN:-$HOME/.local/bin}" WRAPPER="$LOCAL_BIN/$SESSION_NAME" -# cmd_full 결정 — T4: isolation 디스패치(env prefix / CLI args)를 시작 명령에 주입. -# 격리 미사용 시 기존 문자열과 byte-identical (V4 회귀 0). -ISO_ENV_PREFIX="" -ISO_CMD_ARGS="" -if [ -n "$ISOLATION_ROOT" ]; then - ISO_ENV_PREFIX="$(isolation_env_prefix "$AGENT" "$ISOLATION_ROOT")" - ISO_CMD_ARGS="$(isolation_cmd_args "$AGENT" "$ISOLATION_ROOT")" +if [ -z "${HERDR_SERVER_NAME:-}" ] || [ "$HERDR_SERVER_NAME" = "default" ]; then + export HERDR_SERVER_NAME="multi-agent-mux" fi # Resolve absolute path of the agent command to prevent herdr PATH inheritance issues (especially on macOS) @@ -161,7 +138,9 @@ if [ "$AGENT" = "cline" ]; then RESOLVED_BIN="$(command -v cline)" fi else - if command -v "$AGENT" >/dev/null 2>&1; then + if [ "$AGENT" = "claude" ] && [ -x "$HOME/.nvm/versions/node/v24.15.0/bin/claude" ]; then + RESOLVED_BIN="$HOME/.nvm/versions/node/v24.15.0/bin/claude" + elif command -v "$AGENT" >/dev/null 2>&1; then RESOLVED_BIN="$(command -v "$AGENT")" fi fi @@ -172,25 +151,27 @@ if [ "$(uname)" = "Darwin" ] && [ -f "$RESOLVED_BIN" ]; then fi case "$AGENT" in - claude) CMD_FULL="${ISO_ENV_PREFIX}${RESOLVED_BIN} --dangerously-skip-permissions" ;; - agy) CMD_FULL="${ISO_ENV_PREFIX}${RESOLVED_BIN} --dangerously-skip-permissions" ;; - hermes) CMD_FULL="${ISO_ENV_PREFIX}${RESOLVED_BIN}" ;; - cline) CMD_FULL="${RESOLVED_BIN} -i${ISO_CMD_ARGS:+ $ISO_CMD_ARGS}" ;; + claude) CMD_FULL="${RESOLVED_BIN} --dangerously-skip-permissions" ;; + agy) CMD_FULL="${RESOLVED_BIN} --dangerously-skip-permissions" ;; + hermes) CMD_FULL="${RESOLVED_BIN}" ;; + cline) CMD_FULL="${RESOLVED_BIN} -i" ;; esac spawn() { + if [ -z "${HERDR_SERVER_NAME:-}" ] || [ "$HERDR_SERVER_NAME" = "default" ]; then + export HERDR_SERVER_NAME="multi-agent-mux" + fi case "$AGENT" in claude) - # 격리 시 wrapper 경로는 env 주입을 운반하지 못하므로 인라인 spawn 강제 - if [ "$ISOLATE" != "1" ] && { { [ -x "$WRAPPER" ] && [ "$(basename "$WRAPPER")" != "claude" ]; } || [ "$USE_WRAPPER" = "1" ]; }; then + if { [ -x "$WRAPPER" ] && [ "$(basename "$WRAPPER")" != "claude" ]; } || [ "$USE_WRAPPER" = "1" ]; then nohup "$WRAPPER" >/dev/null 2>&1 & disown else - _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL" + HERDR_SERVER_NAME="$HERDR_SERVER_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL" fi ;; agy|hermes|cline) - _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL" + HERDR_SERVER_NAME="$HERDR_SERVER_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL" ;; *) echo "ERROR: --agent must be claude, agy, hermes or cline, got: $AGENT" >&2; exit 2 ;; esac @@ -209,21 +190,20 @@ cleanup_herdr_on_error() { if [ $exit_code -ne 0 ]; then echo "⚠️ Error occurred during initialization. Rolling back and killing herdr session '$SESSION_NAME'..." >&2 _herdr kill-session -t "$SESSION_NAME" 2>/dev/null || true - # T3 rollback: 이 세션용으로 프로비저닝한 격리 홈 제거 (경로 가드 후 rm) - if [ -n "$ISOLATION_ROOT" ] && [ -d "$ISOLATION_ROOT" ]; then - case "$ISOLATION_ROOT" in - */.mam/agent_homes/*) rm -rf "$ISOLATION_ROOT" ;; - esac - fi fi } trap cleanup_herdr_on_error EXIT +export HERDR_SERVER_NAME="${HERDR_SERVER_NAME:-$(resolve_herdr_server "$SESSION_NAME")}" + # TUI 준비 대기 if ! wait_for_tui_ready "$SESSION_NAME" "$AGENT"; then echo "ERROR: agent TUI never became ready — aborting (rollback via trap)" >&2 exit 1 fi +if [ "$AGENT" = "claude" ]; then + handle_startup_dialogs "$SESSION_NAME" 15 +fi # pane 메타 캡처 PANE_PID=$(_herdr list-panes -t "$SESSION_NAME" -F '#{pane_pid}' 2>/dev/null || echo "") @@ -232,11 +212,9 @@ PANE_CMD=$(_herdr list-panes -t "$SESSION_NAME" -F '#{pane_current_command}' 2>/ HERDR_EPOCH=$(date +%s) NOW_ISO=$(date -u +'%Y-%m-%dT%H:%M:%SZ') -# 시작 명령 (CMD_FULL 은 spawn 전에 isolation 디스패치를 반영해 확정됨 — T4) +# 시작 명령 # NOTE: this must match what `spawn()` actually ran above — env-var-driven -# isolation (HERDR_SERVER_NAME picked up by the lib.sh shim), not a -# `--workspace