Releases: wastedcode/claudemux
v0.2.4
Fixes a delivered turn surfacing as lost (DELIVERY_UNCONFIRMED / empty messagesSince) when claude's first transcript flush is late under MCP/tool load (ADR 0009).
📦 npm install @wastedcode/claudemux@0.2.4
Fixed
- A late first transcript flush under MCP/tool load no longer surfaces a delivered turn as lost. Under contention, claude defers a fresh session's first
<id>.jsonlwrite by 3–9 s — past the fixed anchor window — sosend()returned theDELIVERY_UNCONFIRMEDsentinel and a reader that sawwait()→completedstill read an emptymessagesSince("0")/turnComplete === false, even though the turn had landed. claudemux now tolerates the late flush at both seams against one shared budget: the write-side cursor anchor keeps polling while the transcript file is addressable-but-absent, and a read-side settle waits the first records out once. A present / fast-flush session is untouched (the anchor keeps its exact window; the settle returns on the first poll), and the settle is bounded on both success and exhaustion, so a never-flushing session (a crashed/slow resume) is never re-stalled. Verified live (claude 2.1.181): 1/20 → 0/20 sentinels under 20 concurrent heavy tool-MCP sessions. See ADR 0009.
Full changelog: https://github.com/wastedcode/claudemux/blob/v0.2.4/CHANGELOG.md
Compare: v0.2.3...v0.2.4
v0.2.3
Fixes empty agent transcripts when claudemux runs inside another Claude Code session (parent-agent env scrub, ADR 0008).
📦 npm install @wastedcode/claudemux@0.2.3
Fixed
- Spawned agents now persist their conversation transcript when claudemux is run from inside another Claude Code session. An inherited
CLAUDECODE/CLAUDE_CODE_*/AI_AGENTenvironment could trip claude's nested-session detection and suppress the agent's transcript — leaving claudemux, which drives agents by reading that transcript, with an empty conversation (stalled gating, emptymessagesSince, delivery never confirmed). claudemux now scrubs those variables at the pane-launch boundary (env -u … -- claude …, a true unset that survives the shared/persistent tmux server) so a spawned agent always owns its own transcript. Root-caused via A/B across claude 2.1.168–2.1.170 (not a version regression; the suppression is environment-fragile). The existingenvpassthrough remains the escape hatch. See ADR 0008.
Security
- Validate
unsetEnvnames at the backend boundary (InvalidEnvVarName). Defense-in-depth: env-var names spliced into theenv -ulaunch prefix must be POSIX identifiers. Not consumer-exploitable today (the sole producer is a trusted constant and the spawn path is shell-freeexecFileargv), but it closes the seam against any future untrusted producer.
Full changelog: https://github.com/wastedcode/claudemux/blob/v0.2.3/CHANGELOG.md
Compare: v0.2.2...v0.2.3
v0.2.2
Adds claudemux --version and resolves all open security alerts (CodeQL + Dependabot).
📦 npm install @wastedcode/claudemux@0.2.2
Added
claudemux --versionprints the package version. Previously rejected as an unknown option. Thanks to an external contributor (#7).
Security
- No consumer-facing vulnerabilities — the items below are dev/CI hardening only. The published package ships
dist/+bin/(nonode_modules), so none of these reached installs of@wastedcode/claudemux. - Upgraded vitest 2 → 4, clearing three dev-dependency advisories (vitest UI arbitrary file read/exec — the UI server is never run here; transitive vite
.mappath traversal; esbuild dev-server).npm auditis now clean. Supersedes Dependabot #10/#11. - Hardened subprocess calls in the test/dev scripts to build argv arrays via
execFileinstead of interpolating values (incl.CLAUDEMUX_SOCKET) into a shell string. Shippedsrc/was already shell-free. - CI workflow now declares least-privilege
permissions: contents: read.
Full changelog: https://github.com/wastedcode/claudemux/blob/v0.2.2/CHANGELOG.md
Compare: v0.2.1...v0.2.2
v0.2.1
Patch release. Three fixes — one correctness bug against an advertised guarantee, one CLI parity gap, one test-isolation bug.
📦 npm install @wastedcode/claudemux@0.2.1
Fixed
completednow guarantees the reply is on disk across processes. A stable idle pane can settle before the transcript flush of a large reply, so a SEPARATE process callingmessages/messagesSinceright after awaitthat reportedcompletedcould read[]whilecaptureshowed the full reply — a silent empty read for orchestrators using the structured path. The "race-free aftercompleted" guarantee held only for a single in-process observer; the CLIsend→wait→messagesflow crosses three processes whose only shared channel is the on-disk transcript.waitnow holdscompleteduntil the reply record is on disk (newest transcript message isassistant; a claude tool-result isuser-role, so a tool turn waits for its FINAL answer). No deadline is introduced — a blind transcript falls back to the pane, and a readable-but-unflushed reply is bounded by the consumer's existing patience ("time is the policy's"). In-process it's a no-op. Easiest trigger: a long reply.resumeforwards-- <agent flags>likespawn. The post---passthrough had landed onspawnonly; both boot constructors now share onewithBootOptionsbuilder so they can't drift again.- CLI subprocess tests no longer leak onto the default socket. The harness exported
TMUX_SOCKET(read by nobody — tmux uses-L, the substrate usesCLAUDEMUX_SOCKET), so harness-spawned CLI processes silently used the default socket.
Full changelog: https://github.com/wastedcode/claudemux/blob/v0.2.1/CHANGELOG.md
Compare: v0.2.0...v0.2.1