Add multi-session support: multiple agents in the same repo - #8
Conversation
Session identity was keyed by project directory (a single .claude/bridge-session pointer per repo), so a second agent session in the same repo reused the first session's ID — same-repo peers could not address each other, and one session's cleanup could destroy the other's bridge state. - New get-session-key.sh: stable per-agent-session key (BRIDGE_SESSION_KEY env override, else first non-shell ancestor PID) - register.sh: per-session pointers at .claude/bridge-sessions/<key>; optional --as <label> (defaults to git branch) stored in the manifest; legacy .claude/bridge-session no longer written - get-session-id.sh: env -> per-session pointer (walks up from cwd) -> legacy pointer -> path scan - cleanup.sh: removes only the calling session's own dir and pointer - Labels surfaced everywhere: list-peers LABEL column, message metadata fromLabel, FROM_LABEL in bridge-listen, labeled bridge-receive output, label in connect-peer output - heartbeat.sh resolves via get-session-id.sh - Docs updated (bridge.md, SKILL.md, README); tests updated for the new pointer layout; new test-same-repo.sh end-to-end coverage - Full suite: 183 tests passing (was 132)
|
Fwiw, I asked CC to compare this to the similiar PR #5 and here is what it reported: Both PRs target the same defect in PatilShreyas/claude-code-session-bridge: session identity was keyed by project directory (.claude/bridge-session), so two Claude sessions in one repo collapsed into a single bridge peer, and either one exiting destroyed the other's state. They diverge on what carries session identity. The core split
Both suites are green on my machine, and both merge cleanly. Why the mechanism choice decides it PR #5 rests on BRIDGE_SESSION_ID being "per-process, always cor runtime: across independent tool calls, BRIDGE_SESSION_ID andCLAUDE_ENV_FILE are both unset. register.sh writes the var to CLAUDE_ENV_FILE, but the plugin registers only a SessionEnd hook — there's no mechanism Two consequences, both reproduced:
session A = u9acwk ; session B = rm7vms ; pointer -> rm7vms PR #5 guards the pointer file deletion (rm -f only if it's yourSSION_DIR" above it. A exits, B dies, and A leaks.
/bridge start -> 9503k2 ; /bridge listen -> tf2x05 ; same? NO One agent, two bridges, and /bridge listen waits on an inbox nobody addresses. PR #8's key derives from process ancestry, so it needs no propagation. Same environment, two separate tool calls: key=pid76859 (the Stable, and it resolves identically inside the SessionEnd hook, #5 fails. Isolation holds: after A exits -> A dir: NO | B dir: YES <-- B survived Where PR #8 is also imperfect
Incidental win in PR #8: register.sh builds the manifest with jq -n instead of a heredoc, so project paths or names containing quotes no longer produce Reading the two PR #5 is a tight, minimal community fix — correct diagnosis, rit be per-session, not per-project), but it picked a carrier thatdoesn't survive the runtime, and its tests pass because they inject the var the real system never sets. PR #8 is the fuller treatment: it makes identity If you're deciding: PR #8 is the one that works, with the caveat that its fallback chain should probably drop the projectPath scan's destructive path — cleanup that can't positively identify its own session should e#5's most salvageable idea is its cleanup.sh ownership check,which PR #8 already incorporates for both pointer files. |
Problem
Session identity was keyed by project directory:
register.shstored the session ID in a single per-repo pointer (.claude/bridge-session), and reused it for any session registering in that repo. Consequences:get-session-id.sh's path-prefix fallback returned an arbitrary session when several shared a repo.cleanup.sh(SessionEnd hook) resolved through the shared pointer, so one session exiting couldrm -rfa same-repo peer's bridge state./bridge peers(project name only).The messaging transport itself was already per-session-ID — only the identity layer assumed one session per project.
Changes
Identity
scripts/get-session-key.sh— stable per-agent-session key:$BRIDGE_SESSION_KEYenv override, else the first non-shell ancestor process PID (the agent CLI process, stable per terminal session, distinct across sessions).register.sh— pointer files now live at.claude/bridge-sessions/<session-key>; re-registration reuses only this agent session's bridge session. The legacy.claude/bridge-sessionfile is no longer written (still honored as a read fallback inget-session-id.sh/cleanup.shfor existing installs).get-session-id.sh— resolution order: env → per-session pointer (walks up from cwd, so subdirectories work) → legacy pointer → path scan.cleanup.sh— removes only the calling session's own session dir and pointer file; a same-repo peer's state is never touched.Labels (same-repo disambiguation)
register.sh --as <label>(or$BRIDGE_LABEL); defaults to the current git branch — same-repo sessions are usually on different branches.label;list-peers.shshows a LABEL column; messages carrymetadata.fromLabel;bridge-listen.shprintsFROM_LABEL=;bridge-receive.shprintsResponse from <project> [<label>]:;connect-peer.shshows the peer's label.bridge.md/SKILL.md: peer routing is now label-first, then project name.Docs & tests
--asin the commands table, new "Multiple sessions in the same repo" section.tests/test-same-repo.shcovers the full same-repo flow (distinct registration, labeled peers, query/response round trip, stop-only-own cleanup).Backwards compatibility
.claude/bridge-sessionpointers from current installs are still read as a fallback; manifests withoutlabelrender as empty strings everywhere.Usage with this PR
/bridge peersthen shows both sessions with their labels, and the asking side routes by label when project names collide.