projmux 0.15.3 → 0.16.1

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 (38) hide show
  1. package/README-ko.md +3 -4
  2. package/README.md +3 -3
  3. package/docs/agent-message-replies.md +149 -6
  4. package/docs/ai-agent-shortcuts.md +6 -5
  5. package/docs/architecture.md +467 -66
  6. package/docs/claude-coordination-endpoints.md +192 -20
  7. package/docs/cli-guide.md +872 -148
  8. package/docs/cli.md +1459 -485
  9. package/docs/codex-installed-compatibility.md +6 -11
  10. package/docs/codex-native-required-migration.md +1 -59
  11. package/docs/configuration.md +694 -222
  12. package/docs/globalization.md +18 -5
  13. package/docs/hooks.md +529 -62
  14. package/docs/keybindings.md +74 -4
  15. package/docs/legacy-cli-retirement.md +3 -3
  16. package/docs/legacy-diagnostics-inventory.md +4 -4
  17. package/docs/native-picker.md +3 -5
  18. package/docs/notify-queue.md +1 -1
  19. package/docs/npm-distribution.md +4 -0
  20. package/docs/operational-diagnostics.md +319 -34
  21. package/docs/pr-guideline.md +66 -22
  22. package/docs/release.md +101 -0
  23. package/docs/replacement-contract.md +72 -58
  24. package/docs/repo-layout.md +3 -0
  25. package/docs/resource-attribution.md +6 -2
  26. package/docs/session-restore.md +50 -80
  27. package/docs/settings-ia.md +14 -22
  28. package/docs/statusbar.md +38 -37
  29. package/docs/testing.md +83 -18
  30. package/docs/theme-palette.md +6 -6
  31. package/docs/tmux-surface-inventory.md +21 -10
  32. package/docs/troubleshooting.md +2 -4
  33. package/docs/upgrading.md +161 -24
  34. package/docs/usage-tracking.md +40 -49
  35. package/package.json +5 -5
  36. package/docs/agent-workflow.md +0 -2596
  37. package/docs/codex-generation-pool.md +0 -623
  38. package/docs/codex-stored-qualification.md +0 -45
@@ -17,15 +17,27 @@ Managed Agent/Pane names and Claude session titles are not route authority.
17
17
  Renaming an unchanged UID/activation preserves its registration. Phase 1 does
18
18
  not discover unmanaged provider names or observe live Claude `/rename` events.
19
19
  Competing session/process/helper claims for an exact activation are refused;
20
- the exact child's next SessionStart atomically claims a new registration
21
- generation before launching its helper. Delayed admission and cleanup from an
22
- older generation cannot overwrite or clear that newer registration.
20
+ the helper launched by the exact child's next SessionStart claims a new
21
+ registration generation and records it Ready in one Registry transaction, so
22
+ no claimed-but-not-Ready registration is ever published. The claim is a
23
+ compare-and-set on the registration generation the hook observed, so delayed
24
+ admission and cleanup from an older generation cannot overwrite or clear a
25
+ newer registration.
23
26
 
24
27
  The activation gate records the actual child PID and kernel birth identity
25
28
  before replacing itself with Claude. The separate managed SessionStart hook
26
- uses `exec`, making the registered provider its direct parent. The helper
27
- verifies the complete helper → hook → provider process chain while the hook
28
- waits for a bounded startup acknowledgement. A nested unmanaged Claude cannot
29
+ uses `exec`, making the registered provider its direct parent. The hook never
30
+ takes or waits on the Registry lock: it reads the Registry lock-free, starts
31
+ the helper, and waits only for a bounded startup acknowledgement, all inside
32
+ Claude's hook timeout. The helper verifies the complete helper → hook →
33
+ provider process chain, then claims and records the registration in its own
34
+ lifetime, bounded by the Registry lock acquisition timeout. The hook's wait
35
+ bounds only the hook: when it ends without the acknowledgement, the hook
36
+ releases the helper instead of killing it, and a helper whose acknowledgement
37
+ nobody reads still records and keeps its registration. A released helper that
38
+ cannot record, or whose generation is no longer current, exits without writing
39
+ and removes its lease files by itself.
40
+ A nested unmanaged Claude cannot
29
41
  register its own endpoint through inherited activation environment variables.
30
42
  The creator-selected Registry path travels only in private Claude activation
31
43
  context, independent of a tmux server's older XDG environment.
@@ -107,8 +119,11 @@ the same lease. Its address is derived from the exact `AgentRouteRef`; names,
107
119
  tmux `%N`, the provider socket, and the provider token do not enter that address
108
120
  or its protocol. Every operation revalidates Agent UID, Pane UID, activation
109
121
  generation, provider/helper process births, registration generation, and route
