@wastedcode/claudemux 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +257 -0
- package/LICENSE +21 -0
- package/README.md +493 -0
- package/bin/claudemux +6 -0
- package/dist/agents/claude.d.ts +3 -0
- package/dist/agents/claude.d.ts.map +1 -0
- package/dist/agents/claude.js +585 -0
- package/dist/agents/claude.js.map +1 -0
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +2 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/types.d.ts +252 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/backends/tmux/capture.d.ts +25 -0
- package/dist/backends/tmux/capture.d.ts.map +1 -0
- package/dist/backends/tmux/capture.js +35 -0
- package/dist/backends/tmux/capture.js.map +1 -0
- package/dist/backends/tmux/exec.d.ts +105 -0
- package/dist/backends/tmux/exec.d.ts.map +1 -0
- package/dist/backends/tmux/exec.js +226 -0
- package/dist/backends/tmux/exec.js.map +1 -0
- package/dist/backends/tmux/index.d.ts +22 -0
- package/dist/backends/tmux/index.d.ts.map +1 -0
- package/dist/backends/tmux/index.js +108 -0
- package/dist/backends/tmux/index.js.map +1 -0
- package/dist/backends/tmux/keys.d.ts +38 -0
- package/dist/backends/tmux/keys.d.ts.map +1 -0
- package/dist/backends/tmux/keys.js +63 -0
- package/dist/backends/tmux/keys.js.map +1 -0
- package/dist/backends/tmux/options.d.ts +24 -0
- package/dist/backends/tmux/options.d.ts.map +1 -0
- package/dist/backends/tmux/options.js +84 -0
- package/dist/backends/tmux/options.js.map +1 -0
- package/dist/backends/tmux/sessions.d.ts +70 -0
- package/dist/backends/tmux/sessions.d.ts.map +1 -0
- package/dist/backends/tmux/sessions.js +156 -0
- package/dist/backends/tmux/sessions.js.map +1 -0
- package/dist/backends/tmux/socket.d.ts +26 -0
- package/dist/backends/tmux/socket.d.ts.map +1 -0
- package/dist/backends/tmux/socket.js +31 -0
- package/dist/backends/tmux/socket.js.map +1 -0
- package/dist/backends/types.d.ts +110 -0
- package/dist/backends/types.d.ts.map +1 -0
- package/dist/backends/types.js +24 -0
- package/dist/backends/types.js.map +1 -0
- package/dist/cli/ask.d.ts +11 -0
- package/dist/cli/ask.d.ts.map +1 -0
- package/dist/cli/ask.js +17 -0
- package/dist/cli/ask.js.map +1 -0
- package/dist/cli/capture.d.ts +8 -0
- package/dist/cli/capture.d.ts.map +1 -0
- package/dist/cli/capture.js +15 -0
- package/dist/cli/capture.js.map +1 -0
- package/dist/cli/context.d.ts +71 -0
- package/dist/cli/context.d.ts.map +1 -0
- package/dist/cli/context.js +82 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/exists.d.ts +7 -0
- package/dist/cli/exists.d.ts.map +1 -0
- package/dist/cli/exists.js +16 -0
- package/dist/cli/exists.js.map +1 -0
- package/dist/cli/interrupt.d.ts +10 -0
- package/dist/cli/interrupt.d.ts.map +1 -0
- package/dist/cli/interrupt.js +13 -0
- package/dist/cli/interrupt.js.map +1 -0
- package/dist/cli/kill.d.ts +7 -0
- package/dist/cli/kill.d.ts.map +1 -0
- package/dist/cli/kill.js +14 -0
- package/dist/cli/kill.js.map +1 -0
- package/dist/cli/list.d.ts +10 -0
- package/dist/cli/list.d.ts.map +1 -0
- package/dist/cli/list.js +19 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/main.d.ts +13 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +143 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/messages.d.ts +9 -0
- package/dist/cli/messages.d.ts.map +1 -0
- package/dist/cli/messages.js +13 -0
- package/dist/cli/messages.js.map +1 -0
- package/dist/cli/respond.d.ts +10 -0
- package/dist/cli/respond.d.ts.map +1 -0
- package/dist/cli/respond.js +23 -0
- package/dist/cli/respond.js.map +1 -0
- package/dist/cli/resume.d.ts +12 -0
- package/dist/cli/resume.d.ts.map +1 -0
- package/dist/cli/resume.js +21 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/send.d.ts +9 -0
- package/dist/cli/send.d.ts.map +1 -0
- package/dist/cli/send.js +16 -0
- package/dist/cli/send.js.map +1 -0
- package/dist/cli/spawn.d.ts +14 -0
- package/dist/cli/spawn.d.ts.map +1 -0
- package/dist/cli/spawn.js +21 -0
- package/dist/cli/spawn.js.map +1 -0
- package/dist/cli/state.d.ts +7 -0
- package/dist/cli/state.d.ts.map +1 -0
- package/dist/cli/state.js +11 -0
- package/dist/cli/state.js.map +1 -0
- package/dist/cli/turn-complete.d.ts +8 -0
- package/dist/cli/turn-complete.d.ts.map +1 -0
- package/dist/cli/turn-complete.js +14 -0
- package/dist/cli/turn-complete.js.map +1 -0
- package/dist/cli/wait.d.ts +13 -0
- package/dist/cli/wait.d.ts.map +1 -0
- package/dist/cli/wait.js +17 -0
- package/dist/cli/wait.js.map +1 -0
- package/dist/compose.d.ts +81 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +64 -0
- package/dist/compose.js.map +1 -0
- package/dist/errors.d.ts +250 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +300 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/io/baseline.d.ts +53 -0
- package/dist/io/baseline.d.ts.map +1 -0
- package/dist/io/baseline.js +97 -0
- package/dist/io/baseline.js.map +1 -0
- package/dist/io/capture.d.ts +15 -0
- package/dist/io/capture.d.ts.map +1 -0
- package/dist/io/capture.js +13 -0
- package/dist/io/capture.js.map +1 -0
- package/dist/io/interrupt.d.ts +46 -0
- package/dist/io/interrupt.d.ts.map +1 -0
- package/dist/io/interrupt.js +60 -0
- package/dist/io/interrupt.js.map +1 -0
- package/dist/io/respond.d.ts +28 -0
- package/dist/io/respond.d.ts.map +1 -0
- package/dist/io/respond.js +33 -0
- package/dist/io/respond.js.map +1 -0
- package/dist/io/send.d.ts +44 -0
- package/dist/io/send.d.ts.map +1 -0
- package/dist/io/send.js +66 -0
- package/dist/io/send.js.map +1 -0
- package/dist/io/stabilize.d.ts +28 -0
- package/dist/io/stabilize.d.ts.map +1 -0
- package/dist/io/stabilize.js +20 -0
- package/dist/io/stabilize.js.map +1 -0
- package/dist/io/wait.d.ts +47 -0
- package/dist/io/wait.d.ts.map +1 -0
- package/dist/io/wait.js +117 -0
- package/dist/io/wait.js.map +1 -0
- package/dist/observe/incremental.d.ts +28 -0
- package/dist/observe/incremental.d.ts.map +1 -0
- package/dist/observe/incremental.js +57 -0
- package/dist/observe/incremental.js.map +1 -0
- package/dist/observe/observer.d.ts +86 -0
- package/dist/observe/observer.d.ts.map +1 -0
- package/dist/observe/observer.js +167 -0
- package/dist/observe/observer.js.map +1 -0
- package/dist/observe/session-observer.d.ts +49 -0
- package/dist/observe/session-observer.d.ts.map +1 -0
- package/dist/observe/session-observer.js +123 -0
- package/dist/observe/session-observer.js.map +1 -0
- package/dist/session/adopt.d.ts +52 -0
- package/dist/session/adopt.d.ts.map +1 -0
- package/dist/session/adopt.js +57 -0
- package/dist/session/adopt.js.map +1 -0
- package/dist/session/boot.d.ts +66 -0
- package/dist/session/boot.d.ts.map +1 -0
- package/dist/session/boot.js +216 -0
- package/dist/session/boot.js.map +1 -0
- package/dist/session/constants.d.ts +57 -0
- package/dist/session/constants.d.ts.map +1 -0
- package/dist/session/constants.js +54 -0
- package/dist/session/constants.js.map +1 -0
- package/dist/session/create.d.ts +88 -0
- package/dist/session/create.d.ts.map +1 -0
- package/dist/session/create.js +66 -0
- package/dist/session/create.js.map +1 -0
- package/dist/session/default-backend.d.ts +27 -0
- package/dist/session/default-backend.d.ts.map +1 -0
- package/dist/session/default-backend.js +58 -0
- package/dist/session/default-backend.js.map +1 -0
- package/dist/session/handle.d.ts +63 -0
- package/dist/session/handle.d.ts.map +1 -0
- package/dist/session/handle.js +284 -0
- package/dist/session/handle.js.map +1 -0
- package/dist/session/hooks.d.ts +37 -0
- package/dist/session/hooks.d.ts.map +1 -0
- package/dist/session/hooks.js +42 -0
- package/dist/session/hooks.js.map +1 -0
- package/dist/session/mutex.d.ts +15 -0
- package/dist/session/mutex.d.ts.map +1 -0
- package/dist/session/mutex.js +29 -0
- package/dist/session/mutex.js.map +1 -0
- package/dist/session/recover.d.ts +43 -0
- package/dist/session/recover.d.ts.map +1 -0
- package/dist/session/recover.js +45 -0
- package/dist/session/recover.js.map +1 -0
- package/dist/session/ref.d.ts +2 -0
- package/dist/session/ref.d.ts.map +1 -0
- package/dist/session/ref.js +5 -0
- package/dist/session/ref.js.map +1 -0
- package/dist/session/registry.d.ts +31 -0
- package/dist/session/registry.d.ts.map +1 -0
- package/dist/session/registry.js +32 -0
- package/dist/session/registry.js.map +1 -0
- package/dist/session/resolve.d.ts +30 -0
- package/dist/session/resolve.d.ts.map +1 -0
- package/dist/session/resolve.js +24 -0
- package/dist/session/resolve.js.map +1 -0
- package/dist/session/resume.d.ts +68 -0
- package/dist/session/resume.d.ts.map +1 -0
- package/dist/session/resume.js +54 -0
- package/dist/session/resume.js.map +1 -0
- package/dist/session/spawn-boot.d.ts +44 -0
- package/dist/session/spawn-boot.d.ts.map +1 -0
- package/dist/session/spawn-boot.js +87 -0
- package/dist/session/spawn-boot.js.map +1 -0
- package/dist/session/validate.d.ts +10 -0
- package/dist/session/validate.d.ts.map +1 -0
- package/dist/session/validate.js +0 -0
- package/dist/session/validate.js.map +1 -0
- package/dist/state/classifier.d.ts +29 -0
- package/dist/state/classifier.d.ts.map +1 -0
- package/dist/state/classifier.js +37 -0
- package/dist/state/classifier.js.map +1 -0
- package/dist/state/types.d.ts +32 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +2 -0
- package/dist/state/types.js.map +1 -0
- package/dist/types.d.ts +401 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/util/ansi.d.ts +14 -0
- package/dist/util/ansi.d.ts.map +1 -0
- package/dist/util/ansi.js +18 -0
- package/dist/util/ansi.js.map +1 -0
- package/dist/util/emitter.d.ts +17 -0
- package/dist/util/emitter.d.ts.map +1 -0
- package/dist/util/emitter.js +33 -0
- package/dist/util/emitter.js.map +1 -0
- package/dist/util/sleep.d.ts +8 -0
- package/dist/util/sleep.d.ts.map +1 -0
- package/dist/util/sleep.js +10 -0
- package/dist/util/sleep.js.map +1 -0
- package/package.json +50 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-06-05
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- **`wait()` owns no patience — "time is the policy's."** The library-imposed
|
|
13
|
+
`DEFAULT_WAIT_TIMEOUT_MS` (5 min) and the hard-coded `STUCK_MS` (30s idle
|
|
14
|
+
auto-give-up) are **removed** (read/write-split RFC §5: patience is the
|
|
15
|
+
consumer's, not the library's — same class as the 5.5h-deadlock anti-pattern).
|
|
16
|
+
`ReadyOpts` now exposes the consumer's two patience knobs, **both optional with
|
|
17
|
+
no default**: `maxMs` (wall-clock → `budget-exceeded{reason:"max"}`) and `idleMs`
|
|
18
|
+
(no-progress → `budget-exceeded{reason:"idle"}`; a working turn or a tool in
|
|
19
|
+
flight never trips it). With neither supplied, `wait()` blocks until a terminal
|
|
20
|
+
outcome and invents no deadline. `timeoutMs` is kept as a deprecated alias for
|
|
21
|
+
`maxMs` (so existing callers compile unchanged) — **behavior change:** a bare
|
|
22
|
+
`wait()` no longer times out at 5 min. The CLI (`wait`/`ask`), a *consumer*,
|
|
23
|
+
keeps a 300s wall-clock default of its own and gains `--idle-ms`, so shell use
|
|
24
|
+
is unaffected. Surfaced by the drift-from-vision audit (the library was owning
|
|
25
|
+
patience it shouldn't).
|
|
26
|
+
- **Canonical per-session error: `SessionGone` for every read *and* write.** A
|
|
27
|
+
session that has been reaped (crash / `kill` / backend server down) now raises
|
|
28
|
+
the **same** typed error from every per-session op — `send`, `state`, `wait`,
|
|
29
|
+
`capture`, `messagesSince`, `turnComplete`, `adopt` — via a single classifier
|
|
30
|
+
(`runForSession`). Previously a crash could surface as `SessionGone` on a write
|
|
31
|
+
but `BackendUnreachable` on a read (the read/write drift). `BackendUnreachable`
|
|
32
|
+
is now reserved for genuine backend faults (`spawn-failed` / `timeout` /
|
|
33
|
+
`no-server` at the registry layer). `kill()` still never throws it (killing a
|
|
34
|
+
gone session is success). **Behavior change** for code that branched on the old
|
|
35
|
+
split — catch `SessionGone` uniformly.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **`recover()` — the reconnect compound.** One call for daemon boot: tries
|
|
40
|
+
`adopt()` (the pane is still alive → your process restarted, not the session);
|
|
41
|
+
on `SessionGone` (the pane crashed) falls back to `resume()` in a fresh pane.
|
|
42
|
+
Returns `{ session, status }` where `status` is `"attached"` or `"resumed"` —
|
|
43
|
+
so "did it crash?" is a field, not a `try/catch` you hand-roll. The re-send
|
|
44
|
+
decision stays yours (`turnComplete(lastCursor)`). New public types
|
|
45
|
+
`RecoverResult` / `RecoverStatus`. **Additive / non-breaking.** See README
|
|
46
|
+
§"Resume vs adopt vs recover".
|
|
47
|
+
- **`TranscriptUnlocatable`** — a new typed error from `messagesSince` /
|
|
48
|
+
`turnComplete` when the transcript cannot be located (no recoverable
|
|
49
|
+
`agentSessionId` and no hook-reported path). Reads are **blind, not empty** —
|
|
50
|
+
so the substrate says so loudly rather than returning `[]` and looking like "no
|
|
51
|
+
messages." Guard with `agentSessionId !== undefined`, or persist the id. Mainly
|
|
52
|
+
hit by no-id sessions (`--fork-session`, adopt-with-cache-miss).
|
|
53
|
+
|
|
54
|
+
- **`progress().agentChannelHealthy` — a Claude-drift canary.** New boolean on
|
|
55
|
+
`Progress` (and the fused belief). `false` when EVERY observe channel comes up
|
|
56
|
+
blind at once against a non-empty pane: the classifier read no state
|
|
57
|
+
(`unknown`), no hook edges arrived, and no transcript messages parsed — the
|
|
58
|
+
signature of a Claude Code update moving its output format (idle box / hook
|
|
59
|
+
payload / record shape) out from under the parsers. Any single channel with
|
|
60
|
+
signal (a recognized state, an edge, a parsed message, a known interrupt) keeps
|
|
61
|
+
it `true`; an empty/blank pane is never judged. A point-in-time snapshot — treat
|
|
62
|
+
*persistent* `false` as "re-check your version assumptions." Distinct from
|
|
63
|
+
`hookChannelHealthy` (one channel, often legitimately off). **Additive /
|
|
64
|
+
non-breaking.** See README §5.
|
|
65
|
+
- **`send()` now recovers a lost submit (lost-Enter retry).** If the paste reaches
|
|
66
|
+
the composer but the Enter keystroke is dropped (a boot-race / timing flake), the
|
|
67
|
+
turn sits un-submitted and no user record appears. `send()` previously returned
|
|
68
|
+
`DELIVERY_UNCONFIRMED` immediately; it now owns the recovery — when its anchor
|
|
69
|
+
fails and the message wasn't queued, it re-fires Enter once (`submitOnce`, which
|
|
70
|
+
submits the existing draft and **never re-pastes**, so it can never duplicate the
|
|
71
|
+
body) and re-anchors before reporting `DELIVERY_UNCONFIRMED`. This folds the
|
|
72
|
+
consumer's hand-rolled "deliver-with-confirm" recovery into the substrate.
|
|
73
|
+
Unit-tested deterministically (a backend that drops the first Enter). **Additive
|
|
74
|
+
/ non-breaking** — the contract (real cursor | `DELIVERED_QUEUED` |
|
|
75
|
+
`DELIVERY_UNCONFIRMED`) is unchanged; failures are just rarer.
|
|
76
|
+
- **`DELIVERED_QUEUED` — send-while-busy is no longer mistaken for a lost send.**
|
|
77
|
+
A message sent into a still-working session is **queued** by claude (it shows
|
|
78
|
+
"Press up to edit queued messages") and runs after the in-flight turn — but its
|
|
79
|
+
user record doesn't flush until then, so `send()` used to return
|
|
80
|
+
`DELIVERY_UNCONFIRMED`, indistinguishable from a genuinely lost send. A consumer
|
|
81
|
+
re-sending on unconfirmed would **double-run** the queued message. `send()` now
|
|
82
|
+
returns the distinct exported `DELIVERED_QUEUED` sentinel when the agent reports
|
|
83
|
+
its queue affordance — "accepted, will run, don't re-send." Both sentinels still
|
|
84
|
+
read empty against `messagesSince`/`turnComplete` (never a whole-transcript
|
|
85
|
+
slice). The agent owns the queue-affordance vocabulary (new optional
|
|
86
|
+
`ClassifierRules.queued`, mirroring `interrupted`); the send path composes it.
|
|
87
|
+
Verified live on claude 2.1.162. **Additive / non-breaking.** See README §4.
|
|
88
|
+
|
|
89
|
+
- **`respond(choice)` + first-class permission prompts** — claudemux now detects
|
|
90
|
+
claude's mid-turn tool-approval prompt (`Do you want to <verb> <target>?` →
|
|
91
|
+
`1. Yes / 2. Yes, allow all… / 3. No`). `state()` reads `permission-prompt` and
|
|
92
|
+
`wait()` returns `{ kind: "awaiting", on: "permission-prompt" }` instead of
|
|
93
|
+
running out its budget. Answer it with `respond(choice)` —
|
|
94
|
+
`"approve"` / `"approve-for-session"` / `"deny"` (the agent owns the menu
|
|
95
|
+
option-order; you never type a digit). Mechanism, not policy: it fires the
|
|
96
|
+
keystroke and self-confirms the menu cleared before returning (so the natural
|
|
97
|
+
`respond → wait` loop is race-free), but *whether* to approve is yours —
|
|
98
|
+
claudemux never auto-answers an authority grant. New typed error
|
|
99
|
+
`PromptResponseUnsupported` (an agent that declares no menu mapping). New
|
|
100
|
+
public type `PromptChoice`. New CLI verb `claudemux respond <name> <choice>`.
|
|
101
|
+
Detection + handling shipped as **one unit** per ADR 0010; verified verbatim
|
|
102
|
+
against authenticated claude 2.1.162 (both approve and deny). **Additive /
|
|
103
|
+
non-breaking** — `permission-prompt` was already a reserved `State`/`awaiting`
|
|
104
|
+
member. See README §5.
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
|
|
108
|
+
- **Compaction-safe `messagesSince` (defense-in-depth).** Verified live that a
|
|
109
|
+
compaction boundary does *not* break the transcript's `parentUuid` chain on
|
|
110
|
+
claude 2.1.162 (it stays append-only, so a post-compaction turn still descends
|
|
111
|
+
from a pre-compaction cursor — `messagesSince`/recall hold). `descendantsOf` now
|
|
112
|
+
also classifies each message's lineage and, should a future record-format change
|
|
113
|
+
ever drop an intermediate record, falls back to file position for the
|
|
114
|
+
**orphaned** post-cursor tail — provably without re-including the prior turn's
|
|
115
|
+
late-flushed reply (which roots cleanly and is never an orphan). No behavior
|
|
116
|
+
change on current claude.
|
|
117
|
+
- **Denied tool no longer wedges `wait()` at `budget-exceeded`.** A tool the
|
|
118
|
+
consumer *denies* fires `PreToolUse` (a `tool-start` hook edge) but never
|
|
119
|
+
`PostToolUse` — so the hook-derived belief was stuck at `phase=tool`/`working`
|
|
120
|
+
forever though the turn was over, and `wait()` timed out on the stuck detector.
|
|
121
|
+
The fused belief now cross-checks the pane: when the hooks say `working` but the
|
|
122
|
+
pane has settled to a clean idle box (which a genuinely in-flight tool never
|
|
123
|
+
renders), the turn has ended. (Surfaced by the permission-prompt deny path.)
|
|
124
|
+
|
|
125
|
+
### Removed
|
|
126
|
+
|
|
127
|
+
- **`PaneDead` error class** — provably unreachable and now deleted. The backend
|
|
128
|
+
runs `remain-on-exit off`, so a dead pane is **reaped**, not left as a husk: the
|
|
129
|
+
next per-session op sees an absent session and raises `SessionGone` (see the
|
|
130
|
+
canonicalization above). ADR 0007 (pane-dead detection + signal representation)
|
|
131
|
+
is superseded. **Breaking** for any `catch (e) { if (e instanceof PaneDead) … }`
|
|
132
|
+
— switch to `SessionGone`.
|
|
133
|
+
- **`degraded` `TurnOutcome` member** — the substrate never emitted it; the union
|
|
134
|
+
is now `completed | awaiting | aborted | budget-exceeded`. Removed so the type
|
|
135
|
+
tells the truth. **Breaking** only for a `switch` that named the dead arm.
|
|
136
|
+
- **`ClientInfo`** and several internal-only exports (`resetDefaultBackendForTesting`,
|
|
137
|
+
`SERVER_OPTION_COMMANDS`, `SpawnIdentity`) — dead surface, never part of the
|
|
138
|
+
intended public API.
|
|
139
|
+
|
|
140
|
+
### Internal
|
|
141
|
+
|
|
142
|
+
- **CI is now hermetic** — `npm test` spawns no real claude (real tmux only).
|
|
143
|
+
All real-claude tests are gated out of the gate: pre-auth boot behind
|
|
144
|
+
`CLAUDEMUX_LIVE_BOOT=1`, post-auth behind `*.live.test.ts` + `CLAUDEMUX_LIVE_*`.
|
|
145
|
+
Real-claude exercise lives in `scripts/*.mjs` acceptance suites. Not
|
|
146
|
+
consumer-facing.
|
|
147
|
+
|
|
148
|
+
## [0.1.0] - 2026-06-02
|
|
149
|
+
|
|
150
|
+
### Added
|
|
151
|
+
|
|
152
|
+
- **`agentSessionId`** — every session now has a stable, opaque, backend-neutral
|
|
153
|
+
conversation id, surfaced as `readonly agentSessionId?: string` on the handle
|
|
154
|
+
(returned by `create()` and `adopt()`). `create()` mints a v4 UUID and assigns
|
|
155
|
+
it to the agent at spawn (claude's `--session-id`), so you know the id **before
|
|
156
|
+
the agent writes a byte** — no scraping, no race. Use it to **resume** a
|
|
157
|
+
conversation (rides `extraArgs`: `create({ extraArgs: ["--resume", id] })`) and
|
|
158
|
+
to **locate the transcript** (`~/.claude/projects/<cwd-slug>/<id>.jsonl`).
|
|
159
|
+
`create({ agentSessionId })` lets you choose the id for a *fresh* conversation
|
|
160
|
+
(v4-UUID-validated; caller-wins over an `extraArgs` identity flag; supplying
|
|
161
|
+
both is a fail-fast `AgentSessionIdConflict`). A chosen id that already has a
|
|
162
|
+
conversation makes the agent exit rather than silently resume → typed
|
|
163
|
+
`AgentExitedDuringBoot` (the id carried on the error), fast, never a silent
|
|
164
|
+
resume. The id is `undefined` — never fabricated — for older/non-claudemux,
|
|
165
|
+
cache-miss-on-adopt, or bare-`--resume`/`--fork-session` sessions. New typed
|
|
166
|
+
errors: `AgentExitedDuringBoot`, `InvalidAgentSessionId`, `AgentSessionIdConflict`.
|
|
167
|
+
Persist `{ name, agentSessionId }` together for restart recovery. **Additive /
|
|
168
|
+
non-breaking** — the field is optional (optional→required later stays
|
|
169
|
+
non-breaking). See README §"Session identity".
|
|
170
|
+
- **`interrupt()`** — stop a working agent. Fires a single ESC (claude's own
|
|
171
|
+
interrupt key) at the session through the per-handle mutex, same as
|
|
172
|
+
`send`/`wait`. Mechanism, not policy: ESC is sent **regardless of state** and
|
|
173
|
+
is meaningful only when the agent is `working`; on an idle claude it harmlessly
|
|
174
|
+
clears the input box, so the substrate does not guard on state. The verb does
|
|
175
|
+
exactly one thing (stop the turn) and bundles no follow-up. **Additive /
|
|
176
|
+
non-breaking.** Note: after `interrupt()`, `state()` reads `unknown` (claude
|
|
177
|
+
restores the interrupted message into the composer), so do **not**
|
|
178
|
+
`wait()`-for-idle or naively `send()` a replacement — see README
|
|
179
|
+
§"Interrupting a working agent" for the clean interrupt-and-replace recipe
|
|
180
|
+
(clear the restored composer to empty, then `send`) and the slow-abort caveat.
|
|
181
|
+
Mirrored on the CLI as `claudemux interrupt <name>`.
|
|
182
|
+
- **`adopt()`** — the public mirror of `create()`: re-attach to a session that
|
|
183
|
+
is already live but was started by another process (the daemon/process-restart
|
|
184
|
+
recovery path). Pure attach — asserts the session exists, returns a handle; no
|
|
185
|
+
spawn, no boot, no dialog dismissal. Throws `SessionGone` when the session is
|
|
186
|
+
absent (including a cleanly-down backend), symmetric with `create()`'s
|
|
187
|
+
`SessionExists`. **Additive / non-breaking** — no existing API changes. See
|
|
188
|
+
README §"Re-adopting a live session after a restart" for the A/B/C recovery
|
|
189
|
+
taxonomy, the persist-both / single-writer / trust-dialog contracts, and
|
|
190
|
+
`examples/adopt-after-restart.ts` for the runnable recovery loop.
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
|
|
194
|
+
- **`PaneDead.signal` is now the canonical signal name** (e.g. `"SIGKILL"`),
|
|
195
|
+
typed `string | undefined`, replacing the previous `number`. Signal numbers
|
|
196
|
+
are not stable across operating systems; names are, and stay backend-neutral.
|
|
197
|
+
Breaking vs v0.0.1, taken now pre-adoption. See ADR 0007.
|
|
198
|
+
|
|
199
|
+
### Fixed
|
|
200
|
+
|
|
201
|
+
- **`PaneDead` now fires on macOS.** tmux renders the dead-pane cause as a signal
|
|
202
|
+
*name* there (`Pane is dead (signal kill, …)`) but a *number* on Linux; the
|
|
203
|
+
detector required a number, so a dead pane read as alive on macOS. Detection
|
|
204
|
+
now anchors on the `Pane is dead (` annotation prefix, independent of how the
|
|
205
|
+
cause renders (`signal 9` / `signal kill` / `status N`) — no false negatives.
|
|
206
|
+
Pinned with pure unit fixtures for every rendering. See ADR 0007.
|
|
207
|
+
|
|
208
|
+
## [0.0.1] - 2026-05-28
|
|
209
|
+
|
|
210
|
+
### Added
|
|
211
|
+
|
|
212
|
+
- **Eight-verb substrate surface** for Claude Code sessions, library and CLI 1:1:
|
|
213
|
+
`create`/`spawn`, `send`, `wait`, `state`, `capture`, `kill`, `exists`, `list`.
|
|
214
|
+
- **Boot orchestrator** that dismisses the theme-picker dialog on a fresh
|
|
215
|
+
`~/.claude/` and surfaces `LoginRequired` cleanly when claude is not
|
|
216
|
+
authenticated. The substrate never auto-answers the login dialog.
|
|
217
|
+
- **State classifier** with five values — `working`, `idle`, `permission-prompt`,
|
|
218
|
+
`dialog`, `unknown`. Scans only the bottom-N pane lines so scrollback
|
|
219
|
+
false-positives are impossible by construction.
|
|
220
|
+
- **Ten typed errors** — `SessionExists`, `DialogStuck`, `ReplTimeout`,
|
|
221
|
+
`LoginRequired`, `WorkspaceUntrusted`, `PaneDead`, `SessionGone`,
|
|
222
|
+
`BackendUnreachable`, `InvalidSessionName`, `BackendError` — each extends
|
|
223
|
+
`ClaudemuxError` with an actionable, backend-neutral message (the backend's
|
|
224
|
+
vocabulary never leaks into a public error; grep-enforced in CI).
|
|
225
|
+
- **Per-session mutex** on `send`/`wait`/`state`/`capture`/`kill`. Concurrent
|
|
226
|
+
consumer calls cannot interleave bytes.
|
|
227
|
+
- **`onBackendCommand`** — the single observability primitive: one event per
|
|
228
|
+
backend call with argv, duration, exit code, and streams.
|
|
229
|
+
- **Backend-neutral public API**. The current backend is tmux; future
|
|
230
|
+
backends (node-pty, `CustomPaneBackend`, etc.) slot in without consumer
|
|
231
|
+
rewrites. Layering is grep-enforced in CI.
|
|
232
|
+
- **CLI** `claudemux` with the eight verbs above. `--help` is backend-neutral
|
|
233
|
+
by construction (grep-test in CI).
|
|
234
|
+
- **Multi-line paste** via the backend's safe paste mechanism. The `Backend`
|
|
235
|
+
interface has no `sendRawText` primitive — multi-line input cannot leak
|
|
236
|
+
around the seam.
|
|
237
|
+
- **Idempotent kill / list / exists** — kill of a missing session is
|
|
238
|
+
success; list against an empty server returns `[]`.
|
|
239
|
+
|
|
240
|
+
### Notes
|
|
241
|
+
|
|
242
|
+
- **Permission-prompt detection and handling are deferred to v0.1** (per
|
|
243
|
+
ADR 0010). `permission-prompt` is a reserved member of the public `State`
|
|
244
|
+
type that v0.0.1 does not emit: a tool-approval prompt classifies as
|
|
245
|
+
`unknown` (never `idle`), so an interactive `default`-mode session that hits
|
|
246
|
+
one elapses to `ReplTimeout`. Run unattended sessions in a non-interactive
|
|
247
|
+
permission mode — see README §5. The enumerated prompt shapes are kept in
|
|
248
|
+
`test/fixtures/` (not shipped) as the v0.1 starting point.
|
|
249
|
+
- Post-auth dialogs (workspace-trust, post-update banner) are anticipated
|
|
250
|
+
from `claude --help`; they're matched but their advancement is verified
|
|
251
|
+
at product-acceptance against authenticated claude.
|
|
252
|
+
- Windows-native is not supported. tmux is Unix-only; WSL is
|
|
253
|
+
community-contributable, undocumented by the maintainers.
|
|
254
|
+
|
|
255
|
+
[0.2.0]: https://github.com/wastedcode/claudemux/compare/v0.1.0...v0.2.0
|
|
256
|
+
[0.1.0]: https://github.com/wastedcode/claudemux/compare/v0.0.1...v0.1.0
|
|
257
|
+
[0.0.1]: https://github.com/wastedcode/claudemux/releases/tag/v0.0.1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Inder Singh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|