@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.
Files changed (249) hide show
  1. package/CHANGELOG.md +257 -0
  2. package/LICENSE +21 -0
  3. package/README.md +493 -0
  4. package/bin/claudemux +6 -0
  5. package/dist/agents/claude.d.ts +3 -0
  6. package/dist/agents/claude.d.ts.map +1 -0
  7. package/dist/agents/claude.js +585 -0
  8. package/dist/agents/claude.js.map +1 -0
  9. package/dist/agents/index.d.ts +2 -0
  10. package/dist/agents/index.d.ts.map +1 -0
  11. package/dist/agents/index.js +2 -0
  12. package/dist/agents/index.js.map +1 -0
  13. package/dist/agents/types.d.ts +252 -0
  14. package/dist/agents/types.d.ts.map +1 -0
  15. package/dist/agents/types.js +2 -0
  16. package/dist/agents/types.js.map +1 -0
  17. package/dist/backends/tmux/capture.d.ts +25 -0
  18. package/dist/backends/tmux/capture.d.ts.map +1 -0
  19. package/dist/backends/tmux/capture.js +35 -0
  20. package/dist/backends/tmux/capture.js.map +1 -0
  21. package/dist/backends/tmux/exec.d.ts +105 -0
  22. package/dist/backends/tmux/exec.d.ts.map +1 -0
  23. package/dist/backends/tmux/exec.js +226 -0
  24. package/dist/backends/tmux/exec.js.map +1 -0
  25. package/dist/backends/tmux/index.d.ts +22 -0
  26. package/dist/backends/tmux/index.d.ts.map +1 -0
  27. package/dist/backends/tmux/index.js +108 -0
  28. package/dist/backends/tmux/index.js.map +1 -0
  29. package/dist/backends/tmux/keys.d.ts +38 -0
  30. package/dist/backends/tmux/keys.d.ts.map +1 -0
  31. package/dist/backends/tmux/keys.js +63 -0
  32. package/dist/backends/tmux/keys.js.map +1 -0
  33. package/dist/backends/tmux/options.d.ts +24 -0
  34. package/dist/backends/tmux/options.d.ts.map +1 -0
  35. package/dist/backends/tmux/options.js +84 -0
  36. package/dist/backends/tmux/options.js.map +1 -0
  37. package/dist/backends/tmux/sessions.d.ts +70 -0
  38. package/dist/backends/tmux/sessions.d.ts.map +1 -0
  39. package/dist/backends/tmux/sessions.js +156 -0
  40. package/dist/backends/tmux/sessions.js.map +1 -0
  41. package/dist/backends/tmux/socket.d.ts +26 -0
  42. package/dist/backends/tmux/socket.d.ts.map +1 -0
  43. package/dist/backends/tmux/socket.js +31 -0
  44. package/dist/backends/tmux/socket.js.map +1 -0
  45. package/dist/backends/types.d.ts +110 -0
  46. package/dist/backends/types.d.ts.map +1 -0
  47. package/dist/backends/types.js +24 -0
  48. package/dist/backends/types.js.map +1 -0
  49. package/dist/cli/ask.d.ts +11 -0
  50. package/dist/cli/ask.d.ts.map +1 -0
  51. package/dist/cli/ask.js +17 -0
  52. package/dist/cli/ask.js.map +1 -0
  53. package/dist/cli/capture.d.ts +8 -0
  54. package/dist/cli/capture.d.ts.map +1 -0
  55. package/dist/cli/capture.js +15 -0
  56. package/dist/cli/capture.js.map +1 -0
  57. package/dist/cli/context.d.ts +71 -0
  58. package/dist/cli/context.d.ts.map +1 -0
  59. package/dist/cli/context.js +82 -0
  60. package/dist/cli/context.js.map +1 -0
  61. package/dist/cli/exists.d.ts +7 -0
  62. package/dist/cli/exists.d.ts.map +1 -0
  63. package/dist/cli/exists.js +16 -0
  64. package/dist/cli/exists.js.map +1 -0
  65. package/dist/cli/interrupt.d.ts +10 -0
  66. package/dist/cli/interrupt.d.ts.map +1 -0
  67. package/dist/cli/interrupt.js +13 -0
  68. package/dist/cli/interrupt.js.map +1 -0
  69. package/dist/cli/kill.d.ts +7 -0
  70. package/dist/cli/kill.d.ts.map +1 -0
  71. package/dist/cli/kill.js +14 -0
  72. package/dist/cli/kill.js.map +1 -0
  73. package/dist/cli/list.d.ts +10 -0
  74. package/dist/cli/list.d.ts.map +1 -0
  75. package/dist/cli/list.js +19 -0
  76. package/dist/cli/list.js.map +1 -0
  77. package/dist/cli/main.d.ts +13 -0
  78. package/dist/cli/main.d.ts.map +1 -0
  79. package/dist/cli/main.js +143 -0
  80. package/dist/cli/main.js.map +1 -0
  81. package/dist/cli/messages.d.ts +9 -0
  82. package/dist/cli/messages.d.ts.map +1 -0
  83. package/dist/cli/messages.js +13 -0
  84. package/dist/cli/messages.js.map +1 -0
  85. package/dist/cli/respond.d.ts +10 -0
  86. package/dist/cli/respond.d.ts.map +1 -0
  87. package/dist/cli/respond.js +23 -0
  88. package/dist/cli/respond.js.map +1 -0
  89. package/dist/cli/resume.d.ts +12 -0
  90. package/dist/cli/resume.d.ts.map +1 -0
  91. package/dist/cli/resume.js +21 -0
  92. package/dist/cli/resume.js.map +1 -0
  93. package/dist/cli/send.d.ts +9 -0
  94. package/dist/cli/send.d.ts.map +1 -0
  95. package/dist/cli/send.js +16 -0
  96. package/dist/cli/send.js.map +1 -0
  97. package/dist/cli/spawn.d.ts +14 -0
  98. package/dist/cli/spawn.d.ts.map +1 -0
  99. package/dist/cli/spawn.js +21 -0
  100. package/dist/cli/spawn.js.map +1 -0
  101. package/dist/cli/state.d.ts +7 -0
  102. package/dist/cli/state.d.ts.map +1 -0
  103. package/dist/cli/state.js +11 -0
  104. package/dist/cli/state.js.map +1 -0
  105. package/dist/cli/turn-complete.d.ts +8 -0
  106. package/dist/cli/turn-complete.d.ts.map +1 -0
  107. package/dist/cli/turn-complete.js +14 -0
  108. package/dist/cli/turn-complete.js.map +1 -0
  109. package/dist/cli/wait.d.ts +13 -0
  110. package/dist/cli/wait.d.ts.map +1 -0
  111. package/dist/cli/wait.js +17 -0
  112. package/dist/cli/wait.js.map +1 -0
  113. package/dist/compose.d.ts +81 -0
  114. package/dist/compose.d.ts.map +1 -0
  115. package/dist/compose.js +64 -0
  116. package/dist/compose.js.map +1 -0
  117. package/dist/errors.d.ts +250 -0
  118. package/dist/errors.d.ts.map +1 -0
  119. package/dist/errors.js +300 -0
  120. package/dist/errors.js.map +1 -0
  121. package/dist/index.d.ts +22 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +17 -0
  124. package/dist/index.js.map +1 -0
  125. package/dist/io/baseline.d.ts +53 -0
  126. package/dist/io/baseline.d.ts.map +1 -0
  127. package/dist/io/baseline.js +97 -0
  128. package/dist/io/baseline.js.map +1 -0
  129. package/dist/io/capture.d.ts +15 -0
  130. package/dist/io/capture.d.ts.map +1 -0
  131. package/dist/io/capture.js +13 -0
  132. package/dist/io/capture.js.map +1 -0
  133. package/dist/io/interrupt.d.ts +46 -0
  134. package/dist/io/interrupt.d.ts.map +1 -0
  135. package/dist/io/interrupt.js +60 -0
  136. package/dist/io/interrupt.js.map +1 -0
  137. package/dist/io/respond.d.ts +28 -0
  138. package/dist/io/respond.d.ts.map +1 -0
  139. package/dist/io/respond.js +33 -0
  140. package/dist/io/respond.js.map +1 -0
  141. package/dist/io/send.d.ts +44 -0
  142. package/dist/io/send.d.ts.map +1 -0
  143. package/dist/io/send.js +66 -0
  144. package/dist/io/send.js.map +1 -0
  145. package/dist/io/stabilize.d.ts +28 -0
  146. package/dist/io/stabilize.d.ts.map +1 -0
  147. package/dist/io/stabilize.js +20 -0
  148. package/dist/io/stabilize.js.map +1 -0
  149. package/dist/io/wait.d.ts +47 -0
  150. package/dist/io/wait.d.ts.map +1 -0
  151. package/dist/io/wait.js +117 -0
  152. package/dist/io/wait.js.map +1 -0
  153. package/dist/observe/incremental.d.ts +28 -0
  154. package/dist/observe/incremental.d.ts.map +1 -0
  155. package/dist/observe/incremental.js +57 -0
  156. package/dist/observe/incremental.js.map +1 -0
  157. package/dist/observe/observer.d.ts +86 -0
  158. package/dist/observe/observer.d.ts.map +1 -0
  159. package/dist/observe/observer.js +167 -0
  160. package/dist/observe/observer.js.map +1 -0
  161. package/dist/observe/session-observer.d.ts +49 -0
  162. package/dist/observe/session-observer.d.ts.map +1 -0
  163. package/dist/observe/session-observer.js +123 -0
  164. package/dist/observe/session-observer.js.map +1 -0
  165. package/dist/session/adopt.d.ts +52 -0
  166. package/dist/session/adopt.d.ts.map +1 -0
  167. package/dist/session/adopt.js +57 -0
  168. package/dist/session/adopt.js.map +1 -0
  169. package/dist/session/boot.d.ts +66 -0
  170. package/dist/session/boot.d.ts.map +1 -0
  171. package/dist/session/boot.js +216 -0
  172. package/dist/session/boot.js.map +1 -0
  173. package/dist/session/constants.d.ts +57 -0
  174. package/dist/session/constants.d.ts.map +1 -0
  175. package/dist/session/constants.js +54 -0
  176. package/dist/session/constants.js.map +1 -0
  177. package/dist/session/create.d.ts +88 -0
  178. package/dist/session/create.d.ts.map +1 -0
  179. package/dist/session/create.js +66 -0
  180. package/dist/session/create.js.map +1 -0
  181. package/dist/session/default-backend.d.ts +27 -0
  182. package/dist/session/default-backend.d.ts.map +1 -0
  183. package/dist/session/default-backend.js +58 -0
  184. package/dist/session/default-backend.js.map +1 -0
  185. package/dist/session/handle.d.ts +63 -0
  186. package/dist/session/handle.d.ts.map +1 -0
  187. package/dist/session/handle.js +284 -0
  188. package/dist/session/handle.js.map +1 -0
  189. package/dist/session/hooks.d.ts +37 -0
  190. package/dist/session/hooks.d.ts.map +1 -0
  191. package/dist/session/hooks.js +42 -0
  192. package/dist/session/hooks.js.map +1 -0
  193. package/dist/session/mutex.d.ts +15 -0
  194. package/dist/session/mutex.d.ts.map +1 -0
  195. package/dist/session/mutex.js +29 -0
  196. package/dist/session/mutex.js.map +1 -0
  197. package/dist/session/recover.d.ts +43 -0
  198. package/dist/session/recover.d.ts.map +1 -0
  199. package/dist/session/recover.js +45 -0
  200. package/dist/session/recover.js.map +1 -0
  201. package/dist/session/ref.d.ts +2 -0
  202. package/dist/session/ref.d.ts.map +1 -0
  203. package/dist/session/ref.js +5 -0
  204. package/dist/session/ref.js.map +1 -0
  205. package/dist/session/registry.d.ts +31 -0
  206. package/dist/session/registry.d.ts.map +1 -0
  207. package/dist/session/registry.js +32 -0
  208. package/dist/session/registry.js.map +1 -0
  209. package/dist/session/resolve.d.ts +30 -0
  210. package/dist/session/resolve.d.ts.map +1 -0
  211. package/dist/session/resolve.js +24 -0
  212. package/dist/session/resolve.js.map +1 -0
  213. package/dist/session/resume.d.ts +68 -0
  214. package/dist/session/resume.d.ts.map +1 -0
  215. package/dist/session/resume.js +54 -0
  216. package/dist/session/resume.js.map +1 -0
  217. package/dist/session/spawn-boot.d.ts +44 -0
  218. package/dist/session/spawn-boot.d.ts.map +1 -0
  219. package/dist/session/spawn-boot.js +87 -0
  220. package/dist/session/spawn-boot.js.map +1 -0
  221. package/dist/session/validate.d.ts +10 -0
  222. package/dist/session/validate.d.ts.map +1 -0
  223. package/dist/session/validate.js +0 -0
  224. package/dist/session/validate.js.map +1 -0
  225. package/dist/state/classifier.d.ts +29 -0
  226. package/dist/state/classifier.d.ts.map +1 -0
  227. package/dist/state/classifier.js +37 -0
  228. package/dist/state/classifier.js.map +1 -0
  229. package/dist/state/types.d.ts +32 -0
  230. package/dist/state/types.d.ts.map +1 -0
  231. package/dist/state/types.js +2 -0
  232. package/dist/state/types.js.map +1 -0
  233. package/dist/types.d.ts +401 -0
  234. package/dist/types.d.ts.map +1 -0
  235. package/dist/types.js +9 -0
  236. package/dist/types.js.map +1 -0
  237. package/dist/util/ansi.d.ts +14 -0
  238. package/dist/util/ansi.d.ts.map +1 -0
  239. package/dist/util/ansi.js +18 -0
  240. package/dist/util/ansi.js.map +1 -0
  241. package/dist/util/emitter.d.ts +17 -0
  242. package/dist/util/emitter.d.ts.map +1 -0
  243. package/dist/util/emitter.js +33 -0
  244. package/dist/util/emitter.js.map +1 -0
  245. package/dist/util/sleep.d.ts +8 -0
  246. package/dist/util/sleep.d.ts.map +1 -0
  247. package/dist/util/sleep.js +10 -0
  248. package/dist/util/sleep.js.map +1 -0
  249. 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.