110
- incarnation. Normal exit and cleanup unlink only the exact owned coord socket
111
- inode. Projmux never creates a replacement provider listener or relay.
122
+ incarnation. The route incarnation follows the provider conversation (the
123
+ Claude session), so a same-session SessionStart keeps it; the process birth
124
+ and registration generation checks are what fence a replaced helper. Normal
125
+ exit and cleanup unlink only the exact owned coord socket inode. Projmux never
126
+ creates a replacement provider listener or relay.
112
127
 
113
128
  Claude ingress is immediate push. There is no receiver waiter, pending ingress
114
129
  queue, `asyncRewake`, `begin-handoff`, or `no-waiter` state. The helper retains
@@ -168,10 +183,163 @@ a new explicit command. A version string supplied by the caller is never proof.
168
183
  After qualification, `agent message send` persists the exact immutable broker
169
184
  handoff before any provider byte. A missing, mismatched, or unwritable durable
170
185
  record therefore writes zero. The pushed content is a structured
171
- `projmux-coordination` object with `untrusted-coordination-only` authority and
172
- the exact source/target routes, `messageRef`, `conversationRef`, `replyTo`, and
173
- payload. It cannot start or steer a user turn, answer an approval, interrupt a
174
- turn, execute a tool, call a connector, or write Codex app-server/model history.
186
+ `projmux-coordination` object with `untrusted-coordination-only` authority,
187
+ the source and target `agentUID` and `provider`, `messageRef`,
188
+ `conversationRef`, `replyTo`, payload, a `sourceNotice`, and a `replyAction`.
189
+ It cannot start or steer a user turn, answer an approval, interrupt a turn,
190
+ execute a tool, call a connector, or write Codex app-server/model history.
191
+
192
+ The frame route is deliberately narrower than the durable one. The delivery
193
+ fences (`paneUID`, `activationGeneration`, `incarnation`) stay on the durable
194
+ envelope: no frame reader uses them, and a reply re-resolves its route from the
195
+ durable record, not from the frame. `sourceNotice` says the source is a claim
196
+ and the payload untrusted peer coordination; it stays on every frame, including
197
+ one whose source and target are the same Agent, because `--source` is an
198
+ unverified claim. That self-anchored frame has the same keys with an empty
199
+ `replyAction`, since there is no peer to answer. The Codex turn body follows
200
+ the same route, notice, and self rules.
201
+
202
+ The object also carries the integer `schemaVersion`, currently `2`. Version 2
203
+ narrowed the routes, shortened `sourceNotice`, and emptied a self-anchored
204
+ frame's `replyAction`; for an Agent frame it only removed keys and changed
205
+ values. Version 2 also has the operator-input variant described below, which
206
+ a reader tells apart by its `source` fields, not by the version. The target's
207
+ helper renders the frame, so a helper started before an upgrade keeps sending
208
+ the older shape until that Agent is activated again. `schemaVersion` names
209
+ that object's shape only and moves independently of the durable envelope
210
+ version and the message store's on-disk version. A missing `schemaVersion`, or
211
+ an explicit `0`, reads as `1`: every frame written before the field existed has
212
+ that shape. A reader meeting a higher `schemaVersion` reads the fields it knows
213
+ and still surfaces the message; an unknown version is never a drop and never an
214
+ error, because a peer message that silently disappears is worse than one read
215
+ by a slightly stale reader. The append-only eviction history log record uses
216
+ the same field name, the same default, and the same higher-version rule.
217
+
218
+ ### Held while the target awaits its operator
219
+
220
+ A push frame reaching Claude while it shows its operator an `AskUserQuestion`
221
+ question, a permission dialog, or an MCP elicitation renders as pending input
222
+ and can push that widget off a narrow screen. So `agent message send` does not
223
+ call the target's helper while the target Agent's effective interaction is
224
+ `approval_required` or `input_required`. It keeps the accepted record in the
225
+ existing `held` state with reason `target-awaiting-operator`, prints
226
+
227
+ ```text
228
+ <messageRef> held target-awaiting-operator delivery resumes automatically when the target Agent's dialog closes; check projmux agent message status <messageRef>; do not resend
229
+ ```
230
+
231
+ and exits 0. Plain sends and a Claude source's `--reply-to` behave the same.
232
+ A send to a target that is not blocked is also held while that target still has
233
+ an earlier held message, so a later message never overtakes an earlier one.
234
+ Codex targets are never held. An observation older than the 30-minute
235
+ interaction freshness window reads as `unknown` and does not block.
236
+
237
+ The hold ends when the target's interaction leaves `approval_required` or
238
+ `input_required`: `UserPromptSubmit`, `Stop`, `StopFailure`, and the events that
239
+ follow an operator answer (`PostToolUse`, `PostToolUseFailure`,
240
+ `PermissionDenied`, `ElicitationResult`) move it on. The last four change the
241
+ interaction to `in_progress` only when it was blocked; otherwise they stay quiet
242
+ and write nothing. That transition starts a detached
243
+ `projmux internal agent-message-release --agent uid:<agent>` process, which
244
+ holds a per-Agent lock and delivers that Agent's held messages one at a time in
245
+ acceptance order through the ordinary push. Before each message it reads the
246
+ Registry again: a removed Agent or a route that no longer accepts the message
247
+ makes it `stale`, a passed deadline makes it `expired`, and a target that
248
+ awaits its operator again is watched as below, with the rest still held behind
249
+ that message. Every send that writes a hold also starts the same release, so a
250
+ hold never depends on a hook arriving later.
251
+
252
+ A denied permission sends no hook in current Claude Code, so the release
253
+ watches a target that awaits its operator instead of stopping. It keeps its
254
+ lock and reads the Registry and the tail of the Agent's own recorded
255
+ `transcript_path` again at a fixed interval of at most 2 seconds, for at most
256
+ 10 minutes or until the earliest deadline still ahead among the held messages,
257
+ whichever comes first. A hook that moves the interaction on lets the message go
258
+ out on the next read. A `turn_duration` line stamped after the blocking
259
+ observation, with no assistant `tool_use` after it, means that turn has ended
260
+ and its dialog is gone: the release records the `response_complete` interaction
261
+ the `Stop` hook would have written, Registry state only and only if that same
262
+ observation is still current, and delivers at once. A missing path, an
263
+ unreadable file or line, or any other doubt keeps the message held; when the
264
+ window ends the message stays held as below.
265
+
266
+ When the target's helper does not answer the release's route probe and the
267
+ target is not awaiting its operator, or when the Registry cannot be read, the
268
+ release cannot judge that message yet. It keeps its lock and judges the same
269
+ message again after 2 seconds, doubling the wait up to 30 seconds, for at most
270
+ 10 minutes or until the earliest deadline still ahead among the held
271
+ messages, whichever comes first. Each attempt checks the deadline and the
272
+ blocking interaction again, a message whose deadline has passed becomes
273
+ `expired` instead of being waited for, and later messages still wait behind
274
+ this one. When that window ends the release stops and the message stays held;
275
+ it becomes `expired` at its deadline unless a later release delivers it first.
276
+
277
+ The TTL still applies: a held message is not extended, and one still held at
278
+ its deadline becomes `expired` with reason `deadline-expired`.
279
+ `agent message status <messageRef>` answers a held message from the durable
280
+ store, never from the target's helper, which has not seen it; it prints the
281
+ same held line, `expired` once the deadline has passed, and the delivered or
282
+ other terminal result once the release has run.
283
+
284
+ ### Operator input
285
+
286
+ A durable envelope can also carry operator input: text a person wrote through
287
+ an in-process projmux operator client, which is not an Agent. It is
288
+ represented, and every reader accepts and labels it, but no command creates
289
+ it: only an operator client's own in-process sender can, and an audit test pins
290
+ those senders.
291
+
292
+ The client is a name value, never a list projmux keeps: 1-32 bytes of lowercase
293
+ ASCII letters, digits, and `-`, starting with a letter (`internal/core/operatorclient`).
294
+ Operator input is `"kind":"operator"` with a client name under that rule; a
295
+ name outside it is not operator input and is refused as
296
+ `operator-client-invalid` where one is built. Records an earlier build wrote
297
+ with its fixed client name stay operator input under the same rule.
298
+
299
+ - **Envelope.** Operator input carries `"origin":{"kind":"operator","client":"<client>"}`
300
+ and no `source` key; its authority is
301
+ `{"kind":"operator","trust":"untrusted","permission":"coordination-only"}`,
302
+ which grants none of the turn, steer, or config permissions a person at the
303
+ terminal has. Its target must be a Claude Agent (refused otherwise with
304
+ `operator-origin-target-not-claude`), and it is never a reply. An Agent
305
+ message has no `origin` key at all: an absent origin is the Agent origin,
306
+ there is no explicit `"kind":"agent"` encoding, and an Agent envelope's bytes
307
+ are unchanged. A mixed shape (an origin with any source field, or no origin
308
+ and no valid source route) is invalid. The durable envelope `version` stays
309
+ `2`.
310
+ - **Replies.** Operator input has no Agent route to reverse, so a reply to it
311
+ is refused with `explicit-reply-operator-origin`, whether it comes from
312
+ `agent message send --reply-to`, the Claude reply tool, or the store.
313
+ - **Store.** The message store's on-disk version is `3` only while at least one
314
+ stored record is operator input; otherwise every write is version `2`,
315
+ including the first write after the last operator record is reclaimed. A
316
+ version `1` or `2` file that contains an `origin` is malformed. A Claude
317
+ helper started from an older build shares the store file and refuses any
318
+ version but `1` and `2` and any unknown field, and an activation keeps its
319
+ helper across an install, so this rule keeps a store of Agent messages
320
+ readable by those helpers, and keeps a rollback to such a build safe until
321
+ operator input is written.
322
+ - **Frame.** The operator frame's `source` is `{"kind":"operator","client":"<client>"}`
323
+ in place of an Agent route, `sourceNotice` is "Operator input that arrived
324
+ through the projmux <client> client; projmux did not verify the person."
325
+ with the origin's client name, and
326
+ `replyAction` is empty. The other keys are those of an Agent frame. The
327
+ helper proves only the target current, since there is no source route.
328
+ - **Readers.** The web transcript reader shows operator input as a `user`
329
+ turn with `via` `projmux-web` and no `from`, judged by the `source` fields
330
+ alone. A frame without an origin keeps its existing reading: one whose
331
+ source and target are the same Agent is the operator's own `user` turn.
332
+ `agent message status` labels operator input `source=operator (<client>)` in a trailing text column and prints
333
+ an `origin` object and no `source` in JSON; an Agent message's output is
334
+ unchanged. A reclaimed operator record's history line carries `origin` and
335
+ no `source`.
336
+
337
+ Why operator input needs a durable envelope at all: a Claude session has no
338
+ path for user-turn input other than typing into its terminal, and projmux
339
+ forbids sending keys on the Agent input path. The coordination push is the
340
+ only channel that reaches a Claude session without keys, so operator input
341
+ travels as an envelope and is labelled for what it is. Codex needs none of
342
+ this: it already takes web input as a native user turn.
175
343
 
