projmux 0.15.2 → 0.16.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/README-ko.md +3 -4
- package/README.md +3 -3
- package/docs/agent-message-replies.md +82 -2
- package/docs/ai-agent-shortcuts.md +6 -5
- package/docs/architecture.md +149 -63
- package/docs/claude-coordination-endpoints.md +192 -21
- package/docs/cli-guide.md +322 -124
- package/docs/cli.md +605 -500
- package/docs/codex-installed-compatibility.md +6 -11
- package/docs/codex-native-required-migration.md +1 -59
- package/docs/configuration.md +164 -176
- package/docs/globalization.md +11 -1
- package/docs/heterogeneous-dialogue-canary.md +8 -3
- package/docs/hooks.md +83 -32
- package/docs/keybindings.md +108 -3
- package/docs/legacy-cli-retirement.md +3 -3
- package/docs/legacy-diagnostics-inventory.md +4 -4
- package/docs/native-picker.md +3 -5
- package/docs/notify-queue.md +1 -1
- package/docs/operational-diagnostics.md +53 -31
- package/docs/pr-guideline.md +66 -22
- package/docs/release.md +97 -0
- package/docs/replacement-contract.md +66 -58
- package/docs/resource-attribution.md +2 -2
- package/docs/session-restore.md +46 -80
- package/docs/settings-ia.md +43 -20
- package/docs/statusbar.md +25 -22
- package/docs/testing.md +15 -0
- package/docs/theme-palette.md +14 -0
- package/docs/tmux-surface-inventory.md +8 -10
- package/docs/troubleshooting.md +2 -4
- package/docs/upgrading.md +142 -7
- package/docs/usage-tracking.md +56 -53
- package/package.json +5 -5
- package/docs/agent-workflow.md +0 -2123
- package/docs/codex-generation-pool.md +0 -623
- package/docs/codex-stored-qualification.md +0 -45
|
@@ -107,8 +107,11 @@ the same lease. Its address is derived from the exact `AgentRouteRef`; names,
|
|
|
107
107
|
tmux `%N`, the provider socket, and the provider token do not enter that address
|
|
108
108
|
or its protocol. Every operation revalidates Agent UID, Pane UID, activation
|
|
109
109
|
generation, provider/helper process births, registration generation, and route
|
|
110
|
-
incarnation.
|
|
111
|
-
|
|
110
|
+
incarnation. The route incarnation follows the provider conversation (the
|
|
111
|
+
Claude session), so a same-session SessionStart keeps it; the process birth
|
|
112
|
+
and registration generation checks are what fence a replaced helper. Normal
|
|
113
|
+
exit and cleanup unlink only the exact owned coord socket inode. Projmux never
|
|
114
|
+
creates a replacement provider listener or relay.
|
|
112
115
|
|
|
113
116
|
Claude ingress is immediate push. There is no receiver waiter, pending ingress
|
|
114
117
|
queue, `asyncRewake`, `begin-handoff`, or `no-waiter` state. The helper retains
|
|
@@ -127,6 +130,22 @@ failure before any byte is a non-ambiguous `provider-write-zero`; partial bytes,
|
|
|
127
130
|
a write error after bytes, or a lost helper response is ambiguous and has
|
|
128
131
|
`autoResend=false`. A complete write plus helper return is only a transport
|
|
129
132
|
handoff, not proof that Claude parsed, displayed, processed, or answered it.
|
|
133
|
+
Push content has no length cap of its own: only the 8192-byte serialized
|
|
134
|
+
auth+user frame bounds it, and a size excess ends as
|
|
135
|
+
`provider-frame-too-large: frameBytes=N limitBytes=8192` with zero provider
|
|
136
|
+
bytes; the reply tool argv body stays at 4096 bytes.
|
|
137
|
+
|
|
138
|
+
`agent message send` applies the same budget to a Claude target before
|
|
139
|
+
acceptance, for plain sends and `--reply-to` alike. The sender renders the
|
|
140
|
+
content with the helper's renderer, once with its own executable and once with
|
|
141
|
+
the fixed executable phrase, keeps the longer frame, and counts an assumed
|
|
142
|
+
48-byte auth token; it never reads a messaging token. A frame over 8192 bytes
|
|
143
|
+
exits nonzero with `provider-frame-too-large: frameBytes=N limitBytes=8192`
|
|
144
|
+
before any receipt is stored or the helper is called. Codex targets are not
|
|
145
|
+
pre-checked. A helper started before this rule still caps push content at 4096
|
|
146
|
+
bytes and fails a larger body with `provider-frame-invalid-content`; the
|
|
147
|
+
sender's receipt then names the rendered content bytes and asks to re-activate
|
|
148
|
+
(restart) the target Agent.
|
|
130
149
|
|
|
131
150
|
The frozen frame is closed to Claude Code `2.1.263`. A different provider
|
|
132
151
|
version, helper replacement, provider restart, registration replacement, or
|
|
@@ -152,10 +171,153 @@ a new explicit command. A version string supplied by the caller is never proof.
|
|
|
152
171
|
After qualification, `agent message send` persists the exact immutable broker
|
|
153
172
|
handoff before any provider byte. A missing, mismatched, or unwritable durable
|
|
154
173
|
record therefore writes zero. The pushed content is a structured
|
|
155
|
-
`projmux-coordination` object with `untrusted-coordination-only` authority
|
|
156
|
-
the
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
`projmux-coordination` object with `untrusted-coordination-only` authority,
|
|
175
|
+
the source and target `agentUID` and `provider`, `messageRef`,
|
|
176
|
+
`conversationRef`, `replyTo`, payload, a `sourceNotice`, and a `replyAction`.
|
|
177
|
+
It cannot start or steer a user turn, answer an approval, interrupt a turn,
|
|
178
|
+
execute a tool, call a connector, or write Codex app-server/model history.
|
|
179
|
+
|
|
180
|
+
The frame route is deliberately narrower than the durable one. The delivery
|
|
181
|
+
fences (`paneUID`, `activationGeneration`, `incarnation`) stay on the durable
|
|
182
|
+
envelope: no frame reader uses them, and a reply re-resolves its route from the
|
|
183
|
+
durable record, not from the frame. `sourceNotice` says the source is a claim
|
|
184
|
+
and the payload untrusted peer coordination; it stays on every frame, including
|
|
185
|
+
one whose source and target are the same Agent, because `--source` is an
|
|
186
|
+
unverified claim. That self-anchored frame has the same keys with an empty
|
|
187
|
+
`replyAction`, since there is no peer to answer. The Codex turn body follows
|
|
188
|
+
the same route, notice, and self rules.
|
|
189
|
+
|
|
190
|
+
The object also carries the integer `schemaVersion`, currently `2`. Version 2
|
|
191
|
+
narrowed the routes, shortened `sourceNotice`, and emptied a self-anchored
|
|
192
|
+
frame's `replyAction`; for an Agent frame it only removed keys and changed
|
|
193
|
+
values. Version 2 also has the operator-input variant described below, which
|
|
194
|
+
a reader tells apart by its `source` fields, not by the version. The target's
|
|
195
|
+
helper renders the frame, so a helper started before an upgrade keeps sending
|
|
196
|
+
the older shape until that Agent is activated again. `schemaVersion` names
|
|
197
|
+
that object's shape only and moves independently of the durable envelope
|
|
198
|
+
version and the message store's on-disk version. A missing `schemaVersion`, or
|
|
199
|
+
an explicit `0`, reads as `1`: every frame written before the field existed has
|
|
200
|
+
that shape. A reader meeting a higher `schemaVersion` reads the fields it knows
|
|
201
|
+
and still surfaces the message; an unknown version is never a drop and never an
|
|
202
|
+
error, because a peer message that silently disappears is worse than one read
|
|
203
|
+
by a slightly stale reader. The append-only eviction history log record uses
|
|
204
|
+
the same field name, the same default, and the same higher-version rule.
|
|
205
|
+
|
|
206
|
+
### Held while the target awaits its operator
|
|
207
|
+
|
|
208
|
+
A push frame reaching Claude while it shows its operator an `AskUserQuestion`
|
|
209
|
+
question, a permission dialog, or an MCP elicitation renders as pending input
|
|
210
|
+
and can push that widget off a narrow screen. So `agent message send` does not
|
|
211
|
+
call the target's helper while the target Agent's effective interaction is
|
|
212
|
+
`approval_required` or `input_required`. It keeps the accepted record in the
|
|
213
|
+
existing `held` state with reason `target-awaiting-operator`, prints
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
<messageRef> held target-awaiting-operator delivery resumes automatically when the target Agent's dialog closes; check projmux agent message status <messageRef>; do not resend
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
and exits 0. Plain sends and a Claude source's `--reply-to` behave the same.
|
|
220
|
+
A send to a target that is not blocked is also held while that target still has
|
|
221
|
+
an earlier held message, so a later message never overtakes an earlier one.
|
|
222
|
+
Codex targets are never held. An observation older than the 30-minute
|
|
223
|
+
interaction freshness window reads as `unknown` and does not block.
|
|
224
|
+
|
|
225
|
+
The hold ends when the target's interaction leaves `approval_required` or
|
|
226
|
+
`input_required`: `UserPromptSubmit`, `Stop`, `StopFailure`, and the events that
|
|
227
|
+
follow an operator answer (`PostToolUse`, `PostToolUseFailure`,
|
|
228
|
+
`PermissionDenied`, `ElicitationResult`) move it on. The last four change the
|
|
229
|
+
interaction to `in_progress` only when it was blocked; otherwise they stay quiet
|
|
230
|
+
and write nothing. That transition starts a detached
|
|
231
|
+
`projmux internal agent-message-release --agent uid:<agent>` process, which
|
|
232
|
+
holds a per-Agent lock and delivers that Agent's held messages one at a time in
|
|
233
|
+
acceptance order through the ordinary push. Before each message it reads the
|
|
234
|
+
Registry again: a removed Agent or a route that no longer accepts the message
|
|
235
|
+
makes it `stale`, a passed deadline makes it `expired`, and a target that
|
|
236
|
+
awaits its operator again is watched as below, with the rest still held behind
|
|
237
|
+
that message. Every send that writes a hold also starts the same release, so a
|
|
238
|
+
hold never depends on a hook arriving later.
|
|
239
|
+
|
|
240
|
+
A denied permission sends no hook in current Claude Code, so the release
|
|
241
|
+
watches a target that awaits its operator instead of stopping. It keeps its
|
|
242
|
+
lock and reads the Registry and the tail of the Agent's own recorded
|
|
243
|
+
`transcript_path` again at a fixed interval of at most 2 seconds, for at most
|
|
244
|
+
10 minutes or until the earliest deadline still ahead among the held messages,
|
|
245
|
+
whichever comes first. A hook that moves the interaction on lets the message go
|
|
246
|
+
out on the next read. A `turn_duration` line stamped after the blocking
|
|
247
|
+
observation, with no assistant `tool_use` after it, means that turn has ended
|
|
248
|
+
and its dialog is gone: the release records the `response_complete` interaction
|
|
249
|
+
the `Stop` hook would have written, Registry state only and only if that same
|
|
250
|
+
observation is still current, and delivers at once. A missing path, an
|
|
251
|
+
unreadable file or line, or any other doubt keeps the message held; when the
|
|
252
|
+
window ends the message stays held as below.
|
|
253
|
+
|
|
254
|
+
When the target's helper does not answer the release's route probe and the
|
|
255
|
+
target is not awaiting its operator, or when the Registry cannot be read, the
|
|
256
|
+
release cannot judge that message yet. It keeps its lock and judges the same
|
|
257
|
+
message again after 2 seconds, doubling the wait up to 30 seconds, for at most
|
|
258
|
+
10 minutes or until the earliest deadline still ahead among the held
|
|
259
|
+
messages, whichever comes first. Each attempt checks the deadline and the
|
|
260
|
+
blocking interaction again, a message whose deadline has passed becomes
|
|
261
|
+
`expired` instead of being waited for, and later messages still wait behind
|
|
262
|
+
this one. When that window ends the release stops and the message stays held;
|
|
263
|
+
it becomes `expired` at its deadline unless a later release delivers it first.
|
|
264
|
+
|
|
265
|
+
The TTL still applies: a held message is not extended, and one still held at
|
|
266
|
+
its deadline becomes `expired` with reason `deadline-expired`.
|
|
267
|
+
`agent message status <messageRef>` answers a held message from the durable
|
|
268
|
+
store, never from the target's helper, which has not seen it; it prints the
|
|
269
|
+
same held line, `expired` once the deadline has passed, and the delivered or
|
|
270
|
+
other terminal result once the release has run.
|
|
271
|
+
|
|
272
|
+
### Operator input
|
|
273
|
+
|
|
274
|
+
A durable envelope can also carry operator input: text a person wrote through
|
|
275
|
+
the projmux web client, which is not an Agent. It is represented, and every
|
|
276
|
+
reader accepts and labels it, but no command or web route creates it yet.
|
|
277
|
+
|
|
278
|
+
- **Envelope.** Operator input carries `"origin":{"kind":"operator","client":"web"}`
|
|
279
|
+
and no `source` key; its authority is
|
|
280
|
+
`{"kind":"operator","trust":"untrusted","permission":"coordination-only"}`,
|
|
281
|
+
which grants none of the turn, steer, or config permissions a person at the
|
|
282
|
+
terminal has. Its target must be a Claude Agent (refused otherwise with
|
|
283
|
+
`operator-origin-target-not-claude`), and it is never a reply. An Agent
|
|
284
|
+
message has no `origin` key at all: an absent origin is the Agent origin,
|
|
285
|
+
there is no explicit `"kind":"agent"` encoding, and an Agent envelope's bytes
|
|
286
|
+
are unchanged. A mixed shape (an origin with any source field, or no origin
|
|
287
|
+
and no valid source route) is invalid. The durable envelope `version` stays
|
|
288
|
+
`2`.
|
|
289
|
+
- **Replies.** Operator input has no Agent route to reverse, so a reply to it
|
|
290
|
+
is refused with `explicit-reply-operator-origin`, whether it comes from
|
|
291
|
+
`agent message send --reply-to`, the Claude reply tool, or the store.
|
|
292
|
+
- **Store.** The message store's on-disk version is `3` only while at least one
|
|
293
|
+
stored record is operator input; otherwise every write is version `2`,
|
|
294
|
+
including the first write after the last operator record is reclaimed. A
|
|
295
|
+
version `1` or `2` file that contains an `origin` is malformed. A Claude
|
|
296
|
+
helper started from an older build shares the store file and refuses any
|
|
297
|
+
version but `1` and `2` and any unknown field, and an activation keeps its
|
|
298
|
+
helper across an install, so this rule keeps a store of Agent messages
|
|
299
|
+
readable by those helpers, and keeps a rollback to such a build safe until
|
|
300
|
+
operator input is written.
|
|
301
|
+
- **Frame.** The operator frame's `source` is `{"kind":"operator","client":"web"}`
|
|
302
|
+
in place of an Agent route, `sourceNotice` is "Operator input that arrived
|
|
303
|
+
through the projmux web client; projmux did not verify the person.", and
|
|
304
|
+
`replyAction` is empty. The other keys are those of an Agent frame. The
|
|
305
|
+
helper proves only the target current, since there is no source route.
|
|
306
|
+
- **Readers.** The web transcript reader shows operator input as a `user`
|
|
307
|
+
turn with `via` `projmux-web` and no `from`, judged by the `source` fields
|
|
308
|
+
alone. A frame without an origin keeps its existing reading: one whose
|
|
309
|
+
source and target are the same Agent is the operator's own `user` turn.
|
|
310
|
+
`agent message status` labels operator input `source=operator (web)` in a trailing text column and prints
|
|
311
|
+
an `origin` object and no `source` in JSON; an Agent message's output is
|
|
312
|
+
unchanged. A reclaimed operator record's history line carries `origin` and
|
|
313
|
+
no `source`.
|
|
314
|
+
|
|
315
|
+
Why operator input needs a durable envelope at all: a Claude session has no
|
|
316
|
+
path for user-turn input other than typing into its terminal, and projmux
|
|
317
|
+
forbids sending keys on the Agent input path. The coordination push is the
|
|
318
|
+
only channel that reaches a Claude session without keys, so operator input
|
|
319
|
+
travels as an envelope and is labelled for what it is. Codex needs none of
|
|
320
|
+
this: it already takes web input as a native user turn.
|
|
159
321
|
|
|
160
322
|
Reply egress uses only documented official `Stop.last_assistant_message` plus
|
|
161
323
|
one delivered Projmux-owned pending record at the same boundary. Push ingress
|
|
@@ -171,9 +333,9 @@ Codex Agent self-claims it.
|
|
|
171
333
|
| Ready and idle | Immediate one-frame push; model visibility is separate evidence |
|
|
172
334
|
| Active tool/turn | Push never interrupts; next official human boundary makes reply correlation ambiguous |
|
|
173
335
|
| Provider exit or activation restart | Old generation writes and claims zero |
|
|
174
|
-
| Same-generation registration/helper replacement |
|
|
336
|
+
| 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 |
|
|
175
337
|
| Provider version replacement | Old qualification is cleared; unqualified writes zero |
|
|
176
|
-
| Codex endpoint replacement |
|
|
338
|
+
| 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 |
|
|
177
339
|
|
|
178
340
|
Message state is receipt-only. It performs no Registry Agent-interaction or
|
|
179
341
|
tmux badge write, so it cannot overwrite `in_progress`, approval-required, or
|
|
@@ -212,16 +374,21 @@ independently.
|
|
|
212
374
|
validator's verdict on preserved real frame shapes. Neither it nor L20 proves
|
|
213
375
|
installed-provider compatibility, value drift (the corpus keeps top-level key
|
|
214
376
|
shape with placeholder values), or acceptance of unknown frames and fields (the
|
|
215
|
-
closed vocabulary rejects them by design).
|
|
216
|
-
|
|
377
|
+
closed vocabulary rejects them by design). L20 replays every corpus item whose
|
|
378
|
+
verdict is `drop` through the production reply-only validator: the Claude
|
|
379
|
+
fixture reads the corpus at run time from `PROJMUX_FAKE_CLAUDE_OBSERVED_FRAMES`,
|
|
380
|
+
emits each dropped side frame with its key set unchanged after init and before
|
|
381
|
+
its startup result, and the scenario requires the emitted names to equal the
|
|
382
|
+
corpus drop set. Those frames are key-shape placeholders, not model output. L20
|
|
383
|
+
does not emit the two recorded real shapes that are rejected today:
|
|
217
384
|
`result-success-25-keys` (the result allowlist lacks `origin`) and
|
|
218
385
|
`system-init-allowlist-diff` (the init allowlist lacks `memory_paths` and
|
|
219
386
|
`terminal_slash_commands`). Shapes without preserved evidence are gaps, not
|
|
220
|
-
items: `command_lifecycle` keys
|
|
221
|
-
|
|
222
|
-
frames, the exact real init key
|
|
223
|
-
envelope keys beyond `type` and
|
|
224
|
-
inventories.
|
|
387
|
+
items, so neither the corpus nor L20 exercises them: `command_lifecycle` keys
|
|
388
|
+
and values, a non-null assistant `context_management`, `system`
|
|
389
|
+
`thinking_tokens`, hook and user `tool_result` frames, the exact real init key
|
|
390
|
+
set, every nested value, `rate_limit_event` envelope keys beyond `type` and
|
|
391
|
+
`rate_limit_info`, and the lost earlier shape inventories.
|
|
225
392
|
|
|
226
393
|
Provider sources: [SessionStart and Stop hooks](https://code.claude.com/docs/en/hooks)
|
|
227
394
|
and [cross-session messaging](https://code.claude.com/docs/en/cross-session-messaging).
|
|
@@ -229,17 +396,21 @@ and [cross-session messaging](https://code.claude.com/docs/en/cross-session-mess
|
|
|
229
396
|
Once a delivered message has unresolved reply ambiguity (an overlapping human
|
|
230
397
|
turn, multiple pending messages, expiry, or an uncertain write/reply outcome),
|
|
231
398
|
a later idle Stop cannot restore automatic reply correlation. The helper keeps
|
|
232
|
-
push ingress available but refuses automatic replies for
|
|
233
|
-
Use the documented public recovery on the same Agent UID and qualify
|
|
234
|
-
activation before resuming automatic dialogue. Source and target
|
|
235
|
-
revalidated after durable handoff and immediately before the
|
|
399
|
+
push ingress available but refuses automatic replies for the rest of its
|
|
400
|
+
lifetime. Use the documented public recovery on the same Agent UID and qualify
|
|
401
|
+
its new activation before resuming automatic dialogue. Source and target
|
|
402
|
+
routes are revalidated after durable handoff and immediately before the
|
|
403
|
+
provider write.
|
|
236
404
|
|
|
237
405
|
The final source check also asks the existing Codex broker to verify the exact
|
|
238
406
|
runtime, connection and binding lease. This read-only IPC observation binds
|
|
239
407
|
nothing and sends no provider request. An older broker that does not support
|
|
240
408
|
this observation refuses coordination; it is never restarted implicitly.
|
|
241
|
-
Helper store lock contention fails immediately
|
|
242
|
-
|
|
409
|
+
Helper store lock contention fails a reply commit immediately, before any
|
|
410
|
+
durable write. The handoff and delivery records wait up to two seconds for the
|
|
411
|
+
lock first, so a brief holder cannot report a delivered message as failed.
|
|
412
|
+
Concurrent official hooks invalidate reply correlation without waiting for
|
|
413
|
+
another hook to finish.
|
|
243
414
|
|
|
244
415
|
The live harness begins with the benign `Reply READY.` control. The later broker
|
|
245
416
|
payload contains its own exact acknowledgement request; the initial user turn
|