projmux 0.14.2 → 0.15.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 +58 -114
- package/README.md +49 -110
- package/docs/agent-workflow.md +1420 -37
- package/docs/architecture.md +95 -70
- package/docs/assets/projmux-ai-attention-ko.gif +0 -0
- package/docs/assets/projmux-ai-attention.gif +0 -0
- package/docs/assets/projmux-overview-ko.gif +0 -0
- package/docs/assets/projmux-overview.gif +0 -0
- package/docs/assets/projmux-three-pane-workflow-ko.gif +0 -0
- package/docs/assets/projmux-three-pane-workflow.gif +0 -0
- package/docs/claude-coordination-endpoints.md +254 -0
- package/docs/cli-guide.md +283 -39
- package/docs/cli.md +2059 -56
- package/docs/codex-generation-pool.md +618 -0
- package/docs/codex-installed-compatibility.md +69 -0
- package/docs/codex-native-required-migration.md +98 -9
- package/docs/column-profiles.md +83 -0
- package/docs/configuration.md +43 -4
- package/docs/heterogeneous-dialogue-canary.md +339 -0
- package/docs/hooks.md +48 -15
- package/docs/npm-distribution.md +31 -0
- package/docs/operational-diagnostics.md +125 -0
- package/docs/pr-guideline.md +3 -2
- package/docs/replacement-contract.md +555 -0
- package/docs/session-restore.md +15 -5
- package/docs/settings-ia.md +5 -4
- package/docs/testing.md +12 -7
- package/docs/troubleshooting.md +19 -0
- package/docs/upgrading.md +48 -3
- package/npm/projmux.js +65 -0
- package/package.json +5 -5
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# Heterogeneous Dialogue Canary
|
|
2
|
+
|
|
3
|
+
This opt-in Linux canary exercises current Claude 2.1.263 and Codex 0.153.2
|
|
4
|
+
through the public reply-only activation. The required selectorless E2E remains
|
|
5
|
+
one offline L20 scenario. An actual provider run requires the roadmap owner's
|
|
6
|
+
reviewed R1 plan and explicit execution gate; these scripts do not grant it.
|
|
7
|
+
The first actual run covers qualification followed by one idle request. It does
|
|
8
|
+
not certify active-tool overlap, human overlap, recovery or installed behavior.
|
|
9
|
+
|
|
10
|
+
## Public reply-only activation
|
|
11
|
+
|
|
12
|
+
On Linux, explicitly opt one next activation into the restricted transport profile:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
projmux create agent --provider claude --dialogue-reply-only --project <project> --window <window>
|
|
16
|
+
# After normal exit, resume the same Agent UID and provider conversation:
|
|
17
|
+
projmux agent resume uid:<same-agent> --dialogue-reply-only
|
|
18
|
+
# From the exact current Codex source activation:
|
|
19
|
+
projmux agent message qualify uid:<claude-agent> --confirm-isolated-provider-push -o json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The flag changes only that activation. It does not persist an Agent default,
|
|
23
|
+
change a running Agent, or enable ordinary Claude sessions implicitly. Each new
|
|
24
|
+
activation requires a fresh opt-in and exact-version qualification. Without
|
|
25
|
+
this profile or another explicitly validated execution guard, inbound eligibility
|
|
26
|
+
remains unqualified; an ordinary `integrate`/resume alone does not create the guard.
|
|
27
|
+
|
|
28
|
+
This headless profile starts one fixed `Reply READY.` turn, enables only Bash,
|
|
29
|
+
and enforces its exact public reply command through the pinned execution gate.
|
|
30
|
+
It uses restricted mode, no Chrome or slash commands/prompt suggestions, empty
|
|
31
|
+
settings sources, strict empty MCP configuration and owned exec-form hooks.
|
|
32
|
+
The public init must confirm Bash alone, no MCP/plugins and the current version.
|
|
33
|
+
Normal SessionStart, UserPromptSubmit and Stop state callbacks retain badge
|
|
34
|
+
ownership; Stop never publishes a peer reply. Provider session persistence stays
|
|
35
|
+
on so a normal same-UID resume can retain its conversation.
|
|
36
|
+
|
|
37
|
+
The pane is an activation status/EOF surface. Typed terminal text is discarded;
|
|
38
|
+
it is **not** model-visible human input. Ctrl-D closes provider stdin and lets the
|
|
39
|
+
current turn finish before normal exit. The owned observer validates public
|
|
40
|
+
stdout/stderr in memory and forwards only bounded shape/effect assertions to
|
|
41
|
+
the existing coordination helper. Raw text, reasoning, signatures and messaging
|
|
42
|
+
credentials are never evidence files. EOF, stderr, unknown output or observer
|
|
43
|
+
replacement invalidates inbound and issued reply actions. The supervisor requires
|
|
44
|
+
exact observer birth and pidfd exit readiness before removing its private profile;
|
|
45
|
+
uncertain exit retains the profile and reports cleanup failure.
|
|
46
|
+
|
|
47
|
+
The public qualifier obtains fresh observed init evidence from that exact helper
|
|
48
|
+
when `--evidence` is omitted. A supplied evidence file cannot override a live
|
|
49
|
+
profile's missing/invalid observer. Qualification still requires the current
|
|
50
|
+
Codex source, confirmation and a broker-owned explicit reply challenge; observed
|
|
51
|
+
init alone does not admit general inbound traffic. Actual model execution,
|
|
52
|
+
human overlap, active-tool ordering and installed same-UID recovery remain
|
|
53
|
+
separate R2 evidence requirements; these deterministic tests do not prove them.
|
|
54
|
+
|
|
55
|
+
The existing helper's read-only profile evidence also reports bounded observed
|
|
56
|
+
model tool IDs, their parsed original request/target, paired result and public
|
|
57
|
+
reply ref. It separately compares the guard's exact selection and the successful
|
|
58
|
+
broker commit. An issued/consumed permit or a model result alone does not prove
|
|
59
|
+
that commit. Missing, mismatched or late observations cannot grant execution;
|
|
60
|
+
raw commands, bodies, reasoning, signatures and tickets are omitted. A currently
|
|
61
|
+
alive execution PID is only a point-in-time observation, not proof that a second
|
|
62
|
+
push overlapped an active tool. Actual model action and exact Codex claim still
|
|
63
|
+
require the revised owned runner and owner gate.
|
|
64
|
+
|
|
65
|
+
## Exact execution boundary
|
|
66
|
+
|
|
67
|
+
The owned exec-form PreToolUse hook permits only Bash's literal public
|
|
68
|
+
`<candidate> agent message send uid:<original-source> --reply-to <original-ref> -- <one text argument>`.
|
|
69
|
+
The documented [shell prefix](https://code.claude.com/docs/en/env-vars) receives
|
|
70
|
+
one opaque shell invocation. The pinned candidate extracts one canonical
|
|
71
|
+
one-use ticket and directly executes its approved argv; it never evaluates the
|
|
72
|
+
carrier. Exact provider ancestry/birth, session, route, tool ID and image remain
|
|
73
|
+
required at preparation, consumption and commit. Missing, foreign, reused or
|
|
74
|
+
changed tickets/images fail before execution. An observed tool action grants no
|
|
75
|
+
permission. Private coord v5 fences earlier helpers; the frozen provider frame
|
|
76
|
+
is unchanged.
|
|
77
|
+
|
|
78
|
+
## Genuine source transaction
|
|
79
|
+
|
|
80
|
+
The maintained setup, source action, bounded observation reader and parent
|
|
81
|
+
cleanup are connected and have offline regression coverage. The prior
|
|
82
|
+
payload-free source recipe is superseded. **Actual execution is not approved.**
|
|
83
|
+
The current runner now checks public config/read values/origins before source
|
|
84
|
+
creation and release, with offline fixtures. Actual current-version policy
|
|
85
|
+
responses and observation shapes remain unverified. Neither config generation
|
|
86
|
+
nor command-item observation proves that no earlier endpoint-startup effect
|
|
87
|
+
occurred. Those actual boundaries remain open R1 review items,
|
|
88
|
+
not completed acceptance evidence or permission to run the command below.
|
|
89
|
+
Historical failed attempts and their receipts remain unchanged.
|
|
90
|
+
|
|
91
|
+
`scripts/agent-dialogue-canary-setup.py` is the single transaction entrypoint.
|
|
92
|
+
It invokes prepare, starts the exact owned direct app-server endpoint, creates
|
|
93
|
+
the two public Agents, transfers observation to the parent runner and owns
|
|
94
|
+
partial-setup cleanup. Do not run prepare and then create actors manually, run
|
|
95
|
+
the source action from an operator shell, or invoke the run stage separately.
|
|
96
|
+
Those sequences do not establish genuine model-tool origin or the full cleanup
|
|
97
|
+
boundary.
|
|
98
|
+
|
|
99
|
+
Before owner approval, pin the reviewed clean commit and a separately built
|
|
100
|
+
0755 candidate, both provider executables, every runner/schema file, the exact
|
|
101
|
+
generated prompt/config, a fresh short root, external receipt and external audit.
|
|
102
|
+
Use the public current versions Claude 2.1.263 and Codex 0.153.2. The candidate
|
|
103
|
+
and direct Codex image must be regular, owned executable files without group or
|
|
104
|
+
world write. Authentication inputs must be explicitly selected regular files,
|
|
105
|
+
owned by the current UID with mode 0600. Pin their paths and metadata only;
|
|
106
|
+
never record their contents or hashes. Runtime UIDs, process births and native
|
|
107
|
+
thread/turn/item IDs are obtained after approved creation and frozen before the
|
|
108
|
+
first Claude push; they are not fabricated in the pre-run packet.
|
|
109
|
+
|
|
110
|
+
After that separate approval, the reviewed invocation has this form. Each
|
|
111
|
+
uppercase input below is a concrete value from the approved packet; the root,
|
|
112
|
+
receipt and audit must all be absent before invocation:
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
env -i PATH=/usr/local/bin:/usr/bin:/bin HOME="$HOME" \
|
|
116
|
+
PMX_DIALOGUE_LIVE_CANARY=1 \
|
|
117
|
+
PMX_DIALOGUE_CANDIDATE_HEAD="$REVIEWED_HEAD" \
|
|
118
|
+
PMX_DIALOGUE_PROJMUX_BIN="$CANDIDATE_BINARY" \
|
|
119
|
+
PMX_DIALOGUE_REAL_CLAUDE_BIN="$CLAUDE_BINARY" \
|
|
120
|
+
PMX_DIALOGUE_REAL_CODEX_BIN="$CODEX_BINARY" \
|
|
121
|
+
PMX_DIALOGUE_CLAUDE_CREDENTIAL_FILE="$CLAUDE_CREDENTIAL_FILE" \
|
|
122
|
+
PMX_DIALOGUE_CODEX_AUTH_FILE="$CODEX_AUTH_FILE" \
|
|
123
|
+
PMX_DIALOGUE_CANARY_ROOT="$FRESH_ROOT" \
|
|
124
|
+
PMX_DIALOGUE_CANARY_RECEIPT="$EXTERNAL_RECEIPT" \
|
|
125
|
+
PMX_DIALOGUE_MESSAGE_REF="$IDLE_MESSAGE_REF" \
|
|
126
|
+
python3 "$REVIEWED_CHECKOUT/scripts/agent-dialogue-canary-setup.py"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The audit path is exactly `${EXTERNAL_RECEIPT}.audit.jsonl`, exclusive mode
|
|
130
|
+
0600 outside the disposable root. It records bounded stage/exit/byte counts,
|
|
131
|
+
closed source freeze/completion facts and captured writer exit proof before
|
|
132
|
+
root removal. It contains no raw provider output, command, reasoning or auth
|
|
133
|
+
values. An absent success receipt is a failed/incomplete transaction even if
|
|
134
|
+
cleanup removed the root. No failure or unknown write outcome permits resend
|
|
135
|
+
or another transaction under the old approval.
|
|
136
|
+
|
|
137
|
+
## Source action and first-push proof
|
|
138
|
+
|
|
139
|
+
The parent uses the fixed public default socket constructor
|
|
140
|
+
`<root>/codex-home/app-server-control/app-server-control.sock`; its encoded path
|
|
141
|
+
must be shorter than 100 bytes. It directly launches the pinned Codex executable
|
|
142
|
+
with `app-server --listen unix://<socket>`, retains that child handle and checks
|
|
143
|
+
its UID, PID/birth, executable, argv and socket/kernel peer. It does not start
|
|
144
|
+
or stop an ambient daemon service. The existing public Project projection
|
|
145
|
+
selects the tmux session name. A separate TMUX_TMPDIR, unique `-L`, observed
|
|
146
|
+
socket_path and exact socket identity scope both actor creation and cleanup.
|
|
147
|
+
|
|
148
|
+
The existing public source create receives the genuine approved task:
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
<candidate> create agent --provider codex --project uid:<P> --window uid:<W> -o pane-id -- <one genuine source prompt argument>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The prompt is generated by `source_prompt(root)` and contains exactly the one
|
|
155
|
+
`source_command(root)` invocation:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
python3 <root>/bin/agent-dialogue-source-action.py <root>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The source model is asked to execute that command once from `<root>/work`,
|
|
162
|
+
wait for its result, then reply DONE. It is explicitly told not to execute other
|
|
163
|
+
commands, inspect authentication/config/history, create subagents, resend or
|
|
164
|
+
clean up. This is a genuine initial Codex user task, with normal private original
|
|
165
|
+
thread/tool-result writes expected. Claude's reply-only activation separately
|
|
166
|
+
starts the fixed `Reply READY.` turn. Neither startup turn is peer-to-user-turn
|
|
167
|
+
relay; the runner performs no direct peer history/input API writes.
|
|
168
|
+
|
|
169
|
+
The source action writes its independently observed process identity and waits.
|
|
170
|
+
Before release the parent verifies both runtime-first Agent/Pane chains, exact
|
|
171
|
+
current routes/composite source authority and the guarded Claude public init.
|
|
172
|
+
It freezes the actual native thread/turn/in-progress command item and separately
|
|
173
|
+
matches the action PID/birth, exact script argv and ancestry to the owned native
|
|
174
|
+
child. Inherited tmux context or a provider `processId` alone is not origin proof.
|
|
175
|
+
The source action builds its public CLI environment from the frozen own Pane,
|
|
176
|
+
server and socket; it does not use an inherited create-anchor context.
|
|
177
|
+
|
|
178
|
+
Only after release does the source action use existing public `agent message`
|
|
179
|
+
commands: qualify the exact receiver with `--confirm-isolated-provider-push`,
|
|
180
|
+
claim that reply from the original Codex inbox, send one independent idle request,
|
|
181
|
+
then claim its correlated reply. Empty follow-up claims check claim-once. Frozen
|
|
182
|
+
live routes are rechecked before dispatch. Claude tool/result/guarded-commit
|
|
183
|
+
proof is separate from full-frame delivered status. Missing or ambiguous proof
|
|
184
|
+
fails without resend. The parent never executes these coordination commands.
|
|
185
|
+
|
|
186
|
+
Only bounded correlation facts are returned to the original source model. The
|
|
187
|
+
parent then compares the completed command's closed result with the independent
|
|
188
|
+
qualification/idle proofs and separately observes original-turn completion.
|
|
189
|
+
Started-item freeze precedes the first push; completed-item proof follows the
|
|
190
|
+
result. The action never cleans up its own provider or parent, and parent cleanup
|
|
191
|
+
cannot stand in for a successfully returned source tool result.
|
|
192
|
+
|
|
193
|
+
## Observation and requested private policy
|
|
194
|
+
|
|
195
|
+
`agent-dialogue-codex-observation.py` validates the frozen public 0.153.2
|
|
196
|
+
`thread/read(includeTurns=true)` and `item/started`/`item/completed` schemas.
|
|
197
|
+
The four exports in `scripts/agent-dialogue-codex-schema/` retain their original
|
|
198
|
+
hashes, also checked by the reader. `agent-dialogue-native-source.py` connects to
|
|
199
|
+
the pinned owned endpoint and performs bounded non-experimental initialization;
|
|
200
|
+
the reader sends only `thread/read`. Neither component starts/resumes a thread,
|
|
201
|
+
subscribes a relay, sends a user turn, or copies native history to evidence.
|
|
202
|
+
|
|
203
|
+
Observation is bounded to 1 MiB per frame, 8 MiB/128 frames per reader and 32 items
|
|
204
|
+
in the sole expected turn. HTTP upgrade and initialization are each bounded to 16 KiB/five seconds;
|
|
205
|
+
read connections have a ten-second deadline. The source release wait, action
|
|
206
|
+
result wait and completed-result observation are separately bounded. Unknown
|
|
207
|
+
fields/effects, incomplete views, absent source, changed items/routes, wrong
|
|
208
|
+
results, EOF or bounds violations fail closed. Explicitly observed non-userShell
|
|
209
|
+
source enums are checked against the prepared allowlist; no schema default is
|
|
210
|
+
applied. `processId` is never treated as an OS PID. Raw text/command/output,
|
|
211
|
+
reasoning and auth data stay out of the returned facts and external audit.
|
|
212
|
+
|
|
213
|
+
The generated [public configuration](https://developers.openai.com/codex/config-reference)
|
|
214
|
+
requests file authentication, never approvals, workspace-write with the owned
|
|
215
|
+
root as an additional writable root, `/tmp` and TMPDIR expansion excluded,
|
|
216
|
+
sandbox network disabled, web search disabled and no startup update check.
|
|
217
|
+
Fresh HOME/CODEX_HOME/CODEX_SQLITE_HOME/XDG paths are passed to the direct child;
|
|
218
|
+
ambient config/history and keyring policy are not copied. Both auth inputs are
|
|
219
|
+
copied only during the approved transaction to private mode-0600 files.
|
|
220
|
+
|
|
221
|
+
The private Unix listener carries RFC6455 WebSocket messages. The maintained
|
|
222
|
+
`agent-dialogue-websocket.py` first validates HTTP 101/accept, masks every client
|
|
223
|
+
frame and reads bounded server text messages. It does not send raw JSONL onto
|
|
224
|
+
the socket. Fragmented text and interleaved ping/pong retain their message
|
|
225
|
+
boundary; invalid masks/opcodes/UTF-8, extensions, EOF or limits fail closed.
|
|
226
|
+
Each connection permits at most 128 total frames and 8 MiB total wire bytes,
|
|
227
|
+
including control traffic; each text message is at most 1 MiB. The adapter
|
|
228
|
+
opens no socket or listener and retains the existing kernel peer checks.
|
|
229
|
+
|
|
230
|
+
The pinned public v1 InitializeParams/InitializeResponse schemas require
|
|
231
|
+
`codexHome`, `platformFamily`, `platformOs` and `userAgent` in the response.
|
|
232
|
+
The harness compares `codexHome` to the exact owned CODEX_HOME in memory before
|
|
233
|
+
sending `initialized` or config/read. Raw initialization values are discarded.
|
|
234
|
+
Schema-invalid responses and a foreign home are refused. The same framed
|
|
235
|
+
transport serves read-only config/read and thread/read; method permissions,
|
|
236
|
+
source release, policy, route and cleanup fences remain in force.
|
|
237
|
+
|
|
238
|
+
`agent-dialogue-native-policy.py` uses the two original public 0.153.2
|
|
239
|
+
ConfigRead schemas under `scripts/agent-dialogue-config-schema/`. On the same
|
|
240
|
+
owned endpoint it requests only `config/read` with the exact work cwd and
|
|
241
|
+
`includeLayers=false`. It requires the prepared approval, sandbox/network,
|
|
242
|
+
writable-root, web-search, file-auth and update values. Each selected dotted
|
|
243
|
+
leaf must have an explicit origin in the owned user config; a missing origin
|
|
244
|
+
is not inferred from its parent or default. Other reported origins must be
|
|
245
|
+
that same user file or packaged defaults under the pinned distribution.
|
|
246
|
+
Managed, project, session, unknown or mismatched origins fail closed. Nonempty
|
|
247
|
+
MCP/plugin/hook/instruction inputs and returned raw layers are refused.
|
|
248
|
+
|
|
249
|
+
Only those closed values, origin classes/counts and boolean assertions are
|
|
250
|
+
retained, including in the external audit. Arbitrary additional config fields,
|
|
251
|
+
origin revision strings, instructions and secrets are discarded. The response
|
|
252
|
+
has a one-MiB/five-second bound. The parent checks policy before public source
|
|
253
|
+
creation, then rereads it on the exact current peer immediately before release
|
|
254
|
+
and requires identical facts and an unchanged owned config/socket/process.
|
|
255
|
+
An otherwise-valid source action receives no release when policy changes.
|
|
256
|
+
|
|
257
|
+
A failed pre-source read preserves one `policy-failure` audit event before
|
|
258
|
+
cleanup. Its code identifies the rejecting boundary: `policy-schema` for
|
|
259
|
+
pinned schemas/JSON/public schema validation, `policy-request` for initialization
|
|
260
|
+
or config/read framing/transport, `policy-value` for schema-valid policy or layer
|
|
261
|
+
mismatches, `policy-origin` for origin constraints, `policy-config` for the owned
|
|
262
|
+
config file fence, and `policy-socket` for the endpoint/process/image fence.
|
|
263
|
+
These fixed codes contain no field names, raw values, layers, exception strings,
|
|
264
|
+
paths, instructions or secrets. They identify a validation boundary, not the
|
|
265
|
+
provider's underlying cause. Earlier failures without a code remain unknown.
|
|
266
|
+
Both consumed v2 and v3 `policy-request` failures retain unknown finer predicates;
|
|
267
|
+
later offline findings do not establish the cause of either actual failure.
|
|
268
|
+
The event now also records closed `substage` and `rejectionKind` labels. Substages
|
|
269
|
+
separate connect/current/socket/peer checks, WebSocket upgrade, initialize
|
|
270
|
+
schema/write/read/decode/envelope/result, initialized notification write,
|
|
271
|
+
config/read params/write/read/decode/envelope/result, and post-read socket/config
|
|
272
|
+
checks. Rejection kinds distinguish deadline, I/O, EOF/close, size bounds, UTF-8,
|
|
273
|
+
frame or HTTP validation, identity, and envelope shape/ID/error/notification/request.
|
|
274
|
+
Unclassified failures remain `unknown`; no native error code, message, method,
|
|
275
|
+
dynamic response field names or exception text is exported. Error/notification/request envelopes
|
|
276
|
+
still stop the transaction. No notification is skipped to search for a success.
|
|
277
|
+
The existing product and pinned public JSON-RPC contracts agree on
|
|
278
|
+
initialize → matching response → initialized → config/read, with `id`/`result`
|
|
279
|
+
success envelopes and no required `jsonrpc` member. This comparison found no
|
|
280
|
+
transport or admission mismatch after v3. The classifier now distinguishes a
|
|
281
|
+
public server request (including optional bounded trace context) from a generic
|
|
282
|
+
shape refusal; malformed error or method types remain generic refusals. Pinned
|
|
283
|
+
public `JSONRPCRequest`, `JSONRPCResponse`, `JSONRPCNotification` and `JSONRPCError`
|
|
284
|
+
schemas anchor the offline fixtures. These are closed known-field subsets:
|
|
285
|
+
unknown fields and mixed forms still refuse, and the expected-ID fence runs
|
|
286
|
+
first. A request is neither answered nor skipped. Its method, params, trace and
|
|
287
|
+
error data never become evidence. The consumed v4 and v5 `config-read-envelope /
|
|
288
|
+
envelope-shape` results do not establish which public or unknown form arrived.
|
|
289
|
+
No new admission or transport mismatch was established by the bounded v5 review.
|
|
290
|
+
|
|
291
|
+
At an initialize/config-read envelope rejection, an optional `envelopeFacts`
|
|
292
|
+
projection records all remaining structural predicates together. Its thirteen
|
|
293
|
+
fixed fields describe the top-level JSON type, ID presence/type/equality,
|
|
294
|
+
result/params presence, method type/emptiness, error object/code/message types,
|
|
295
|
+
trace object/member-value types, and booleans for unknown top-level/error/trace
|
|
296
|
+
members. Simultaneous defects remain visible without copying any unknown names,
|
|
297
|
+
IDs, numeric error codes, method strings, trace values or nested payloads. The
|
|
298
|
+
audit sink rejects missing/extra projection fields, non-boolean flags, unknown
|
|
299
|
+
enum values, and projections outside the two envelope rejection boundaries
|
|
300
|
+
before writing. The existing exact `{id,result}` success allowlist and expected
|
|
301
|
+
integer ID fence remain independent of these diagnostic facts. Offline fixtures
|
|
302
|
+
cover every combination of known top-level fields, malformed nested types,
|
|
303
|
+
otherwise-valid replies with unknown fields, privacy, and once cleanup after
|
|
304
|
+
the external facts are written; they establish no actual v5 response contents.
|
|
305
|
+
Audit write failure still runs the one owned writer cleanup and both credential
|
|
306
|
+
finally paths, retains the root, and cannot produce a successful receipt.
|
|
307
|
+
|
|
308
|
+
This is resolved config evidence, not a ThreadStartResponse observation. The
|
|
309
|
+
existing public create sends cwd and runtime workspace roots; this reader adds
|
|
310
|
+
no dummy thread/start, resume, policy mutation or relay to obtain more evidence.
|
|
311
|
+
The original thread's actual policy/override boundary and the current public
|
|
312
|
+
origin representation remain explicit review/actual-verification limits.
|
|
313
|
+
Endpoint-startup effects preceding the read are not retroactively certified.
|
|
314
|
+
Offline fixtures prove rejection/ordering and data minimization, not actual
|
|
315
|
+
provider execution or a waiver of that remaining boundary.
|
|
316
|
+
|
|
317
|
+
## Parent cleanup and remaining acceptance evidence
|
|
318
|
+
|
|
319
|
+
The parent owns one cleanup transaction on every partial failure and after the
|
|
320
|
+
source tool result returns. It captures owned writer births before teardown,
|
|
321
|
+
including the direct daemon, source action and exact kernel peers of the private
|
|
322
|
+
broker discovery directory. Broker credential records are not read. Signals use
|
|
323
|
+
pidfds for only validated captured roles, alongside exact public Project/tmux
|
|
324
|
+
teardown. The broker's 30-second idle default is not treated as proof of exit
|
|
325
|
+
within the existing 20-second writer deadline.
|
|
326
|
+
|
|
327
|
+
Both owned auth copies are removed on success and retained-root failure paths.
|
|
328
|
+
The exact-root writer exit proof is preserved in the external audit before one
|
|
329
|
+
root removal. Unknown writer exit, audit failure or removal failure remains
|
|
330
|
+
failure and preserves evidence; there is no fixed-sleep proof, broad signal,
|
|
331
|
+
retry removal or manual-cleanup PASS. No protected/shared process is an owned
|
|
332
|
+
seed. Auth source files are not modified. A successful external receipt requires
|
|
333
|
+
closed model/tool/claim proofs and automatic cleanup; it does not certify other
|
|
334
|
+
acceptance cases.
|
|
335
|
+
|
|
336
|
+
The selectorless E2E remains the single deterministic L20 scenario. Actual
|
|
337
|
+
active-tool/human overlap, multiple ordinary requests, same-UID recovery and
|
|
338
|
+
installed smoke remain separate unverified evidence. They are not run by this
|
|
339
|
+
qualification-plus-one-idle transaction and are not inferred from its result.
|
package/docs/hooks.md
CHANGED
|
@@ -229,6 +229,39 @@ amber-orange status role, response-complete panes use success green, and
|
|
|
229
229
|
in-progress panes use progress yellow. They do not inherit the critical queue
|
|
230
230
|
severity, and permission/input status badges do not use red.
|
|
231
231
|
|
|
232
|
+
## Hook Pane Identity
|
|
233
|
+
|
|
234
|
+
Every provider hook projmux installs is handed the Pane it belongs to:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
--pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
projmux plants that activation envelope on the process it launches in the Pane,
|
|
241
|
+
so the value reaches the hook without depending on the tmux environment. That
|
|
242
|
+
matters because the process that actually runs a provider hook is often not the
|
|
243
|
+
one projmux started in the pane: an app-server can be launched with no `TMUX` and
|
|
244
|
+
no `TMUX_PANE` at all, and a hook spawned from it inherits nothing tmux knows.
|
|
245
|
+
|
|
246
|
+
The argument is resolved against the Registry, never trusted on its own. The
|
|
247
|
+
Pane must exist, hold a live runtime handle, and round-trip through the Agent
|
|
248
|
+
that owns it. When it does, that Pane is the answer and no other evidence is
|
|
249
|
+
consulted — falling through to a working-directory match is exactly how an event
|
|
250
|
+
lands on somebody else's Pane. When it does not, the event is not attributed and
|
|
251
|
+
`ai-ingest.log` records which step failed.
|
|
252
|
+
|
|
253
|
+
An app-server shared by several Panes carries no envelope, so the argument
|
|
254
|
+
expands to a bare `--pane=`. That is a valid answer meaning "nothing was handed
|
|
255
|
+
over", and attribution falls back to the established ladder: `TMUX_PANE`, then a
|
|
256
|
+
working-directory match against the live pane list, then a thread or session id
|
|
257
|
+
match. Nothing about any of this is provider-specific.
|
|
258
|
+
|
|
259
|
+
Attribution failures are a closed vocabulary in `projmux diagnostics agent-hook`:
|
|
260
|
+
`pane inventory unavailable` (no live pane list could be read), `no matching
|
|
261
|
+
pane` (the list was read and nothing matched), `pane registry unavailable`,
|
|
262
|
+
`explicit pane is not registered`, `explicit pane has no live runtime`, and
|
|
263
|
+
`explicit pane binding is stale`.
|
|
264
|
+
|
|
232
265
|
## Codex Hooks Engine
|
|
233
266
|
|
|
234
267
|
`projmux doctor` reports Codex hooks-engine wiring separately from legacy
|
|
@@ -282,49 +315,49 @@ hooks = true
|
|
|
282
315
|
matcher = "*"
|
|
283
316
|
[[hooks.PreToolUse.hooks]]
|
|
284
317
|
type = "command"
|
|
285
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
318
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
286
319
|
|
|
287
320
|
[[hooks.PermissionRequest]]
|
|
288
321
|
matcher = "*"
|
|
289
322
|
[[hooks.PermissionRequest.hooks]]
|
|
290
323
|
type = "command"
|
|
291
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
324
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
292
325
|
|
|
293
326
|
[[hooks.PostToolUse]]
|
|
294
327
|
matcher = "*"
|
|
295
328
|
[[hooks.PostToolUse.hooks]]
|
|
296
329
|
type = "command"
|
|
297
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
330
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
298
331
|
|
|
299
332
|
[[hooks.PreCompact]]
|
|
300
333
|
matcher = "*"
|
|
301
334
|
[[hooks.PreCompact.hooks]]
|
|
302
335
|
type = "command"
|
|
303
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
336
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
304
337
|
|
|
305
338
|
[[hooks.PostCompact]]
|
|
306
339
|
matcher = "*"
|
|
307
340
|
[[hooks.PostCompact.hooks]]
|
|
308
341
|
type = "command"
|
|
309
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
342
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
310
343
|
|
|
311
344
|
[[hooks.SessionStart]]
|
|
312
345
|
matcher = "*"
|
|
313
346
|
[[hooks.SessionStart.hooks]]
|
|
314
347
|
type = "command"
|
|
315
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
348
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
316
349
|
|
|
317
350
|
[[hooks.UserPromptSubmit]]
|
|
318
351
|
matcher = "*"
|
|
319
352
|
[[hooks.UserPromptSubmit.hooks]]
|
|
320
353
|
type = "command"
|
|
321
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
354
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
322
355
|
|
|
323
356
|
[[hooks.Stop]]
|
|
324
357
|
matcher = "*"
|
|
325
358
|
[[hooks.Stop.hooks]]
|
|
326
359
|
type = "command"
|
|
327
|
-
command = "projmux internal agent-hook ingest codex-hook >/dev/null 2>&1 || true"
|
|
360
|
+
command = "projmux internal agent-hook ingest codex-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true"
|
|
328
361
|
```
|
|
329
362
|
|
|
330
363
|
Repeated installs are idempotent and preserve unrelated Codex config, including
|
|
@@ -543,7 +576,7 @@ an observability hook:
|
|
|
543
576
|
"hooks": [
|
|
544
577
|
{
|
|
545
578
|
"type": "command",
|
|
546
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
579
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
547
580
|
}
|
|
548
581
|
]
|
|
549
582
|
}
|
|
@@ -553,7 +586,7 @@ an observability hook:
|
|
|
553
586
|
"hooks": [
|
|
554
587
|
{
|
|
555
588
|
"type": "command",
|
|
556
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
589
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
557
590
|
}
|
|
558
591
|
]
|
|
559
592
|
}
|
|
@@ -563,7 +596,7 @@ an observability hook:
|
|
|
563
596
|
"hooks": [
|
|
564
597
|
{
|
|
565
598
|
"type": "command",
|
|
566
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
599
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
567
600
|
}
|
|
568
601
|
]
|
|
569
602
|
}
|
|
@@ -573,7 +606,7 @@ an observability hook:
|
|
|
573
606
|
"hooks": [
|
|
574
607
|
{
|
|
575
608
|
"type": "command",
|
|
576
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
609
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
577
610
|
}
|
|
578
611
|
]
|
|
579
612
|
}
|
|
@@ -583,7 +616,7 @@ an observability hook:
|
|
|
583
616
|
"hooks": [
|
|
584
617
|
{
|
|
585
618
|
"type": "command",
|
|
586
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
619
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
587
620
|
}
|
|
588
621
|
]
|
|
589
622
|
}
|
|
@@ -593,7 +626,7 @@ an observability hook:
|
|
|
593
626
|
"hooks": [
|
|
594
627
|
{
|
|
595
628
|
"type": "command",
|
|
596
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
629
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
597
630
|
}
|
|
598
631
|
]
|
|
599
632
|
}
|
|
@@ -603,7 +636,7 @@ an observability hook:
|
|
|
603
636
|
"hooks": [
|
|
604
637
|
{
|
|
605
638
|
"type": "command",
|
|
606
|
-
"command": "projmux internal agent-hook ingest claude-hook >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
639
|
+
"command": "projmux internal agent-hook ingest claude-hook --pane=${PMX_INTERNAL_ACTIVATION_PANE_UID:-} >/dev/null 2>&1 || true # projmux-managed:claude-hook:v1"
|
|
607
640
|
}
|
|
608
641
|
]
|
|
609
642
|
}
|
package/docs/npm-distribution.md
CHANGED
|
@@ -20,6 +20,37 @@ npm-specific guidance. npm is only an update/install source label here; the
|
|
|
20
20
|
keybinding flow remains `projmux shell` first, then `projmux setup` and
|
|
21
21
|
`projmux setup terminal` only for terminals that swallow shortcuts.
|
|
22
22
|
|
|
23
|
+
## Install residue notice
|
|
24
|
+
|
|
25
|
+
There is no `postinstall` script in this package, and there is no plan to add
|
|
26
|
+
one. A lifecycle script is skipped by `--ignore-scripts` and by many global and
|
|
27
|
+
CI installs, and npm buffers or reorders its output unless
|
|
28
|
+
`--foreground-scripts`. The bin shim cannot be skipped — it *is* the entrypoint
|
|
29
|
+
— and it writes straight to the user's terminal.
|
|
30
|
+
|
|
31
|
+
So the shim carries the install residue notice
|
|
32
|
+
([operational-diagnostics.md](operational-diagnostics.md#install-residue-ledger)).
|
|
33
|
+
`npm install -g projmux` and `npm update` delete and rewrite the package
|
|
34
|
+
directory, so a missing `npm/.install-residue-reported` watermark inside that
|
|
35
|
+
directory *is* the "this install is new" signal — no fingerprinting, no second
|
|
36
|
+
copy of the Go side's XDG path logic in JavaScript, and nothing written outside
|
|
37
|
+
the package directory. The watermark is not in the published `files` list, so
|
|
38
|
+
it never ships in a tarball.
|
|
39
|
+
|
|
40
|
+
On the first run after an install, if stderr is a TTY, the shim creates the
|
|
41
|
+
watermark and then — **after** the user's command has finished, so the notice is
|
|
42
|
+
the last thing on screen and never delays or interleaves with the real command
|
|
43
|
+
— runs `projmux internal install-residue`. A non-TTY run (a hook, CI, a pipe)
|
|
44
|
+
does nothing and leaves the watermark missing, so the next interactive run is
|
|
45
|
+
the one that reports; the notice should land on a run a human is looking at. If
|
|
46
|
+
the watermark cannot be written the notice is never shown, so an unwritable
|
|
47
|
+
package directory cannot produce it on every invocation forever. None of this
|
|
48
|
+
can change the shim's exit code.
|
|
49
|
+
|
|
50
|
+
The trade-off is deliberate: for npm the notice appears on the first
|
|
51
|
+
interactive run after the install rather than during `npm install` itself. The
|
|
52
|
+
ledger's `at` timestamp is the install-detection moment either way.
|
|
53
|
+
|
|
23
54
|
## Local Packaging
|
|
24
55
|
|
|
25
56
|
Build and dry-run pack all npm packages:
|