176
344
  Reply egress uses only documented official `Stop.last_assistant_message` plus
177
345
  one delivered Projmux-owned pending record at the same boundary. Push ingress
@@ -187,9 +355,9 @@ Codex Agent self-claims it.
187
355
  | Ready and idle | Immediate one-frame push; model visibility is separate evidence |
188
356
  | Active tool/turn | Push never interrupts; next official human boundary makes reply correlation ambiguous |
189
357
  | Provider exit or activation restart | Old generation writes and claims zero |
190
- | Same-generation registration/helper replacement | Old incarnation writes zero; fresh exact-version qualification required |
358
+ | Same-generation registration/helper replacement | Incarnation unchanged within the same Claude session; the old helper still writes zero, enforced by the lease authority; fresh exact-version qualification still required |
191
359
  | Provider version replacement | Old qualification is cleared; unqualified writes zero |
192
- | Codex endpoint replacement | Old incarnation self-claim zero; exact new incarnation may claim |
360
+ | Codex endpoint replacement | Incarnation unchanged; the old endpoint's self-claim is still zero by the exact authority check; the exact new endpoint may claim |
193
361
 
194
362
  Message state is receipt-only. It performs no Registry Agent-interaction or
195
363
  tmux badge write, so it cannot overwrite `in_progress`, approval-required, or
