@junghanacs/entwurf 0.19.0 → 0.20.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.
- package/AGENTS.md +1 -1
- package/BASELINE.md +23 -5
- package/CHANGELOG.md +261 -0
- package/DELIVERY.md +59 -6
- package/README.md +41 -9
- package/VERIFY.md +9 -1
- package/docs/acp-backend-rail.md +41 -16
- package/docs/external-mcp-host.md +10 -5
- package/docs/fresh-cut-policy.md +3 -2
- package/docs/setup-clean-host.md +93 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
- package/package.json +3 -3
- package/pi-extensions/acp-provider.ts +1 -1
- package/pi-extensions/lib/acp/acp-client.ts +3 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
- package/pi-extensions/lib/acp/backend.ts +3 -3
- package/pi-extensions/lib/acp/event-mapper.ts +4 -4
- package/pi-extensions/lib/acp/models.ts +3 -3
- package/pi-extensions/lib/acp/overlay.ts +6 -2
- package/pi-extensions/lib/acp/tool-surface.ts +6 -0
- package/pi-extensions/lib/meta-session.ts +25 -0
- package/run.sh +104 -33
- package/scripts/agy-bridge.sh +1 -1
- package/scripts/agy-imprint.sh +10 -7
- package/scripts/check-acp-carrier-augment.ts +6 -5
- package/scripts/check-acp-cortex.ts +41 -0
- package/scripts/check-acp-provider-surface.ts +9 -6
- package/scripts/check-acp-usage-accounting.ts +9 -9
- package/scripts/check-copilot-birth-hook.ts +3 -1
- package/scripts/check-copilot-receive-arm.ts +169 -21
- package/scripts/check-copilot-statusline.ts +3 -1
- package/scripts/check-gate-qualification.ts +11 -10
- package/scripts/check-hook-launch-topology.ts +6 -5
- package/scripts/check-install-surface.ts +25 -8
- package/scripts/check-meta-facts.ts +6 -4
- package/scripts/check-meta-hook-session-switch.ts +4 -3
- package/scripts/check-meta-identity-consumers.ts +8 -7
- package/scripts/check-meta-receiver-marker.ts +2 -1
- package/scripts/check-omp-birth-hook.ts +3 -2
- package/scripts/check-probe-bridge-command.ts +3 -1
- package/scripts/check-setup-qualification.sh +21 -0
- package/scripts/copilot-bridge-doctor.sh +15 -5
- package/scripts/copilot-bridge-install.sh +6 -2
- package/scripts/copilot-launch.sh +16 -2
- package/scripts/copilot-receive-bridge.sh +80 -13
- package/scripts/lib/reclaim-on-exit.ts +86 -0
- package/scripts/meta-bridge-doctor.sh +56 -15
- package/scripts/meta-bridge-install.sh +13 -7
- package/scripts/meta-bridge-uninstall.sh +6 -4
- package/scripts/mutants/acp-cortex.json +26 -2
- package/scripts/mutants/copilot-receive.json +26 -0
- package/scripts/mutants/omp-birth.json +23 -6
- package/scripts/mutants/pi-package-ownership.json +26 -0
- package/scripts/mutants/setup-verdict.json +13 -0
- package/scripts/omp-bridge-doctor.sh +110 -41
- package/scripts/omp-bridge-install.sh +6 -2
- package/scripts/omp-receive-install.sh +7 -2
- package/scripts/pi_settings_io.py +3 -1
- package/scripts/raw-async-delivery/README.md +10 -1
- package/scripts/raw-codex-measure/README.md +689 -0
- package/scripts/raw-codex-measure/source-audit.md +243 -0
- package/scripts/raw-macos-measure/README.md +148 -0
- package/scripts/raw-macos-measure/probe.sh +389 -0
- package/scripts/register-pi-package.py +38 -30
- package/scripts/register-pi-provider.py +3 -2
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-meta-async-drift.sh +7 -5
- package/scripts/smoke-meta-install-state.sh +70 -19
- package/scripts/smoke-omp-bridge-state.sh +38 -0
- package/scripts/smoke-setup-verdict.sh +100 -0
- package/scripts/smoke-user-scope-citizen.sh +199 -71
package/docs/acp-backend-rail.md
CHANGED
|
@@ -92,10 +92,10 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
|
|
|
92
92
|
|
|
93
93
|
| Surface | Declaration | Class | What a green actually says |
|
|
94
94
|
|---|---|---|---|
|
|
95
|
-
| Entwurf package | `0.
|
|
95
|
+
| Entwurf package | `0.20.0` | shipped baseline | the package contract these rows belong to |
|
|
96
96
|
| pi runtime | devDep exact `0.85.1`, peer `>=0.85.1 <0.86` | **exact** oracle + **closed range** | built and certified against 0.85.1; hosts inside the range are accepted, and the ceiling moves only on measurement |
|
|
97
97
|
| ACP wire SDK | `@agentclientprotocol/sdk 1.4.0` | **exact** | the shared wire oracle both adapters speak |
|
|
98
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
98
|
+
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.76.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
|
|
99
99
|
| Claude Agent SDK | `0.3.257` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
100
100
|
| Anthropic SDK | `0.100.1` | **exact**, peer-resolution only | satisfies the Agent SDK peer floor (0.93.0+); never an API client here (gate L4) |
|
|
101
101
|
| Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
|
|
@@ -151,6 +151,23 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
151
151
|
entwurf passes that flag nowhere (repo grep, 0 hits). They are
|
|
152
152
|
unreachable only because the common loop never invokes them (nor `logout`). Nothing upstream
|
|
153
153
|
enforces that; it is our own call-site discipline, and it stops holding the moment we use one.
|
|
154
|
+
- **Advertised but never called — 0.76.0 adds exactly one.** Upstream #1111 puts a
|
|
155
|
+
`recommendedValue` on AIR config options, and it is **opt-in**: the adapter turns it on only
|
|
156
|
+
when `initialize`'s `clientCapabilities._meta.jetbrains.air.capabilities` names
|
|
157
|
+
`recommendedValue`. entwurf sends `clientCapabilities: {}` (`backend.ts:1758`), so
|
|
158
|
+
`useRecommendedValue` is false on every branch. The label normalization, the `default` row and
|
|
159
|
+
the effort selector that ride it are unreachable for a second, independent reason as well:
|
|
160
|
+
`[측정 2026-09-10]` `git grep -c configOptions pi-extensions/lib/acp/` is **0**, and entwurf
|
|
161
|
+
has never set `EFFORT_CONFIG_ID`, which is the other gate on the SDK-side `applyFlagSettings`.
|
|
162
|
+
The rest of 0.76.0 is one refactor: `resolveModelPreference` moved into a new
|
|
163
|
+
`dist/session-model.*` (diff is comments and formatting), `dist/session-effort.*` is new, and
|
|
164
|
+
`setSessionConfigOption` — our only model-forcing wire call — is byte-identical across
|
|
165
|
+
`v0.75.1..v0.76.0`.
|
|
166
|
+
- **One 0.76.0 change has a reachable SHAPE and is still inert for us.** When
|
|
167
|
+
`configuredSettings` is a STRING PATH, its `readFile` + `JSON.parse` moved out of
|
|
168
|
+
`resolvedProvider` and up onto the unconditional `session/new` path. entwurf passes `settings`
|
|
169
|
+
as an OBJECT (`tool-surface.ts:153`), so the branch is never taken. That makes "entwurf never
|
|
170
|
+
hands `settings` as a path" a contract rather than an accident; it is stated at that call site.
|
|
154
171
|
- **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
|
|
155
172
|
synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
|
|
156
173
|
`Compact conversation`, and `_meta.contextCompaction` schema v1 — where it used to arrive as
|
|
@@ -197,7 +214,7 @@ operator's repair named.
|
|
|
197
214
|
|
|
198
215
|
| Seam | Claude | Cortex Code |
|
|
199
216
|
|---|---|---|
|
|
200
|
-
| Model ids | unprefixed `claude-sonnet-5`, `claude-opus-5` | `cortex-auto`, `cortex-claude-opus-5`, `cortex-claude-sonnet-5`, `cortex-openai-gpt-5.4`; prefix stripped before set-model |
|
|
217
|
+
| Model ids | unprefixed `claude-sonnet-5`, `claude-opus-5`, `claude-fable-5-1` | `cortex-auto`, `cortex-claude-opus-5`, `cortex-claude-sonnet-5`, `cortex-openai-gpt-5.4`; prefix stripped before set-model |
|
|
201
218
|
| Launch | bundled `claude-agent-acp`; `CLAUDE_AGENT_ACP_COMMAND` override | `cortex acp serve`; optional connection; `CORTEX_ACP_COMMAND` override; never a launch-time `-m` |
|
|
202
219
|
| Model authority | per-turn ACP set-model | per-turn ACP set-model; an unavailable curated id fails before prompt |
|
|
203
220
|
| Carrier | engraving in `_meta.systemPrompt` | no system-prompt carrier; engraving rides the first-user augment |
|
|
@@ -256,6 +273,14 @@ Cortex containment was measured against the live CLI rather than copied from Cla
|
|
|
256
273
|
exact-writes an overlay-private `cortex/mcp.json`. Non-stdio declarations fail before
|
|
257
274
|
spawn. Only the `entwurf-bridge` entry receives the real operator HOME required to
|
|
258
275
|
see the garden store.
|
|
276
|
+
- **`realHome` absoluteness:** the D10 guard judges the captured `realHome` in BOTH
|
|
277
|
+
path flavors (POSIX and win32), so the refusal rule states the contract rather than
|
|
278
|
+
the host it happens to run on. This claims no native-Windows support — native
|
|
279
|
+
Windows is UNSUPPORTED. The certified ACP axis remains Linux
|
|
280
|
+
desktop/workstation; macOS ACP turns are NOT CERTIFIED — pending physical
|
|
281
|
+
host. The flavor-explicit form only stops a POSIX host from reading a
|
|
282
|
+
drive/UNC path as relative, which is what made the defect unkillable on
|
|
283
|
+
Linux (PR #77).
|
|
259
284
|
- **Carrier:** Cortex has no `_meta.systemPrompt` contract. The engraving is placed at
|
|
260
285
|
the head of the first-user augment; claiming a system-prompt engraving is false.
|
|
261
286
|
|
|
@@ -267,23 +292,23 @@ caller-session `_meta`, and cross-machine certification.
|
|
|
267
292
|
|
|
268
293
|
A backend can return `newSession` before its declared MCP server is callable. This was
|
|
269
294
|
observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
|
|
270
|
-
path. Neither `claude-agent-acp` 0.
|
|
295
|
+
path. Neither `claude-agent-acp` 0.76.0 nor the Cortex landing adds a client-side
|
|
271
296
|
readiness fence over a session's declared MCP servers, and entwurf's common loop
|
|
272
297
|
calls `mcpServerStatus()` nowhere.
|
|
273
|
-
(Re-measured at the 0.
|
|
274
|
-
0.
|
|
275
|
-
`src/acp-agent.ts` are **2 at
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
298
|
+
(Re-measured at the 0.75.1 → 0.76.0 bump, not inherited — the previous bump's argument is
|
|
299
|
+
not reused, the way the 0.73.0 → 0.75.1 entry did not reuse 0.70.0 → 0.73.0's.
|
|
300
|
+
`mcpServerStatus` call sites in `src/acp-agent.ts` are **2 at v0.75.1 and 2 at v0.76.0**
|
|
301
|
+
`[측정 2026-09-10, upstream v0.76.0/src/acp-agent.ts read directly, grep -n]`; they first
|
|
302
|
+
appeared in 0.71.0 via `0cbbaf3` (MCP OAuth, LLM-25012), so the ADAPTER calls it where it
|
|
303
|
+
once did not. Both were re-read at `v0.76.0 src/acp-agent.ts:1762` and `:1855`
|
|
304
|
+
(v0.75.1: `:1736` / `:1829`; v0.73.0: `:1618` / `:1711`): the first sits inside
|
|
279
305
|
`authenticateMcpServers` behind `supportsMcpOAuth(query)` and skips every status that is not
|
|
280
306
|
`needs-auth`; the second polls a SINGLE named server to `connected` under an OAuth deadline.
|
|
281
307
|
Neither waits on every declared server before `newSession` returns. That is an auth
|
|
282
|
-
handshake, not a readiness fence, so the boundary below is unchanged. The
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
titles, permission-mode kinds and clear-context planning are all new-but-uncalled.
|
|
308
|
+
handshake, not a readiness fence, so the boundary below is unchanged. The surrounding 200
|
|
309
|
+
lines are byte-identical and the region moved +26; the whole 0.76.0 delta is one refactor
|
|
310
|
+
plus one opt-in AIR extension that `clientCapabilities: {}` never enables, so the other
|
|
311
|
+
reachable-surface findings stand as re-measured at the previous bump.
|
|
287
312
|
This bump changes no readiness behavior and closes no part of #72.)
|
|
288
313
|
|
|
289
314
|
### 11-7-a/b. Instrument and first measurement
|
|
@@ -376,5 +401,5 @@ Receipt, limits and the `completed`-branch gap: `scripts/raw-acp-compaction-meas
|
|
|
376
401
|
|
|
377
402
|
- causal MCP-readiness diagnosis and, only with proof, a backend-invariant fence;
|
|
378
403
|
- broader installed-host and cross-machine Cortex evidence;
|
|
379
|
-
- any future Codex managed native-citizen lane—separate from ACP;
|
|
404
|
+
- any future Codex managed native-citizen lane—separate from ACP; the lane is open as #95 and no release ships one;
|
|
380
405
|
- persisted ACP resume/load, which is not implemented by today's in-memory reuse.
|
|
@@ -52,9 +52,12 @@ Prerequisites on the host running the external MCP client:
|
|
|
52
52
|
> carrier reader trims and tests truthiness, so empty and absent are the same answer by
|
|
53
53
|
> construction. Pinned by `[QK:FRESHCALL-IDENTITY-SCRUB]` in the `check-mux-fresh-call` vitest lane,
|
|
54
54
|
> inside `check:full`. The DETECT half is unchanged and still load-bearing, because a seam covers
|
|
55
|
-
> only the launches that pass through it: `doctor-omp-bridge` reads
|
|
56
|
-
>
|
|
57
|
-
>
|
|
55
|
+
> only the launches that pass through it: `doctor-omp-bridge` reads each live `omp`
|
|
56
|
+
> process's environment (on Linux, `/proc/<pid>/environ`) and goes RED on its own
|
|
57
|
+
> axis when one carries either carrier. The remaining uncertified Darwin axis is
|
|
58
|
+
> per-process environment DISCOVERY, not `/proc` absence; macOS rails are
|
|
59
|
+
> NOT CERTIFIED — pending physical host. An omp the operator started
|
|
60
|
+
> from a pi citizen's bash never touched the seam and is still unsupported, exactly
|
|
58
61
|
> as this boundary says.
|
|
59
62
|
|
|
60
63
|
Example env file:
|
|
@@ -166,6 +169,7 @@ Use the managed install surface rather than editing omp's files by hand:
|
|
|
166
169
|
```bash
|
|
167
170
|
entwurf install-omp-bridge # the BIRTH extension (a garden id per visible TUI session)
|
|
168
171
|
entwurf install-omp-mcp # the MCP hand (this section)
|
|
172
|
+
entwurf install-omp-config # the operator setting (tools.xdev: false)
|
|
169
173
|
entwurf install-omp-receive # the RECEIVER extension (mailbox watch + announce-only doorbell)
|
|
170
174
|
|
|
171
175
|
entwurf doctor-omp-bridge
|
|
@@ -173,7 +177,7 @@ entwurf doctor-omp-mcp
|
|
|
173
177
|
entwurf doctor-omp-receive
|
|
174
178
|
```
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
Four units, not two. The receiver is what makes the citizen answerable at all — without it omp
|
|
177
181
|
sends under its own garden id and every reply is refused as `mailbox-undeliverable` — and it is
|
|
178
182
|
also a prerequisite of visible fresh: the fresh preflight refuses this host before touching tmux
|
|
179
183
|
when the receive unit is missing, rather than opening a window that can never be reached.
|
|
@@ -241,7 +245,8 @@ bridge registered, **11 devices**: omp's own `ast_edit`, `debug`, `lsp`, `browse
|
|
|
241
245
|
seven `entwurf_*`. So the default does not merely wrap entwurf — it wraps omp's own LSP and
|
|
242
246
|
debugger too.
|
|
243
247
|
|
|
244
|
-
|
|
248
|
+
**`entwurf setup` writes this for a detected omp host, and `entwurf install-omp-config` is
|
|
249
|
+
the repair leaf. The value they own:**
|
|
245
250
|
|
|
246
251
|
```yaml
|
|
247
252
|
# ~/.omp/agent/config.yml
|
package/docs/fresh-cut-policy.md
CHANGED
|
@@ -22,7 +22,8 @@ the new generation.
|
|
|
22
22
|
## What certification checks
|
|
23
23
|
|
|
24
24
|
`certifyActiveStore` is shared by the doctor and all identity writers: pi birth,
|
|
25
|
-
Claude's `SessionStart`,
|
|
25
|
+
Claude's `SessionStart`, Copilot's first-prompt birth hook, OMP's birth extension,
|
|
26
|
+
agy's imprint, and `entwurf_register_native`. Every active
|
|
26
27
|
record must be:
|
|
27
28
|
|
|
28
29
|
- a regular, non-symlink `.meta.json` file;
|
|
@@ -43,7 +44,7 @@ are opened without following symlinks; implementation and race proofs live besid
|
|
|
43
44
|
|
|
44
45
|
## Running a cut
|
|
45
46
|
|
|
46
|
-
First close every pi, Claude Code, and agy session that could own a citizen or
|
|
47
|
+
First close every pi, Claude Code, Copilot, OMP, and agy session that could own a citizen or
|
|
47
48
|
transport artifact. Then run:
|
|
48
49
|
|
|
49
50
|
```bash
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# Clean-host setup
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Operator recipe for a POSIX host. **Linux is the certified axis** — §2–§6 are
|
|
4
|
+
written for it and their receipts are Linux receipts. What follows is the macOS
|
|
5
|
+
evidence split, and its three states must not collapse into "macOS is supported":
|
|
6
|
+
|
|
7
|
+
- **CERTIFIED (CI)** — the Entwurf-only install surface on macOS
|
|
8
|
+
(`macos-install-surface`). Weaker than a physical-host doctor green. §1 is
|
|
9
|
+
this surface.
|
|
10
|
+
- **NOT CERTIFIED — pending physical host** — **on macOS**, every garden-native
|
|
11
|
+
harness rail in §2–§6 (pi, Claude, Copilot, OMP, agy), marker join, ACP turn,
|
|
12
|
+
mux. Wiring may be written; that is not a rail receipt. The remaining
|
|
13
|
+
unmeasured Darwin doctor reason is per-process environment DISCOVERY
|
|
14
|
+
(start-key is portable).
|
|
15
|
+
- **UNSUPPORTED** — native Windows only. macOS is never this.
|
|
6
16
|
|
|
7
17
|
## Requirements
|
|
8
18
|
|
|
@@ -22,8 +32,35 @@ only on Linux because its strict live-owner join uses `/proc`.
|
|
|
22
32
|
Claude Code >=2.1.217 is required for the managed exec-hook lifecycle. The package
|
|
23
33
|
never supplies or proxies backend credentials.
|
|
24
34
|
|
|
35
|
+
On a physical Darwin host, `python3` may be the CommandLineTools stub (a
|
|
36
|
+
dialog instead of an interpreter) — unmeasured. That is a probe cell
|
|
37
|
+
(`scripts/raw-macos-measure/probe.sh` M3), not a certification path.
|
|
38
|
+
|
|
25
39
|
## 1. Install Node and entwurf
|
|
26
40
|
|
|
41
|
+
This section is the Entwurf-only install surface. On macOS it is
|
|
42
|
+
**CERTIFIED (CI)** (`macos-install-surface`): npm pack → clean consumer
|
|
43
|
+
install → bins → `--help` → `check-bridge` → harness-absent `setup`. That
|
|
44
|
+
is not a harness-rail receipt.
|
|
45
|
+
|
|
46
|
+
On Darwin, `entwurf setup` is intentionally non-green when it detects a
|
|
47
|
+
harness. The wiring is written; the row is FAIL:
|
|
48
|
+
|
|
49
|
+
> the wiring WAS written and nothing failed to install, but this harness
|
|
50
|
+
> rail is NOT CERTIFIED — pending physical host on Darwin. setup runs no
|
|
51
|
+
> doctor; the rail axis is owned by '<doctor>', which refuses on this
|
|
52
|
+
> platform. Tracking: #78.
|
|
53
|
+
|
|
54
|
+
That FAIL is honest: setup runs no doctor, and no physical-host rail
|
|
55
|
+
receipt exists — it is not an install failure. An Entwurf-only host (no
|
|
56
|
+
harness on PATH) stays green (five SKIP, `bins: PASS`, `core: PASS`,
|
|
57
|
+
`result: green`), which is what CERTIFIED (CI) observed.
|
|
58
|
+
|
|
59
|
+
A physical Mac can send host facts without installing entwurf or logging
|
|
60
|
+
into a harness: `scripts/raw-macos-measure/probe.sh` (`/bin/sh`, ~3.5s,
|
|
61
|
+
cells M1–M9, writes nothing). Measurement, not certification — it does
|
|
62
|
+
not move CERTIFIED (CI) or close a rail.
|
|
63
|
+
|
|
27
64
|
Use the host's normal Node 24 installation. With nvm:
|
|
28
65
|
|
|
29
66
|
```bash
|
|
@@ -107,6 +144,12 @@ packageRoot↔installerRoot coupling mismatch and a package/provider managed-pat
|
|
|
107
144
|
|
|
108
145
|
## 2. Optional pi adapter / ACP plugin
|
|
109
146
|
|
|
147
|
+
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
148
|
+
physical host. Control sockets, mux, and ACP plugin hosting have no
|
|
149
|
+
physical-Darwin receipt. `entwurf install` may write `.pi/settings.json`
|
|
150
|
+
on Darwin; that is wiring, not a rail receipt. If `pi` is on PATH,
|
|
151
|
+
`setup` names the pi row FAIL with the §1 Darwin wording.
|
|
152
|
+
|
|
110
153
|
Install the exact release floor, then wire the project:
|
|
111
154
|
|
|
112
155
|
```bash
|
|
@@ -134,6 +177,9 @@ The V3 record births the garden id; do not inject a pi session id manually.
|
|
|
134
177
|
|
|
135
178
|
## 3. Optional Claude Code native citizen
|
|
136
179
|
|
|
180
|
+
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending physical
|
|
181
|
+
host (see the end of this section).
|
|
182
|
+
|
|
137
183
|
First register the MCP bridge if the stable bin is not already present:
|
|
138
184
|
|
|
139
185
|
```bash
|
|
@@ -159,12 +205,21 @@ validation, or hand-inspected marker cannot replace a new real session. If the l
|
|
|
159
205
|
form is unsupported, reinstall; if ownership is correct but the live join is absent,
|
|
160
206
|
restart the affected session.
|
|
161
207
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
208
|
+
The Darwin install fence is open (`Linux | Darwin`); python3 is the
|
|
209
|
+
refusal, not the platform name. A completed Darwin install is still not
|
|
210
|
+
a rail receipt: this rail is NOT CERTIFIED — pending physical host. The
|
|
211
|
+
remaining unmeasured doctor reason is per-process environment DISCOVERY
|
|
212
|
+
of live bridge children (start-key is portable). A CI runner cannot
|
|
213
|
+
supply a Claude login. Uninstall is open on Darwin too. That is an
|
|
214
|
+
evidence boundary, not UNSUPPORTED.
|
|
165
215
|
|
|
166
216
|
## 4. Optional GitHub Copilot CLI native citizen
|
|
167
217
|
|
|
218
|
+
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
219
|
+
physical host. The Darwin install fence for `install-copilot-bridge` is
|
|
220
|
+
open (`Linux | Darwin`); a completed install is not a rail receipt. That
|
|
221
|
+
is an evidence boundary, not UNSUPPORTED.
|
|
222
|
+
|
|
168
223
|
Copilot has four independently owned surfaces. `setup` composes all four when `copilot` is on
|
|
169
224
|
PATH (#86 C3b); the commands below are the per-unit repair, doctor, and inverse surfaces. All
|
|
170
225
|
four must be green for supported visible fresh; a manual citizen may omit the footer, but fresh
|
|
@@ -187,7 +242,12 @@ scanning for that process, checks the receiver, removes inherited pi identity ca
|
|
|
187
242
|
the model/permission defaults. Birth occurs on the first prompt. `entwurf_fresh_call` uses this
|
|
188
243
|
same managed invocation and requires the birth, MCP, receiver, and visible-identity preflight.
|
|
189
244
|
|
|
190
|
-
## 4b. Optional OMP (`omp`) native citizen — accepted on one host
|
|
245
|
+
## 4b. Optional OMP (`omp`) native citizen — accepted on one Linux host (thinkpad)
|
|
246
|
+
|
|
247
|
+
This rail is CERTIFIED on Linux (one host, named in the version rule
|
|
248
|
+
below); on macOS it is NOT CERTIFIED — pending physical host. The Darwin
|
|
249
|
+
install fences for `install-omp-bridge` and `install-omp-receive` are
|
|
250
|
+
open (`Linux | Darwin`); a completed install is not a rail receipt.
|
|
191
251
|
|
|
192
252
|
Three independently owned surfaces, and a boundary that is part of the instructions rather
|
|
193
253
|
than a footnote. Birth, visible identity, who-sent, the MCP hand and RECEIVE are landed, and
|
|
@@ -338,6 +398,10 @@ why `disabledServers` is never the way to hide an import.
|
|
|
338
398
|
|
|
339
399
|
## 5. Optional Antigravity native citizen
|
|
340
400
|
|
|
401
|
+
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
402
|
+
physical host. The installers may write on Darwin; a green install is
|
|
403
|
+
not a native-push receipt.
|
|
404
|
+
|
|
341
405
|
Install the three independently owned surfaces:
|
|
342
406
|
|
|
343
407
|
```bash
|
|
@@ -363,6 +427,10 @@ LIVE=1 AGY_CONVERSATION_ID=<id> entwurf smoke-agy-native-push-live
|
|
|
363
427
|
|
|
364
428
|
## 6. Optional ACP backend turns
|
|
365
429
|
|
|
430
|
+
ACP turns are CERTIFIED on Linux; on macOS they are NOT CERTIFIED —
|
|
431
|
+
pending physical host. Mux (visible fresh / resume) is the same state.
|
|
432
|
+
A CI runner has no Claude or Cortex login.
|
|
433
|
+
|
|
366
434
|
Claude uses the operator's existing local Claude authentication:
|
|
367
435
|
|
|
368
436
|
```bash
|
|
@@ -386,6 +454,15 @@ After upgrading the package, rerun the managed installers for every native harne
|
|
|
386
454
|
in use and restart their existing processes. Native plugin caches are not live-reload
|
|
387
455
|
safe across launch-contract changes.
|
|
388
456
|
|
|
457
|
+
On Darwin the four previously closed install fences
|
|
458
|
+
(`install-meta-bridge`, `install-copilot-bridge`, `install-omp-bridge`,
|
|
459
|
+
`install-omp-receive`) now accept `Linux | Darwin` and refuse on the
|
|
460
|
+
real toolchain (python3 / node / harness presence), not the platform
|
|
461
|
+
name. Re-running them writes wiring. `setup` still names every detected
|
|
462
|
+
harness unit FAIL with the §1 Darwin wording — upgrade does not mint a
|
|
463
|
+
rail receipt. Doctors refuse on Darwin until a physical-host receipt
|
|
464
|
+
exists.
|
|
465
|
+
|
|
389
466
|
If install or doctor reports an unreadable/old active citizen generation, do not edit
|
|
390
467
|
records by hand. Close pi, Claude, Copilot, and agy sessions first, run
|
|
391
468
|
`entwurf meta-bridge-fresh-cut`, and read its exit status before any install. Then choose the
|
|
@@ -414,6 +491,10 @@ PASS/SKIP/FAIL summary. The complete quiescence, archive, and exit-code contract
|
|
|
414
491
|
core / full candidate floor); maintainer checkout only.
|
|
415
492
|
- `check-install-container`: checkout-invisible Linux package-consumer shape using
|
|
416
493
|
fixtures; not a native lifecycle proof.
|
|
494
|
+
- `macos-install-surface`: macOS Entwurf-only package-consumer shape, CERTIFIED (CI)
|
|
495
|
+
(`macos-latest` runner, run 34303884286 @ `70eda03`); not a native lifecycle proof.
|
|
496
|
+
- `scripts/raw-macos-measure/probe.sh`: physical-Darwin host facts (~3.5s,
|
|
497
|
+
no install, no login). Measurement, not a certification path.
|
|
417
498
|
- `doctor-meta-bridge`: one installed real Claude host, only with a new live session.
|
|
418
499
|
- `LIVE=1 entwurf release-gate /path/to/scratch --cut`: aggregate runtime acceptance (`--cut` makes any MUST SKIP red; without it the run is a diagnostic pass).
|
|
419
500
|
|
|
@@ -422,6 +503,11 @@ host verdicts are [BASELINE.md](../BASELINE.md).
|
|
|
422
503
|
|
|
423
504
|
## Uninstall
|
|
424
505
|
|
|
506
|
+
Darwin uninstall is open for the same surfaces the installers write
|
|
507
|
+
(the Claude uninstall fence was already open; the four install fences
|
|
508
|
+
now match). Running an inverse on Darwin is cleanup, not a rail
|
|
509
|
+
certification.
|
|
510
|
+
|
|
425
511
|
Run only the surfaces this host owns:
|
|
426
512
|
|
|
427
513
|
```bash
|
|
@@ -123,8 +123,8 @@ export const claudeAdapter = {
|
|
|
123
123
|
},
|
|
124
124
|
// MEASURED (2026-09-02, #93): claude-agent-acp's `usage_update` carries a
|
|
125
125
|
// session-cumulative ESTIMATED cost (`cost.amount = message.total_cost_usd`)
|
|
126
|
-
// and a context-occupancy scalar (`used`; both read at 0.
|
|
127
|
-
// `dist/acp-agent.js:
|
|
126
|
+
// and a context-occupancy scalar (`used`; both read at 0.76.0
|
|
127
|
+
// `dist/acp-agent.js:3471-3486`). Both are measured, so backend.ts seals them.
|
|
128
128
|
// The token partition is NOT declared here: ACP's only token carrier is a
|
|
129
129
|
// per-turn round-trip aggregate, which is not what pi's four fields mean.
|
|
130
130
|
sealsTurnAccounting: true,
|
|
@@ -49,11 +49,11 @@ export const ENTWURF_ACP_NO_AUTH_SENTINEL = "entwurf-no-auth";
|
|
|
49
49
|
// both axes (protocol smoke + agent interview) — do not extend casually.
|
|
50
50
|
// Exported so the claude backend adapter (backend-adapter.ts) can answer
|
|
51
51
|
// `routeModel` without re-deriving the set from curatedClaudeModels().
|
|
52
|
-
export const SUPPORTED_ANTHROPIC_MODEL_IDS = ["claude-sonnet-5", "claude-opus-5"];
|
|
52
|
+
export const SUPPORTED_ANTHROPIC_MODEL_IDS = ["claude-sonnet-5", "claude-opus-5", "claude-fable-5-1"];
|
|
53
53
|
/** The anchor model whose absence is a hard registry regression, not a soft skip. */
|
|
54
54
|
export const CURATED_ANCHOR_MODEL_ID = "claude-opus-5";
|
|
55
|
-
// Anthropic's registry reports 1M for
|
|
56
|
-
// entwurf surface exposes the full 1M for
|
|
55
|
+
// Anthropic's registry reports 1M for Sonnet 5, Opus 5 and Fable 5.1, and the
|
|
56
|
+
// entwurf surface exposes the full 1M for all three. Sonnet 5's 1M window is the
|
|
57
57
|
// whole point of the 0.12.3 bump — it is the compact-free long-context floor the
|
|
58
58
|
// earlier 200K Sonnet cap could not provide. We still clamp to a 1M ceiling so a
|
|
59
59
|
// future registry value can't silently inflate the surface past what we verify.
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { createHash } from "node:crypto";
|
|
36
36
|
import { existsSync, lstatSync, mkdirSync, readdirSync, readlinkSync, rmSync, symlinkSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
37
37
|
import { homedir } from "node:os";
|
|
38
|
-
import { join } from "node:path";
|
|
38
|
+
import { isAbsolute, join, win32 } from "node:path";
|
|
39
39
|
/** Operator's real Claude config dir — the symlink-passthrough SOURCE. */
|
|
40
40
|
export const CLAUDE_REAL_CONFIG_DIR = join(homedir(), ".claude");
|
|
41
41
|
/** pi-owned overlay dir — the CLAUDE_CONFIG_DIR target for ACP child spawns. */
|
|
@@ -356,7 +356,11 @@ function cortexLinkIfExists(realPath, overlayPath) {
|
|
|
356
356
|
* Returns the isolated HOME/SNOWFLAKE_HOME the spawn env must carry.
|
|
357
357
|
*/
|
|
358
358
|
export function ensureCortexDualHomeOverlay(params) {
|
|
359
|
-
|
|
359
|
+
// Absoluteness is judged in BOTH path flavors so the guard states the contract
|
|
360
|
+
// ("the parent captured an absolute HOME") instead of the host it happens to run
|
|
361
|
+
// on. This asserts nothing about native-Windows support; it only stops a POSIX
|
|
362
|
+
// host from silently accepting a drive/UNC path as "relative".
|
|
363
|
+
if (!params.realHome || !(isAbsolute(params.realHome) || win32.isAbsolute(params.realHome))) {
|
|
360
364
|
throw new Error(`entwurf: cortex dual-HOME overlay requires an absolute realHome captured by the parent (got ${JSON.stringify(params.realHome)})`);
|
|
361
365
|
}
|
|
362
366
|
const overlaysRoot = params.overlaysRoot ?? CORTEX_OVERLAYS_ROOT;
|
|
@@ -120,6 +120,12 @@ export function buildClaudeSessionMeta(params, normalizedSystemPrompt) {
|
|
|
120
120
|
...(params.modelId ? { model: params.modelId } : {}),
|
|
121
121
|
tools: [...params.tools],
|
|
122
122
|
settingSources: [...params.settingSources],
|
|
123
|
+
// `settings` is an OBJECT here, and that is a contract, not a convenience.
|
|
124
|
+
// claude-agent-acp also accepts a STRING PATH, and at 0.76.0 the readFile +
|
|
125
|
+
// JSON.parse for that form moved out of `resolvedProvider` up onto the
|
|
126
|
+
// unconditional `session/new` path — a shape that WOULD reach us, and does not,
|
|
127
|
+
// only because we never hand it a path. Keep it inline: a path would put file
|
|
128
|
+
// IO, and its failure modes, inside every session creation.
|
|
123
129
|
settings: {
|
|
124
130
|
permissions: {
|
|
125
131
|
allow: [...params.permissionAllow],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Garden-citizen dispatch substrate and meta-bridge for Claude Code,
|
|
3
|
+
"version": "0.20.1",
|
|
4
|
+
"description": "Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, and Antigravity; Claude and Snowflake Cortex Code ACP backends. Codex has verified delivery-probe evidence, not a managed native-citizen lane.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Junghan Kim",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"claudeCodeFloor": ">=2.1.217"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@agentclientprotocol/claude-agent-acp": "0.
|
|
85
|
+
"@agentclientprotocol/claude-agent-acp": "0.76.0",
|
|
86
86
|
"@agentclientprotocol/sdk": "1.4.0",
|
|
87
87
|
"@anthropic-ai/sdk": "0.100.1",
|
|
88
88
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// registry (backend-adapter.ts), NOT a per-backend list spelled here. Adding a
|
|
15
15
|
// backend therefore never edits this file. check-acp-provider-surface pins that
|
|
16
16
|
// the compiled entry really registers the EXACT union
|
|
17
|
-
// ([QK:CORTEX-PROVIDER-
|
|
17
|
+
// ([QK:CORTEX-PROVIDER-EXACT-ROW-SURFACE]).
|
|
18
18
|
//
|
|
19
19
|
// Fence: this entry rides the emit-capable root tsconfig (it is not in the root
|
|
20
20
|
// `exclude` list); its lib modules are imported with `.js` suffixes (the root
|
|
@@ -46,8 +46,8 @@ import type { AcpTextBlock } from "./context.js";
|
|
|
46
46
|
* shape) and never onto those four. A field rename upstream fails the typecheck
|
|
47
47
|
* here rather than silently zeroing the operator's cache-effect badge.
|
|
48
48
|
*/
|
|
49
|
-
/** One `_meta.quota.token_count` row (claude-agent-acp 0.
|
|
50
|
-
* `dist/acp-agent.js:
|
|
49
|
+
/** One `_meta.quota.token_count` row (claude-agent-acp 0.76.0
|
|
50
|
+
* `dist/acp-agent.js:6566-6575`). `cachedInputTokens` is cache READS — the name
|
|
51
51
|
* differs from `usage.cachedReadTokens` because the shape is shared with
|
|
52
52
|
* codex-acp; `cachedWriteTokens` is Claude's extra sibling. */
|
|
53
53
|
export type AcpQuotaTokenCount = {
|
|
@@ -64,7 +64,7 @@ export type AcpPromptResponse = {
|
|
|
64
64
|
/** Vendor-private, version-pinned. `_meta` is a standard ACP extension slot
|
|
65
65
|
* whose values a client may not assume, and `quota` is NOT in
|
|
66
66
|
* claude-agent-acp's exported types — it is produced by the private
|
|
67
|
-
* `turnQuotaMeta()` (read at 0.
|
|
67
|
+
* `turnQuotaMeta()` (read at 0.76.0 `dist/acp-agent.js:6549-6558`). Read
|
|
68
68
|
* defensively, never structurally required, and re-measure on a pin move. */
|
|
69
69
|
_meta?: {
|
|
70
70
|
quota?: {
|
|
@@ -150,7 +150,7 @@ export interface AcpAdapterSettingsParams {
|
|
|
150
150
|
*
|
|
151
151
|
* During stream handling the vendor keeps a per-message snapshot in
|
|
152
152
|
* `lastAssistantUsage`, but its `usage_update` publishes only the scalar `used`
|
|
153
|
-
* value (read at claude-agent-acp 0.
|
|
153
|
+
* value (read at claude-agent-acp 0.76.0 `dist/acp-agent.js:3857-3882`). Until
|
|
154
154
|
* that partition is carried on the wire, NOTHING honest can go in pi's four
|
|
155
155
|
* fields, so entwurf writes none of them.
|
|
156
156
|
*/
|
|
@@ -342,8 +342,8 @@ export const claudeAdapter: AcpBackendAdapter = {
|
|
|
342
342
|
|
|
343
343
|
// MEASURED (2026-09-02, #93): claude-agent-acp's `usage_update` carries a
|
|
344
344
|
// session-cumulative ESTIMATED cost (`cost.amount = message.total_cost_usd`)
|
|
345
|
-
// and a context-occupancy scalar (`used`; both read at 0.
|
|
346
|
-
// `dist/acp-agent.js:
|
|
345
|
+
// and a context-occupancy scalar (`used`; both read at 0.76.0
|
|
346
|
+
// `dist/acp-agent.js:3471-3486`). Both are measured, so backend.ts seals them.
|
|
347
347
|
// The token partition is NOT declared here: ACP's only token carrier is a
|
|
348
348
|
// per-turn round-trip aggregate, which is not what pi's four fields mean.
|
|
349
349
|
sealsTurnAccounting: true,
|
|
@@ -137,7 +137,7 @@ function finiteOrZero(value: unknown): number {
|
|
|
137
137
|
* "the accounting-grade figure per the SDK" — it also counts Task subagents,
|
|
138
138
|
* sidechains, and INTERNAL CALLS SUCH AS COMPACTION, so its rows "can total more
|
|
139
139
|
* than `token_count`" and are "the fuller picture, not a decomposition of it"
|
|
140
|
-
* (read at claude-agent-acp 0.
|
|
140
|
+
* (read at claude-agent-acp 0.76.0 `dist/acp-agent.js:6538-6558`). The narrower
|
|
141
141
|
* `PromptResponse.usage` (== `quota.token_count`) is the MAIN AGENT LOOP only.
|
|
142
142
|
*
|
|
143
143
|
* The wider one is the right numerator because the denominator already has that
|
|
@@ -1237,7 +1237,7 @@ export function streamAcpTurn(
|
|
|
1237
1237
|
// Writing zeros is not a placeholder for a better number we could compute:
|
|
1238
1238
|
// the per-request partition is genuinely absent from the wire. The vendor
|
|
1239
1239
|
// builds it in `lastAssistantUsage` and sends only its scalar sum (read at
|
|
1240
|
-
// claude-agent-acp 0.
|
|
1240
|
+
// claude-agent-acp 0.76.0 `dist/acp-agent.js:3857-3882`) — #96.
|
|
1241
1241
|
//
|
|
1242
1242
|
// But silence is NOT the resting state. A cache miss the operator never sees
|
|
1243
1243
|
// is a false reading, not a modest one: a session can run for hours believing
|
|
@@ -1356,7 +1356,7 @@ export function streamAcpTurn(
|
|
|
1356
1356
|
// between them is exactly why the notice below names a MECHANISM and
|
|
1357
1357
|
// never a cause: claude-agent-acp's `conversation_reset` handler only
|
|
1358
1358
|
// switches the SDK to a fresh conversation and touches no cost at all
|
|
1359
|
-
// (read at 0.
|
|
1359
|
+
// (read at 0.76.0 `dist/acp-agent.js:4286-4293`), while claude-agent-sdk
|
|
1360
1360
|
// separately documents that "a mid-session /clear resets the running
|
|
1361
1361
|
// total" (read at 0.3.257 `sdk.d.ts:4884`). A reset therefore PLAUSIBLY
|
|
1362
1362
|
// explains a backwards total, but nothing here has MEASURED that it did,
|
|
@@ -60,8 +60,8 @@ export type AcpPiStreamState = {
|
|
|
60
60
|
*
|
|
61
61
|
* Last write wins, never a sum: both are latest session-level observations, and
|
|
62
62
|
* one turn can legitimately see several (claude emits one per `result` message,
|
|
63
|
-
* including a sub-agent's own — read at claude-agent-acp 0.
|
|
64
|
-
* `dist/acp-agent.js:
|
|
63
|
+
* including a sub-agent's own — read at claude-agent-acp 0.76.0
|
|
64
|
+
* `dist/acp-agent.js:3471-3486`), each carrying that result's current values.
|
|
65
65
|
*/
|
|
66
66
|
observedSessionCostUsd?: number;
|
|
67
67
|
observedContextOccupancyTokens?: number;
|
|
@@ -346,8 +346,8 @@ export function applyAcpSessionUpdate(
|
|
|
346
346
|
case "usage_update": {
|
|
347
347
|
// `used` is OCCUPANCY-shaped — the backend's post-turn context size, not
|
|
348
348
|
// the prompt response's turn aggregate. Claude sends `lastAssistantTotalUsage`
|
|
349
|
-
// as `used` (read at claude-agent-acp 0.
|
|
350
|
-
// `dist/acp-agent.js:
|
|
349
|
+
// as `used` (read at claude-agent-acp 0.76.0
|
|
350
|
+
// `dist/acp-agent.js:3871-3882`), after constructing that scalar from the
|
|
351
351
|
// latest assistant snapshot (`:3853-3866`). pi reads `usage.totalTokens` as
|
|
352
352
|
// exactly that occupancy (`calculateContextTokens(usage) = usage.totalTokens
|
|
353
353
|
// || input + output + cacheRead + cacheWrite`, read at pi-coding-agent
|
|
@@ -53,13 +53,13 @@ export const ENTWURF_ACP_NO_AUTH_SENTINEL = "entwurf-no-auth";
|
|
|
53
53
|
// both axes (protocol smoke + agent interview) — do not extend casually.
|
|
54
54
|
// Exported so the claude backend adapter (backend-adapter.ts) can answer
|
|
55
55
|
// `routeModel` without re-deriving the set from curatedClaudeModels().
|
|
56
|
-
export const SUPPORTED_ANTHROPIC_MODEL_IDS = ["claude-sonnet-5", "claude-opus-5"] as const;
|
|
56
|
+
export const SUPPORTED_ANTHROPIC_MODEL_IDS = ["claude-sonnet-5", "claude-opus-5", "claude-fable-5-1"] as const;
|
|
57
57
|
|
|
58
58
|
/** The anchor model whose absence is a hard registry regression, not a soft skip. */
|
|
59
59
|
export const CURATED_ANCHOR_MODEL_ID = "claude-opus-5";
|
|
60
60
|
|
|
61
|
-
// Anthropic's registry reports 1M for
|
|
62
|
-
// entwurf surface exposes the full 1M for
|
|
61
|
+
// Anthropic's registry reports 1M for Sonnet 5, Opus 5 and Fable 5.1, and the
|
|
62
|
+
// entwurf surface exposes the full 1M for all three. Sonnet 5's 1M window is the
|
|
63
63
|
// whole point of the 0.12.3 bump — it is the compact-free long-context floor the
|
|
64
64
|
// earlier 200K Sonnet cap could not provide. We still clamp to a 1M ceiling so a
|
|
65
65
|
// future registry value can't silently inflate the surface past what we verify.
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
writeFileSync,
|
|
47
47
|
} from "node:fs";
|
|
48
48
|
import { homedir } from "node:os";
|
|
49
|
-
import { join } from "node:path";
|
|
49
|
+
import { isAbsolute, join, win32 } from "node:path";
|
|
50
50
|
|
|
51
51
|
import type { AcpMcpServer } from "./config.js";
|
|
52
52
|
|
|
@@ -416,7 +416,11 @@ function cortexLinkIfExists(realPath: string, overlayPath: string): void {
|
|
|
416
416
|
* Returns the isolated HOME/SNOWFLAKE_HOME the spawn env must carry.
|
|
417
417
|
*/
|
|
418
418
|
export function ensureCortexDualHomeOverlay(params: CortexOverlayParams): CortexOverlayResult {
|
|
419
|
-
|
|
419
|
+
// Absoluteness is judged in BOTH path flavors so the guard states the contract
|
|
420
|
+
// ("the parent captured an absolute HOME") instead of the host it happens to run
|
|
421
|
+
// on. This asserts nothing about native-Windows support; it only stops a POSIX
|
|
422
|
+
// host from silently accepting a drive/UNC path as "relative".
|
|
423
|
+
if (!params.realHome || !(isAbsolute(params.realHome) || win32.isAbsolute(params.realHome))) {
|
|
420
424
|
throw new Error(
|
|
421
425
|
`entwurf: cortex dual-HOME overlay requires an absolute realHome captured by the parent (got ${JSON.stringify(params.realHome)})`,
|
|
422
426
|
);
|
|
@@ -150,6 +150,12 @@ export function buildClaudeSessionMeta(
|
|
|
150
150
|
...(params.modelId ? { model: params.modelId } : {}),
|
|
151
151
|
tools: [...params.tools],
|
|
152
152
|
settingSources: [...params.settingSources],
|
|
153
|
+
// `settings` is an OBJECT here, and that is a contract, not a convenience.
|
|
154
|
+
// claude-agent-acp also accepts a STRING PATH, and at 0.76.0 the readFile +
|
|
155
|
+
// JSON.parse for that form moved out of `resolvedProvider` up onto the
|
|
156
|
+
// unconditional `session/new` path — a shape that WOULD reach us, and does not,
|
|
157
|
+
// only because we never hand it a path. Keep it inline: a path would put file
|
|
158
|
+
// IO, and its failure modes, inside every session creation.
|
|
153
159
|
settings: {
|
|
154
160
|
permissions: {
|
|
155
161
|
allow: [...params.permissionAllow],
|
|
@@ -1622,6 +1622,31 @@ export type MarkerOwnerVerdict = "live" | "dead" | "uncertain";
|
|
|
1622
1622
|
* comparable to each other: `linux:<starttime ticks since boot>` and `ps:<lstart
|
|
1623
1623
|
* wall-clock text>` describe the same process with different numbers, so a
|
|
1624
1624
|
* mismatch ACROSS schemes says nothing about whether the process changed.
|
|
1625
|
+
*
|
|
1626
|
+
* They are also NOT equally resolved. `linux:` carries `/proc` clock ticks at
|
|
1627
|
+
* 10 ms granularity; `ps:` carries `lstart` wall-clock text at ONE-SECOND
|
|
1628
|
+
* granularity [measured, oracle 2026-09-09: two children spawned 50 ms apart
|
|
1629
|
+
* returned identical `lstart` strings, and widening to `lstart=,command=`
|
|
1630
|
+
* still collided when argv was identical]. Darwin has no `/proc`, so it ALWAYS
|
|
1631
|
+
* takes the `ps:` branch — there the pid-reuse defense window is 100x wider
|
|
1632
|
+
* than on Linux. 0.20.0 ships no key change anyway, and the numbers are why.
|
|
1633
|
+
* A same-second pid reuse on Darwin requires pid N to die, the whole
|
|
1634
|
+
* sequential pid space (PID_MAX 99999, wrapping after ~99,899 spawns and
|
|
1635
|
+
* skipping live pids) to wrap back around to N, and the replacement to start
|
|
1636
|
+
* in the SAME wall-clock second — a sustained ~99,900 spawns/second for a full
|
|
1637
|
+
* second. Measured on the reference host: 19.9 forks/second average under real
|
|
1638
|
+
* load, ~14,873 forks/second pathological synthetic peak (6.7x short even at
|
|
1639
|
+
* the synthetic peak), with Darwin's default `kern.maxproc` (~2,500) capping
|
|
1640
|
+
* churn in flight besides. The error direction is one-sided: a coarser clock
|
|
1641
|
+
* can only make more keys compare EQUAL, and {@link classifyMarkerOwner} grants
|
|
1642
|
+
* `live` on equal keys — coarsening can mint a false `live` (identity grant
|
|
1643
|
+
* fails OPEN) and can never mint a false `dead` (the generation cut stays
|
|
1644
|
+
* fail-CLOSED). The migration axis is empty by measurement, not argument: a
|
|
1645
|
+
* read-only scan of this host's real marker store found 1,248 marker files —
|
|
1646
|
+
* all `linux:`, zero `ps:` keys — because on Linux `/proc` always wins, so no
|
|
1647
|
+
* Linux install ever minted a `ps:` key; only a Darwin install can. A
|
|
1648
|
+
* Darwin-side hardening decision, if one is ever wanted, belongs with the
|
|
1649
|
+
* physical-Mac receipts (#78), not ahead of them.
|
|
1625
1650
|
*/
|
|
1626
1651
|
export type StartKeyScheme = "linux" | "ps";
|
|
1627
1652
|
|