@@ -250,17 +418,21 @@ and [cross-session messaging](https://code.claude.com/docs/en/cross-session-mess
250
418
  Once a delivered message has unresolved reply ambiguity (an overlapping human
251
419
  turn, multiple pending messages, expiry, or an uncertain write/reply outcome),
252
420
  a later idle Stop cannot restore automatic reply correlation. The helper keeps
253
- push ingress available but refuses automatic replies for that incarnation.
254
- Use the documented public recovery on the same Agent UID and qualify its new
255
- activation before resuming automatic dialogue. Source and target routes are
256
- revalidated after durable handoff and immediately before the provider write.
421
+ push ingress available but refuses automatic replies for the rest of its
422
+ lifetime. Use the documented public recovery on the same Agent UID and qualify
423
+ its new activation before resuming automatic dialogue. Source and target
424
+ routes are revalidated after durable handoff and immediately before the
425
+ provider write.
257
426
 
258
427
  The final source check also asks the existing Codex broker to verify the exact
259
428
  runtime, connection and binding lease. This read-only IPC observation binds
260
429
  nothing and sends no provider request. An older broker that does not support
261
430
  this observation refuses coordination; it is never restarted implicitly.
262
- Helper store lock contention fails immediately. Concurrent official hooks
263
- invalidate reply correlation without waiting for another hook to finish.
431
+ Helper store lock contention fails a reply commit immediately, before any
432
+ durable write. The handoff and delivery records wait up to two seconds for the
433
+ lock first, so a brief holder cannot report a delivered message as failed.
434
+ Concurrent official hooks invalidate reply correlation without waiting for
435
+ another hook to finish.
264
436
 
265
437
  The live harness begins with the benign `Reply READY.` control. The later broker
266
438
  payload contains its own exact acknowledgement request; the initial user turn