@junghanacs/entwurf 0.51.0 → 0.53.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 +116 -397
- package/CHANGELOG.md +191 -2
- package/CONTRIBUTING.md +4 -4
- package/DELIVERY.md +2 -2
- package/ENTWURF-CLI.json +1 -1
- package/ENTWURF.json +10 -4
- package/README.md +11 -8
- package/VERIFY.md +333 -143
- package/demo/README.md +1 -1
- package/demo/demo-baseline.sh +1 -1
- package/demo/demo.sh +1 -1
- package/docs/acp-backend-rail.md +248 -34
- package/docs/setup-clean-host.md +75 -80
- package/mcp/entwurf-bridge/dist/ENTWURF.json +10 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +90 -14
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +4 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/context.js +15 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/model-ids.js +9 -12
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +41 -56
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +33 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +4 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +34 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +2 -1
- package/package.json +14 -14
- package/pi/pi-durable/carrier/harness-setup.js +1 -1
- package/pi/pi-durable/carrier/subagent.js +1 -1
- package/pi/pi-durable/overlay/upstream-pin.json +15 -15
- package/pi/settings.reference.json +0 -1
- package/pi-extensions/acp-provider.ts +12 -6
- package/pi-extensions/lib/acp/acp-client.ts +6 -7
- package/pi-extensions/lib/acp/backend-adapter.ts +139 -15
- package/pi-extensions/lib/acp/backend.ts +261 -32
- package/pi-extensions/lib/acp/config.ts +4 -9
- package/pi-extensions/lib/acp/context.ts +17 -0
- package/pi-extensions/lib/acp/event-mapper.ts +236 -17
- package/pi-extensions/lib/acp/model-ids.js +9 -12
- package/pi-extensions/lib/acp/models.ts +46 -57
- package/pi-extensions/lib/acp/tool-surface.ts +34 -6
- package/pi-extensions/lib/entwurf-control-rpc.ts +5 -1
- package/pi-extensions/lib/fresh-call-composition.ts +37 -3
- package/pi-extensions/lib/herdr-fresh-call.ts +3 -1
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +3 -1
- package/pi-extensions/meta-bridge-pi-durable.ts +1 -1
- package/run.sh +62 -120
- package/scripts/check-acp-carrier-augment.ts +3 -21
- package/scripts/check-acp-prompt-lifecycle.ts +24 -3
- package/scripts/check-acp-provider-surface.ts +17 -26
- package/scripts/check-acp-session-reuse.ts +23 -4
- package/scripts/check-acp-stop-reason.ts +22 -3
- package/scripts/check-acp-stream-hooks.ts +24 -3
- package/scripts/check-acp-usage-accounting.ts +53 -24
- package/scripts/check-copilot-receive-arm.ts +19 -2
- package/scripts/check-entwurf-v2-surface.ts +7 -21
- package/scripts/check-fresh-call-dispatch.ts +9 -3
- package/scripts/check-gate-qualification.ts +6 -6
- package/scripts/check-herdr-fresh-call.ts +1 -0
- package/scripts/check-install-surface.ts +6 -0
- package/scripts/check-pi-durable-send.ts +4 -1
- package/scripts/check-probe-ordering.ts +34 -24
- package/scripts/check-release-gate-outcomes.ts +9 -23
- package/scripts/lib/codex-fresh-source-receipts.ts +12 -6
- package/scripts/lib/probe-cli-target.ts +3 -2
- package/scripts/mutants/acp-augment.json +0 -26
- package/scripts/mutants/acp-cortex.json +10 -183
- package/scripts/mutants/acp-foreground-only.json +11 -0
- package/scripts/mutants/acp-launch-namespace.json +3 -1
- package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
- package/scripts/mutants/acp-set-model-readback.json +488 -0
- package/scripts/mutants/agent-guidance.json +2 -2
- package/scripts/mutants/codex-native.json +2 -2
- package/scripts/mutants/control-send-receipt.json +47 -7
- package/scripts/mutants/mux-fresh-call.json +12 -1
- package/scripts/mutants/pack-install.json +4 -4
- package/scripts/mutants/pi-floor.json +8 -10
- package/scripts/mutants/release-gate.json +3 -3
- package/scripts/mutants/v2-surface.json +4 -6
- package/scripts/raw-acp-compaction-measure/probe.ts +8 -2
- package/scripts/smoke-acp-cortex-live.ts +4 -408
- package/scripts/smoke-acp-ordering-probe-live.ts +7 -1
- package/scripts/smoke-acp-session-reuse-live.ts +11 -2
- package/scripts/smoke-acp-v2-send-live.ts +1 -1
- package/scripts/smoke-herdr-raw-install-live.sh +4 -4
- package/scripts/check-acp-cortex.ts +0 -710
package/docs/setup-clean-host.md
CHANGED
|
@@ -23,26 +23,25 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
23
23
|
|
|
24
24
|
## Requirements
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
The current published Entwurf 0.52.0 uses the exact Pi 1.5.2 SDK set and host Pi `>=1.5.2 <1.6`. The marked 0.51.0 / SDK 1.5.0 examples below are historical and remain bound to their own published manifest. Source validation, publication and a running host's loaded bytes are separate evidence; the earlier addon-less validation tarball installed on one operating host is not the published artifact (see the [#158 boundary](#sdk-152-candidate-boundary-158)).
|
|
27
|
+
|
|
28
|
+
The validation TGZ installed in #155's operating window before the D1 display repair is an earlier, separate artifact, not the published one; see the [SDK 1.5 boundary](#sdk-15-development-boundary-155) below.
|
|
29
|
+
|
|
30
|
+
The older package, `0.50.0`, ships the Pi 1.4.0 SDK set and requires a Pi 1.4.x host; the setup guide inside that tarball is its contract.
|
|
29
31
|
|
|
30
|
-
<!-- entwurf:published @junghanacs/entwurf@0.50.0 pi 1.4.0 -->
|
|
31
32
|
| Component | Requirement | Needed for |
|
|
32
33
|
|---|---|---|
|
|
33
34
|
| Node | **`>=24.0.0`** | package and bridge runtime |
|
|
34
35
|
| npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
|
|
35
36
|
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot/Codex wiring writers |
|
|
36
37
|
| entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
|
|
37
|
-
| pi | optional-by-presence
|
|
38
|
-
| Native durable (`pi-durable`) | supplied by the Entwurf package (#130): Entwurf's owned app carrier plus
|
|
38
|
+
| pi | optional-by-presence — current 0.52.0: `>=1.5.2 <1.6`; historical 0.51.0: <!-- entwurf:published @junghanacs/entwurf@0.51.0 pi 1.5.0 -->`>=1.5.0 <1.6`<!-- /entwurf:published -->. Absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
|
|
39
|
+
| Native durable (`pi-durable`) | supplied by the Entwurf package (#130): Entwurf's owned app carrier plus its exact pi SDK set (1.5.2 in published 0.52.0; 1.5.0 in historical 0.51.0) as production dependencies — setup PASS when they verify, a named FAIL otherwise (never SKIP) | the `entwurf pi-durable` contact; native birth/LIVE on this supply surface is a separate release proof (§2b) |
|
|
39
40
|
| Claude Code | optional, **`>=2.1.306`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
|
|
40
41
|
| GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
|
|
41
42
|
| OMP (`omp`) | optional-by-presence, operator-installed — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/`tools.xdev` setting/receiver) | self-fetch citizen and visible fresh (accepted on one host — see §4b) |
|
|
42
43
|
| Antigravity `agy` | optional, operator-installed and authenticated | legacy citizen; native push paused (#152) |
|
|
43
44
|
| OpenAI Codex CLI | optional-by-presence, operator-installed and authenticated; detected composes all four operator-owned units (birth/MCP/status-line/terminal-title) and stays non-green until the operator trusts the birth declaration once in a visible Codex; visible fresh also requires the operator-owned app-server, started with `entwurf codex-app-server` in a tmux session of the operator's choosing (#95 D1 retired the fixed `codex` home) | native-push and visible fresh, supported in 0.21.0 |
|
|
44
|
-
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
45
|
-
<!-- /entwurf:published -->
|
|
46
45
|
|
|
47
46
|
Claude Code >=2.1.306 is required for the managed exec-hook lifecycle and same-id resume. The package
|
|
48
47
|
never supplies or proxies backend credentials.
|
|
@@ -85,7 +84,7 @@ node --version
|
|
|
85
84
|
npm --version
|
|
86
85
|
```
|
|
87
86
|
|
|
88
|
-
The two npm forms below install the **published** package, `0.
|
|
87
|
+
The two npm forms below install the **published** package, `0.52.0` (Pi 1.5.x, the Pi 1.5.2 SDK
|
|
89
88
|
set). The [product entry](../README.md#install)'s unpinned `npm install -g @junghanacs/entwurf`
|
|
90
89
|
takes whichever version npm's `latest` dist-tag names; pin `@<version>` as below when you mean the
|
|
91
90
|
exact artifact the [Requirements](#requirements) table describes.
|
|
@@ -94,7 +93,7 @@ Global install is simplest when native harnesses should find stable bins from ev
|
|
|
94
93
|
working directory:
|
|
95
94
|
|
|
96
95
|
```bash
|
|
97
|
-
npm install -g @junghanacs/entwurf@0.
|
|
96
|
+
npm install -g @junghanacs/entwurf@0.52.0
|
|
98
97
|
entwurf --help
|
|
99
98
|
entwurf setup /path/to/your-project
|
|
100
99
|
entwurf check-bridge
|
|
@@ -105,7 +104,7 @@ A project-local installation is also supported:
|
|
|
105
104
|
```bash
|
|
106
105
|
mkdir -p ~/entwurf-smoke && cd ~/entwurf-smoke
|
|
107
106
|
npm init -y
|
|
108
|
-
npm install --save-dev @junghanacs/entwurf@0.
|
|
107
|
+
npm install --save-dev @junghanacs/entwurf@0.52.0
|
|
109
108
|
npx entwurf setup .
|
|
110
109
|
npx entwurf check-bridge
|
|
111
110
|
```
|
|
@@ -115,10 +114,10 @@ project-local install, or `./run.sh <command>` from a source checkout. Keep that
|
|
|
115
114
|
same scope when upgrading and checking; native process restarts are separate (§7).
|
|
116
115
|
|
|
117
116
|
`check-bridge` is auth-free. It proves the installed prebuilt MCP server boots and
|
|
118
|
-
lists the nine garden tools (0.
|
|
117
|
+
lists the nine garden tools (0.52.0); it does not prove a backend model turn, native hook or retirement permission.
|
|
119
118
|
|
|
120
119
|
Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, Codex CLI, agy,
|
|
121
|
-
|
|
120
|
+
and their authentication remain operator-owned optional prerequisites for the
|
|
122
121
|
integrations that use them; all may be absent on an Entwurf-only host. A source checkout's
|
|
123
122
|
pinned Pi development packages are for building and testing this repo, not a transitive product installation promise.
|
|
124
123
|
|
|
@@ -226,13 +225,11 @@ physical-Darwin receipt. `entwurf install` may write `.pi/settings.json`
|
|
|
226
225
|
on Darwin; that is wiring, not a rail receipt. If `pi` is on PATH,
|
|
227
226
|
`setup` names the pi row FAIL with the §1 Darwin wording.
|
|
228
227
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
the global package root:
|
|
228
|
+
For published Entwurf 0.52.0, install its exact host Pi floor and wire the project as shown below.
|
|
229
|
+
The previous 0.51.0 uses the Pi 1.5.0 floor named in its own setup guide:
|
|
232
230
|
|
|
233
|
-
<!-- entwurf:published @junghanacs/entwurf@0.50.0 pi 1.4.0 -->
|
|
234
231
|
```bash
|
|
235
|
-
npm install -g @earendil-works/pi-coding-agent@1.
|
|
232
|
+
npm install -g @earendil-works/pi-coding-agent@1.5.2
|
|
236
233
|
pi --version
|
|
237
234
|
|
|
238
235
|
cd ~/entwurf-smoke
|
|
@@ -240,11 +237,12 @@ entwurf install .
|
|
|
240
237
|
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
|
|
241
238
|
```
|
|
242
239
|
|
|
243
|
-
The supported range is `>=1.
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
The supported host Pi range for published Entwurf 0.52.0 is `>=1.5.2 <1.6`.
|
|
241
|
+
A detected host outside that range is refused for Pi wiring; Entwurf does not upgrade it.
|
|
242
|
+
Align the host explicitly, for example with the exact npm command above, before wiring Pi. A host using only the external MCP bridge can skip pi until it needs a
|
|
246
243
|
control socket; no delivery rail launches a pi process.
|
|
247
|
-
|
|
244
|
+
|
|
245
|
+
Published 0.52.0 requires host Pi `>=1.5.2 <1.6`; `setup` reports a detected older host as FAIL. Prepare host Pi 1.5.2 before wiring, and use the checkout path rather than the global npm package when running a checkout directly. Range eligibility does not replace installation, validation or operating approval. Ordinary Pi 1.5.2 defaults its global `cacheWarming` setting to `idle`, which may send additional provider requests; pi-durable does not map that setting. See the [#158 durable boundary](./durable-native-support.md#sdk-152-candidate-boundary-158).
|
|
248
246
|
|
|
249
247
|
For daily garden-native pi sessions:
|
|
250
248
|
|
|
@@ -263,7 +261,7 @@ A plain `pi` can load the package without becoming a citizen.
|
|
|
263
261
|
In that session ask for `entwurf_self`, then `entwurf_peers`. Pick an existing
|
|
264
262
|
live garden id and ask for `entwurf_v2` with `intent: "fire-and-forget"` and your
|
|
265
263
|
message. These are harness tools, not shell subcommands; pi exposes them through
|
|
266
|
-
Pi's built-in MCP as `mcp__entwurf_bridge__<verb>`. The
|
|
264
|
+
Pi's built-in MCP as `mcp__entwurf_bridge__<verb>`. The bridge has nine tools;
|
|
267
265
|
pi hides `entwurf_inbox_read` and `entwurf_register_native`, leaving seven active.
|
|
268
266
|
The added `entwurf_retire_call` is only for one target/action explicitly requested by the
|
|
269
267
|
human operator, never autonomous cleanup. Where a host provides or is configured with an approval
|
|
@@ -348,11 +346,12 @@ by `entwurf pi` or by registering ordinary Pi's adapter.
|
|
|
348
346
|
|
|
349
347
|
What the Entwurf package supplies:
|
|
350
348
|
|
|
351
|
-
1. **The pi SDK set** —
|
|
352
|
-
pi-durable, chord, pi-mcp, pi-codemode, pi-telemetry, pi-schema}` at exactly `1.5.
|
|
353
|
-
`proper-lockfile`, as
|
|
354
|
-
|
|
355
|
-
|
|
349
|
+
1. **The pi SDK set** — published 0.52.0 supplies `@earendil-works/{pi-coding-agent, pi-ai, pi-tui, pi-agent-core,
|
|
350
|
+
pi-durable, chord, pi-mcp, pi-codemode, pi-telemetry, pi-schema}` at exactly `1.5.2`, plus
|
|
351
|
+
`proper-lockfile`, as production dependencies. Model data rides `pi-ai@1.5.2`, not a catalog copy.
|
|
352
|
+
Historical 0.51.0 used <!-- entwurf:published @junghanacs/entwurf@0.51.0 pi 1.5.0 -->`@earendil-works/pi-ai@1.5.0`<!-- /entwurf:published -->.
|
|
353
|
+
The [SDK pin](../pi/pi-durable/overlay/upstream-pin.json) and [package manifest](../package.json)
|
|
354
|
+
describe the checkout you hold, not immutable published bytes.
|
|
356
355
|
2. **The native app/TUI carrier** — upstream does not publish the experimental durable
|
|
357
356
|
app (`pi-coding-agent` excludes `dist/experimental`; `pi-durable` on npm is the
|
|
358
357
|
library). Entwurf owns the six app files as canonical source (`pi/pi-durable/src`,
|
|
@@ -362,9 +361,9 @@ What the Entwurf package supplies:
|
|
|
362
361
|
(`pi/pi-durable/carrier-resolver.mjs`), which maps them to the published package's
|
|
363
362
|
own `dist/` as installed — never through a source checkout.
|
|
364
363
|
|
|
365
|
-
The upstream
|
|
366
|
-
|
|
367
|
-
(last published at v1.1.0) describe what the carrier is. Running the bare upstream app does not attach Entwurf's
|
|
364
|
+
The upstream library guide for the published 0.51.0 pairing is [v1.5.0](https://github.com/earendil-works/pi/blob/v1.5.0/packages/durable/README.md); for the published 0.52.0 SDK APIs use [v1.5.2](https://github.com/earendil-works/pi/blob/v1.5.2/packages/durable/README.md).
|
|
365
|
+
The [experimental app guide](https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/src/experimental/durable/README.md)
|
|
366
|
+
(last published at v1.1.0) records the app's origin: Entwurf's maintained app source, not a newer upstream app, is the current composition authority. Both guides describe what the carrier is. Running the bare upstream app does not attach Entwurf's
|
|
368
367
|
garden contact. Entwurf does not rebuild the controller/model/auth/storage system or
|
|
369
368
|
proxy a subscription; native execution, models, auth, queues, SQLite and recovery stay
|
|
370
369
|
upstream-owned.
|
|
@@ -377,7 +376,7 @@ carrier file, the license, the resolver and its relocation grammar are present
|
|
|
377
376
|
(`pi-durable-carrier-drift`); every declared SDK package resolves from the carrier
|
|
378
377
|
(`pi-durable-sdk-absent`); and every reachable physical edge — from the carrier to each package,
|
|
379
378
|
and from every package copy it reaches, at any depth, to its own `@earendil-works` dependencies —
|
|
380
|
-
lands on a package of the declared name at exactly the pinned version (`pi-durable-sdk-mismatch`). The members' declarations are exact in the
|
|
379
|
+
lands on a package of the declared name at exactly the pinned version (`pi-durable-sdk-mismatch`). The members' declarations are exact in the published 1.5.0 set and, measured on the registry tarballs (#158), in the 1.5.2 set; even so, the physical package each import binds must be checked. Sharing
|
|
381
380
|
one copy with an aligned host install of the same version is an ordinary layout. A mixed
|
|
382
381
|
tree (for example an override that binds pi-durable's `pi-ai` to an older published version) still
|
|
383
382
|
*imports* — which is why the edge check exists — and is refused by name.
|
|
@@ -459,9 +458,9 @@ tree (for example an override that binds pi-durable's `pi-ai` to an older publis
|
|
|
459
458
|
manifest field; it is a known constraint of supplying both from one package, accepted for 0.32,
|
|
460
459
|
not a claim that every load path is harmless.
|
|
461
460
|
|
|
462
|
-
The ordinary Pi CLI on PATH is a separate subject (§2
|
|
461
|
+
The ordinary Pi CLI on PATH is a separate subject (§2; historical 0.51.0 range <!-- entwurf:published @junghanacs/entwurf@0.51.0 pi 1.5.0 -->`>=1.5.0 <1.6`<!-- /entwurf:published -->, current 0.52.0 range `>=1.5.2 <1.6`): a host Pi is
|
|
463
462
|
not needed to run pi-durable, and a host inside the range need not be the same copy as the
|
|
464
|
-
carrier's.
|
|
463
|
+
carrier's. The native harnesses own credentials, model settings and their respective session stores — ordinary Pi JSONL and pi-durable SQLite; keep them
|
|
465
464
|
in place rather than copying credentials or migrating ordinary Pi transcripts. Native
|
|
466
465
|
recovery does not prove exactly-once Entwurf dispatch or inbox read recovery. The contact
|
|
467
466
|
provides garden attachment, visible identity and announce-only root-directed self-fetch.
|
|
@@ -472,12 +471,18 @@ proof ([#130](https://github.com/junghan0611/entwurf/issues/130)).
|
|
|
472
471
|
|
|
473
472
|
### SDK 1.5 development boundary (#155)
|
|
474
473
|
|
|
475
|
-
The
|
|
476
|
-
candidates
|
|
477
|
-
did not authorize an operating first write. A separately
|
|
478
|
-
LIVE were later measured as described below; neither
|
|
479
|
-
artifact. Installing an older package is not a data rollback. Follow the current evidence in
|
|
480
|
-
[#155](https://github.com/junghan0611/entwurf/issues/155), not the 1.3→1.4 receipt below.
|
|
474
|
+
The historical 0.51.0 instructions describe the SDK 1.5.0 set that #155 shipped; the #155 SDK
|
|
475
|
+
1.5 development candidates that preceded it were distinct subjects. Synthetic migration and
|
|
476
|
+
downgrade-refusal fixtures on `0408a66` did not authorize an operating first write. A separately
|
|
477
|
+
approved 1.5 operating window and Native LIVE were later measured as described below; neither made
|
|
478
|
+
the validation TGZ the published artifact. Installing an older package is not a data rollback. Follow the current evidence in
|
|
479
|
+
[#155](https://github.com/junghan0611/entwurf/issues/155), not the 1.3→1.4 receipt below. The #158 source candidate is a separate boundary, next.
|
|
480
|
+
|
|
481
|
+
### SDK 1.5.2 candidate boundary (#158)
|
|
482
|
+
|
|
483
|
+
The published 0.52.0 package (#158) pins the Pi SDK at exactly 1.5.2 (ten members) and declares host Pi `>=1.5.2 <1.6`. Publication does not install it on an operating host; an operating move requires its own installation and same-identity return receipts. The #155 receipts above prove their own subject only; none is inherited by 1.5.2.
|
|
484
|
+
|
|
485
|
+
What #158 measured so far and what it did not is recorded in [the durable-native boundary](./durable-native-support.md#sdk-152-candidate-boundary-158) and in the #158 thread: registry tarball integrity and member edges, an isolated six-member scheduler comparison and a native SQLite cross-version read/append check. The pre-version checkpoint's frozen FULL belongs to its own recorded worktree. The earlier validation candidate has its own floor and installation receipts; later source edits and the final carrier-bearing artifact require their own acceptance. Operating-scale and interrupted sessions are not certified by the bounded fixtures. An operating move follows the default radius below, each step with its own receipt.
|
|
481
486
|
|
|
482
487
|
### Default radius for a requested Pi SDK upgrade
|
|
483
488
|
|
|
@@ -493,17 +498,19 @@ The measured precedent is [#148's pre-cutover hold](https://github.com/junghan06
|
|
|
493
498
|
|
|
494
499
|
Push, release/tag/npm publication and retirement of any other citizen retain their separate authorization boundaries. This decision fixes the repeatable method; it does not pre-authorize those actions.
|
|
495
500
|
|
|
501
|
+
**Installation handoff: prepared is not installed.** State prominently whether the installer has actually run or is still waiting for the operator's approval. A prepared terminal, a pulled checkout or a printed resume command is not success. Keep the normal old-owner exit and consistent complete backup gates; before presenting the reopen step as ready, require the recorded installation exit 0 and coherent carrier/SDK verification. Then reopen the intended same session with its original explicit native module. Do not automatically send Enter, install or switch ownership to bridge an approval wait. The [0.51.0 ThinkPad incident](../CHANGELOG.md#install-handoff-incident-and-recovery--thinkpad-2026-11-04) records a new-source/old-dependency launch refusal and the separately authorized recovery; it is not evidence of data loss or an SDK regression.
|
|
502
|
+
|
|
496
503
|
**Recurring SDK rail.** At task start, agree the exact SDK and the scope of development, review and verification once; do not repeatedly ask for the existing-auth strategy. Inspect the published SDK set and upstream changes, isolate the development checkout from operating runtimes, and implement against the coherent pinned set in private HOME, PI and writable XDG roots. Use the gate's own temporary-directory constraints: the #155 FULL's deeper scratch produced 117/109-byte socket paths and EINVAL; run3 used `TMPDIR=/tmp` with each gate creating its own private scratch and passed. Preserve both `pnpm_config_verify_deps_before_run=error` and `npm_config_verify_deps_before_run=error`, managed builds and candidate seals; do not run offline gates under ambient CI.
|
|
497
504
|
|
|
498
505
|
The owned canonical app/TUI source is production responsibility; the SDK owns the execution engine. In independent upgrade review, compare old and new app-visible payload semantics against known fixtures and a separate official SDK oracle: more than 50 active entries on reopen and same-process conversation switch, head/reset and background-compaction retention, and the distinction from model memory. API names, typechecks or a QK that only fixes the new SDK's window can all be green while older display compatibility regresses. When official emission changes a generated digest, check its pin-linked mutant find literal and the existing manifest inventory together; this is not a new gate.
|
|
499
506
|
|
|
500
|
-
Freeze for sequential independent review, then affected PARTIAL groups, frozen FULL and applicable scripted/native-H carrier cells. Accept the **same reviewed validation TGZ** in the Linux container and rehearse ownership refusal/takeover in isolation. Measure SDK publication age early to plan the window, then recheck it fresh before installation; never automatically exempt the global policy.
|
|
507
|
+
Freeze for sequential independent review, then affected PARTIAL groups, frozen FULL and applicable scripted/native-H carrier cells. Accept the **same reviewed validation TGZ** in the Linux container and rehearse ownership refusal/takeover in isolation. Measure SDK publication age early to plan the window, then recheck it fresh before installation; never automatically exempt the global policy. Any operator-approved exception must name the exact packages and versions and be recorded with that upgrade's decision; it is not a standing patch-release exemption and not permission for an operating cutover. For #158 the operator explicitly approved the exact SDK 1.5.2 release-age exception on 2026-11-11: only those ten member/version terms are added to `minimumReleaseAgeExclude`, and the global policy and every other dependency remain subject to their own checks. That differs from #155's dated no-exception installation and authorises no operating write or publication.
|
|
501
508
|
|
|
502
509
|
In the named operating window, identify owners, obtain their normal exit and prove it, then take a consistent complete session backup including WAL/SHM before the first new-SDK write. Install a coherent host and validation TGZ, verify installed global root (GR), runtime and ownership independently, and perform only the approved takeover. Reopen the same identities with their original explicitly named native modules and existing auth; observe real turns and round trips before the Native LIVE floor. Public pi-durable `entwurf_resume_call` is bridge-only: it does not save or restore operator native modules. If one is needed, use the original explicit CLI entry; do not infer a profile or kill a citizen automatically. One approval can cover the named scope, sibling roles and fixture work, but not bypass normal exit, the first-write decision, or separate push/npm authority.
|
|
503
510
|
|
|
504
511
|
For each named Native fixture, record the runtime root and SDK actually selected separately from the operator installation: a private PATH can select the genuine source CLI while an ordinary Pi extension still comes from an older installed GR. Do not substitute a wrapper or rewire the operating host to make the evidence agree. A planned source-CLI Native run is not a completed receipt.
|
|
505
512
|
|
|
506
|
-
**#155 measured example (2026-11-03; before the D1 display repair):** at 20:47 KST host Pi 1.5.0 and the unpublished, addon-less 0.51.0 validation TGZ (SHA-256 `199e5f41d20e5b2665125868b7e3e27740e5985f7a25ac61902126b7ed638bf5`) were installed, not the final publication artifact. Four owners exited normally; the 171-file durable backup's source-before/backup/source-after manifests matched (`5c08bed1968fd1e26baa58112f0ce5c36874e147ccc5e9b4c69ec1a0d9070a46`); two Pi transcripts were also backed up. All ten SDK members were freshly measured at 24.70–24.84 hours old, without a policy exception. The P5 Native LIVE cut recorded MUST 24/0/0 and BEHAVIOR 1/0/0 (pass/fail/skip) on its own sealed candidate, not the prepared SHA. Native-host criterion 4 separately measured a fresh Claude Code fixture's actual installed 0.51 MCP child and strict doctor; existing pre-cutover Claude sessions retained their loaded 0.50 bridge. Neither the 20:47 installation nor P5 proves that the later D1 display repair is operating: the D1-bearing artifact must first be installed, and the human must then restart the process; restarting against the earlier artifact still loads the old app. See [0.51.0 release evidence](../CHANGELOG.md#
|
|
513
|
+
**#155 measured example (2026-11-03; before the D1 display repair):** at 20:47 KST host Pi 1.5.0 and the unpublished, addon-less 0.51.0 validation TGZ (SHA-256 `199e5f41d20e5b2665125868b7e3e27740e5985f7a25ac61902126b7ed638bf5`) were installed, not the final publication artifact. Four owners exited normally; the 171-file durable backup's source-before/backup/source-after manifests matched (`5c08bed1968fd1e26baa58112f0ce5c36874e147ccc5e9b4c69ec1a0d9070a46`); two Pi transcripts were also backed up. All ten SDK members were freshly measured at 24.70–24.84 hours old, without a policy exception. The P5 Native LIVE cut recorded MUST 24/0/0 and BEHAVIOR 1/0/0 (pass/fail/skip) on its own sealed candidate, not the prepared SHA. Native-host criterion 4 separately measured a fresh Claude Code fixture's actual installed 0.51 MCP child and strict doctor; existing pre-cutover Claude sessions retained their loaded 0.50 bridge. Neither the 20:47 installation nor P5 proves that the later D1 display repair is operating: the D1-bearing artifact must first be installed, and the human must then restart the process; restarting against the earlier artifact still loads the old app. See [0.51.0 release evidence](../CHANGELOG.md#0510--2026-11-04) for scopes and receipts.
|
|
507
514
|
|
|
508
515
|
The Windows-bearing release TGZ, its Linux acceptance, exact prepared-SHA CI, installed-host acceptance and publication/registry proof are separate later evidence. Re-measure owners, loaded bytes, version/integrity, backup and candidate evidence on every upgrade; this ordering does not guarantee success for a future SDK.
|
|
509
516
|
|
|
@@ -773,7 +780,7 @@ as a note rather than a fault.
|
|
|
773
780
|
|
|
774
781
|
**entwurf sets no OMP version floor in code, and will not grow one on schedule.** Detection is
|
|
775
782
|
presence-only (`command -v "${OMP_BIN:-omp}"`); there is no `entwurf.ompFloor`, no coherence
|
|
776
|
-
gate, and no exact pin — unlike Node (`engines.node`), pi (
|
|
783
|
+
gate, and no exact pin — unlike Node (`engines.node`), pi (the #158 checkout's predicate, `>=1.5.2 <1.6`) and Claude Code
|
|
777
784
|
(`entwurf.claudeCodeFloor`), each of which has an enforcement point. That asymmetry is a
|
|
778
785
|
decision, not an omission. A floor is the answer to a vendor that fails SILENTLY — Claude Code
|
|
779
786
|
earned one because an older binary validates the exec manifest, drops `args` at runtime, and
|
|
@@ -988,7 +995,7 @@ real initial Pi outside `codex`, then omitted-placement Codex and outbound Pi in
|
|
|
988
995
|
callbacks and addressed delivery both ways. `DELIVERY.md` owns that 57-assertion receipt, its four
|
|
989
996
|
coordinates, and its digest as history. The caller-seat re-run is **accepted** — 2026-09-16, 56
|
|
990
997
|
assertions for the seat topology and 65 for the caller-DIRECTORY axis, both exit 0 — and it, not the
|
|
991
|
-
fixed home, is the current contract (`DELIVERY.md`,
|
|
998
|
+
fixed home, is the current contract (`DELIVERY.md`, BASELINE.md's release-host table (Codex caller-seat rows), `VERIFY.md` release
|
|
992
999
|
acceptance).
|
|
993
1000
|
|
|
994
1001
|
`entwurf_fresh_call` accepts backend `codex` — supported in 0.21.0 — and requires an explicit model. Its
|
|
@@ -1068,7 +1075,7 @@ LIVE=1 AGY_CONVERSATION_ID=<id> entwurf smoke-agy-native-push-live
|
|
|
1068
1075
|
|
|
1069
1076
|
ACP turns are CERTIFIED on Linux; on macOS they are NOT CERTIFIED —
|
|
1070
1077
|
pending physical host. Mux (visible fresh / resume) is the same state.
|
|
1071
|
-
A CI runner has no Claude
|
|
1078
|
+
A CI runner has no Claude login.
|
|
1072
1079
|
|
|
1073
1080
|
Claude uses the operator's existing local Claude authentication:
|
|
1074
1081
|
|
|
@@ -1076,16 +1083,10 @@ Claude uses the operator's existing local Claude authentication:
|
|
|
1076
1083
|
LIVE=1 entwurf smoke-acp-provider-live
|
|
1077
1084
|
```
|
|
1078
1085
|
|
|
1079
|
-
Cortex
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> \
|
|
1084
|
-
entwurf smoke-acp-cortex-live
|
|
1085
|
-
```
|
|
1086
|
-
|
|
1087
|
-
The aggregate release gate is Claude-backed and does not run Cortex automatically.
|
|
1088
|
-
Its silence is not a Cortex PASS.
|
|
1086
|
+
Cortex ACP is disabled. Existing Cortex configuration and credentials are not modified.
|
|
1087
|
+
Entwurf fresh/resume exact admission refuses retired Cortex ids; the ACP adapter also
|
|
1088
|
+
refuses a Cortex id that reaches it, without a Claude fallback. Pi's own direct `--model`
|
|
1089
|
+
fuzzy selection precedes that adapter; its result for a typed retired id is unmeasured here.
|
|
1089
1090
|
|
|
1090
1091
|
### Backend prerequisites and resolver
|
|
1091
1092
|
|
|
@@ -1097,12 +1098,10 @@ operator-installed Claude runtime. Resolution order:
|
|
|
1097
1098
|
2. `require.resolve(...)` against the bundled `@agentclientprotocol/claude-agent-acp`.
|
|
1098
1099
|
3. `PATH:claude-agent-acp` fallback if package resolution fails.
|
|
1099
1100
|
|
|
1100
|
-
The curated Claude ids are `claude-opus-5-5`, `claude-sonnet-5-5`,
|
|
1101
|
-
`claude-fable-5-1
|
|
1102
|
-
`
|
|
1103
|
-
|
|
1104
|
-
an authenticated connection; the env override wins. Codex and Antigravity are
|
|
1105
|
-
native-push citizens (§4c/§5), not ACP backends.
|
|
1101
|
+
The curated Claude ids are `claude-opus-5-5`, `claude-sonnet-5-5`,
|
|
1102
|
+
`claude-fable-5-1`, and `claude-fable-5-2`. Cortex ACP is disabled; a legacy
|
|
1103
|
+
`entwurfProvider.cortexConnection` does not select a backend or block Claude.
|
|
1104
|
+
Codex and Antigravity are native-push citizens (§4c/§5), not ACP backends.
|
|
1106
1105
|
|
|
1107
1106
|
### Settings reference and backend-specific keys
|
|
1108
1107
|
|
|
@@ -1137,15 +1136,11 @@ retired bundled `session-bridge` entry. It is not a general MCP catalog.
|
|
|
1137
1136
|
user augment. Large Claude `_meta.systemPrompt` carriers can route OAuth sessions
|
|
1138
1137
|
to metered “extra usage” billing.
|
|
1139
1138
|
|
|
1140
|
-
`entwurfProvider` is one block, not a promise that every key shapes both backends.
|
|
1141
1139
|
`tools`, `permissionAllow`, `disallowedTools`, `settingSources`, `skillPlugins`
|
|
1142
|
-
and `appendSystemPrompt` are Claude's declaration surface
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
transports. `compaction` is a **top-level pi key**, not a backend knob. Unknown
|
|
1147
|
-
and retired keys are ignored; a wrong-backend key can fail silently, so check
|
|
1148
|
-
which backend consumes it before diagnosing bridge failure.
|
|
1140
|
+
and `appendSystemPrompt` are Claude's declaration surface. `mcpServers` carries
|
|
1141
|
+
explicit servers. `compaction` is a **top-level pi key**, not a backend knob.
|
|
1142
|
+
The retired `cortexConnection` key is not consumed by Claude and does not enable
|
|
1143
|
+
Cortex ACP; unknown and retired keys are ignored.
|
|
1149
1144
|
|
|
1150
1145
|
### Backend operation and containment
|
|
1151
1146
|
|
|
@@ -1160,12 +1155,8 @@ Read/Bash/Edit/Write; `Skill` is added for non-empty `skillPlugins`.
|
|
|
1160
1155
|
The launch env sets `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1`: vendor-managed
|
|
1161
1156
|
background work is off, not arbitrary processes a command detaches itself.
|
|
1162
1157
|
|
|
1163
|
-
Cortex uses its native tools, session-private HOME / `SNOWFLAKE_HOME`, and private
|
|
1164
|
-
`cortex/mcp.json` projection because it ignores wire `mcpServers`. Only the bridge
|
|
1165
|
-
receives the real operator HOME needed for the garden store; `CORTEX_HOME` is
|
|
1166
|
-
refused, including an empty value, because it bypasses containment.
|
|
1167
1158
|
[ACP backend rail — Shipped adapters](./acp-backend-rail.md#shipped-adapters)
|
|
1168
|
-
owns the exact overlay/carrier
|
|
1159
|
+
owns the exact Claude overlay/carrier mechanics and audit limits.
|
|
1169
1160
|
|
|
1170
1161
|
Entwurf owns no memory layer or memory DB, and no backend is another's memory
|
|
1171
1162
|
authority. Operator semantic-memory / Denote tools remain host-native skills,
|
|
@@ -1200,7 +1191,7 @@ Non-empty `skillPlugins` auto-adds `Skill` to `tools` and `Skill(*)` to
|
|
|
1200
1191
|
path or missing `.claude-plugin/plugin.json` before starting the session; the
|
|
1201
1192
|
SDK, not the bridge, validates plugin.json contents and SKILL.md bodies.
|
|
1202
1193
|
Start a fresh Claude session and ask it to list callable skills. The operator
|
|
1203
|
-
judgement is
|
|
1194
|
+
judgement is asking the session which skills it can call; it is not a named row in [VERIFY](../VERIFY.md).
|
|
1204
1195
|
This is Claude-only; Codex uses native `~/.codex/skills/` passthrough.
|
|
1205
1196
|
[agent-config](https://github.com/junghan0611/agent-config) is a real multi-harness
|
|
1206
1197
|
consumer, not a requirement for this setup.
|
|
@@ -1226,8 +1217,7 @@ Bridge identity, pi context, `~/AGENTS.md`, cwd AGENTS and date/cwd ride a one-s
|
|
|
1226
1217
|
first-user prepend (`pi-context-augment.ts`), not the system carrier. It removes
|
|
1227
1218
|
duplicate cwd AGENTS already in `<project-context ...>`. Its descriptions do not
|
|
1228
1219
|
grant tools: the actual callable schema is truth (`read` / `Read` / `exec_command`,
|
|
1229
|
-
MCP only when schema-visible).
|
|
1230
|
-
has no system-prompt carrier; Claude uses `_meta.systemPrompt`.
|
|
1220
|
+
MCP only when schema-visible). Claude uses `_meta.systemPrompt` for its engraving.
|
|
1231
1221
|
|
|
1232
1222
|
### Session reuse and compaction
|
|
1233
1223
|
|
|
@@ -1243,7 +1233,12 @@ owns the reuse policy and distinguishes live mapping from semantic recall.
|
|
|
1243
1233
|
**Entwurf does not implement compaction.** A backend's native compaction preserves
|
|
1244
1234
|
the pi session/mapping; configure its policy through that backend's own interface,
|
|
1245
1235
|
not a pi-side JSONL summary. The bridge exposes no backend-specific compaction
|
|
1246
|
-
knobs.
|
|
1236
|
+
knobs. One fixed exception on the Claude ACP rail: its adapter's idle `/compact` (an autonomous
|
|
1237
|
+
turn on an idle session) is turned off inline (`settings.idleCompaction: false`), because pi would
|
|
1238
|
+
not see that turn; in-prompt auto-compaction stays the backend's, and a managed settings tier would
|
|
1239
|
+
still win over the inline value ([rail §Capability posture](./acp-backend-rail.md#capability-posture)).
|
|
1240
|
+
Pi's own summaries on a Claude ACP model (`/compact`, the threshold, overflow recovery, `/tree`) are not that backend compaction: each runs as a one-shot tool-less turn on a new child, and the session's retained child is closed after a compaction ([rail §11-10](./acp-backend-rail.md)).
|
|
1241
|
+
The ACP footer uses `usage_update.used / size` (backend prompt/tools/cache/
|
|
1247
1242
|
session included) with `[entwurf:usage]` diagnostics. Near limit choose a visible
|
|
1248
1243
|
action: clear, open a new session with a different model, or let the backend
|
|
1249
1244
|
compact natively.
|
|
@@ -1266,7 +1261,7 @@ harness unit FAIL with the §1 Darwin wording — upgrade does not mint a
|
|
|
1266
1261
|
rail receipt. Doctors refuse on Darwin until a physical-host receipt
|
|
1267
1262
|
exists.
|
|
1268
1263
|
|
|
1269
|
-
If install or doctor reports an
|
|
1264
|
+
If install or doctor reports an active citizen generation it cannot read, first distinguish reader-version skew from a genuinely unreadable generation. Roll a stale reader forward and preserve identities and records; only a genuinely unreadable generation, as the [fresh-cut policy](./fresh-cut-policy.md) prescribes, takes the procedure below. Do not fresh-cut merely because an SDK or checkout changed. For a genuinely unreadable generation, do not edit
|
|
1270
1265
|
records by hand. Close pi, Claude, Copilot, OMP, agy, and Codex sessions first, run
|
|
1271
1266
|
`entwurf meta-bridge-fresh-cut`, and read its exit status before any install. Then choose the
|
|
1272
1267
|
installation mode you actually own:
|
|
@@ -1316,7 +1311,7 @@ An already-running process keeps the modules it loaded, whatever is on disk. So,
|
|
|
1316
1311
|
|
|
1317
1312
|
1. Build once from the checkout (`pnpm run build-bridge`, or its body `bash scripts/with-dist-lock.sh bash scripts/build-bridge.sh` when no package lifecycle should run).
|
|
1318
1313
|
2. Compare the whole compiled output with the accepted candidate's, as a file-set and hash manifest. A hash of the selected entry alone is not enough: the entry can stay identical while a helper it loads differs. Check the pinned SDK set separately, and that the new process started after the build.
|
|
1319
|
-
3. Close the visible session and reopen it with its own native same-id resume. Closing a working sibling is an operator decision for that session, not something an agent does because new bytes landed.
|
|
1314
|
+
3. Close the visible session and reopen it with its own native same-id resume. Closing a working sibling is an operator decision for that session, not something an agent does because new bytes landed. For a pi-durable session launched with an operator module, reopen the intended native session with its original explicit CLI and module after verifying the installation; garden-id resume does not restore modules.
|
|
1320
1315
|
4. Confirm `entwurf_self` names the same garden id, and that the receiver/owner join points at the new process, whose bridge child started after the build.
|
|
1321
1316
|
|
|
1322
1317
|
A per-component setup PASS or a `git pull` does not show which bytes a running bridge loaded. On 2026-10-20 one host of the #107 pair still selected a pre-feature dist after a pull and a green setup. The cause was not traced. A hash check caught it before the first message, and a manual build and visible resume fixed it. Entwurf adds no auto-build, reload or watcher for this.
|
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
"editing": "Preserve decoded strings exactly unless an explicit semantic amendment is intended. Review related consumers and independent oracles atomically. Accepted payload changes advance revision; history-only edits, routine releases and unrelated code changes do not. schemaVersion tracks reader/structural compatibility. A slotted hint holds only the text around its slot: the slot value (for record-less-socket, the fresh-cut command in both invocation forms) is supplied by its code owner and never copied here. Metadata is not delivered to models."
|
|
7
7
|
},
|
|
8
8
|
"schemaVersion": 6,
|
|
9
|
-
"revision":
|
|
9
|
+
"revision": 20,
|
|
10
10
|
"history": [
|
|
11
|
+
{
|
|
12
|
+
"revision": 20,
|
|
13
|
+
"source": "b9fa0d2aa43ebefc1c3bd5bc864c84965b52289a",
|
|
14
|
+
"issue": "https://github.com/junghan0611/entwurf/issues/159",
|
|
15
|
+
"change": "Align delivery descriptions with completion and reply routing while preserving receipt, rail, and lifecycle boundaries."
|
|
16
|
+
},
|
|
11
17
|
{
|
|
12
18
|
"revision": 19,
|
|
13
19
|
"source": "2734f524bfeca6eb1a0ae092494d45576af9ebef",
|
|
@@ -126,11 +132,11 @@
|
|
|
126
132
|
"tools": {
|
|
127
133
|
"entwurf_v2": {
|
|
128
134
|
"descriptions": {
|
|
129
|
-
"mcp": "
|
|
130
|
-
"durable": "Send
|
|
135
|
+
"mcp": "Send, reply or hand off to an existing garden id; the decider selects the live rail (live socket citizen → control-socket send; deliverable self-fetch citizen → meta-bridge mailbox; probe-alive native-push citizen → direct injection into its conversation) and reports ONE outcome, named per rail: a control receiver's acceptance boundary (`sent` / `queued-steer` / `queued-follow-up` / `accepted-unknown-boundary` — those four, or `mailbox-enqueued` on a re-resolved mailbox leg), a mailbox ENQUEUE receipt, a native-push INJECTION, a rejection, or lock-dirty. A receipt is not a reply or proof of reading. A local final is not delivery: when asked to reply, call entwurf_v2 for the requested recipient (normally the replyable sender); honor routing and no-send instructions. Discover existing targets with entwurf_peers. A live socket can receive fire-and-forget; unsupported means only \"no control-socket probe,\" not reachability. For a target without a live socket, the THIRD RESULT is rejection if neither mailbox nor native-push is available. An inactive self-fetch citizen is mailbox-undeliverable; native-push has NO mailbox: dead → native-push-target-dead; indeterminate → native-push-probe-indeterminate; agy (paused) → native-push-paused, no probe. A dormant socket rejects this delivery verb as dormant-fire-forget-unsupported. This delivery verb never launches or resumes a process. Only on an explicit operator request, entwurf_resume_call may visibly reopen a proven-dormant Pi, pi-durable or Claude Code citizen under the same id, submitting no prompt; that lifecycle is separate from delivery. A control-socket-DOMAIN dispatch takes a target lock; mailbox and native-push rails are lock-free. mode applies only to a CONTROL-SOCKET send. wants_reply requests a response without authorizing an action or requiring an automatic acknowledgment; an absent or false marker does not cancel a request in the message. message caps at 16000 chars; send an artifact path + digest for more.",
|
|
136
|
+
"durable": "Send, reply or hand off to an existing garden id through entwurf-bridge (intent fire-and-forget), returning a delivery receipt or named rejection. The receipt is not a reply or proof of reading. For a requested reply, send to the authorized recipient, normally the replyable sender; a local final is not delivery. Honor routing and no-send instructions; an absent or false wants_reply does not cancel a request, and an address alone needs no ACK."
|
|
131
137
|
},
|
|
132
138
|
"parameters": {
|
|
133
|
-
"intent": "fire-and-forget
|
|
139
|
+
"intent": "fire-and-forget sends, replies or hands off to an existing target after a current rail check. unsupported is not a reachability verdict: self-fetch uses a deliverable mailbox or rejects as mailbox-undeliverable; native-push injects when alive, rejects dead as native-push-target-dead and indeterminate as native-push-probe-indeterminate; paused agy rejects as native-push-paused before probing. wants_reply requests a response, not a wait. This delivery verb never launches or resumes a process; a dormant socket rejects as dormant-fire-forget-unsupported.",
|
|
134
140
|
"message": "Message / prompt to the colleague. In familiar language and natural sentences, convey what matters now and the context they need. Keep identifiers and evidence exact; trim repetition, not meaning. Hard cap 16000 chars; for larger payloads send a file/artifact path plus digest.",
|
|
135
141
|
"mode": "Injection style for a CONTROL-SOCKET send only: steer (ask the receiver's steering queue) or follow_up (ask its follow-up queue). NEITHER is an interrupt: a busy receiver drains steering after each turn and follow-ups only when its inner loop ends, so a later steer overtakes every earlier follow_up and there is no order between the two. Both queues are volatile process memory — an abort drops what is in them. The receipt names which queue accepted the message (`queued-steer` / `queued-follow-up`); an idle receiver answers `sent`, meaning the turn was TRIGGERED, not that the model has seen the text. The mailbox and native-push plans carry no mode, so it has no effect on those rails.",
|
|
136
142
|
"target": "Target garden id (use entwurf_peers to discover)",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// This module owns the `AcpConnectionLike` seam so the backend and the live
|
|
17
17
|
// smokes both drive ONE adapter — the SDK method-name mapping lives here only,
|
|
18
18
|
// and the backend's orchestration + the gate fakes stay untouched.
|
|
19
|
-
import { AGENT_METHODS, CLIENT_METHODS, client
|
|
19
|
+
import { AGENT_METHODS, CLIENT_METHODS, client } from "@agentclientprotocol/sdk";
|
|
20
20
|
/**
|
|
21
21
|
* Production factory — wrap the SDK 1.1 fluent `client()` into the
|
|
22
22
|
* `AcpConnectionLike` seam the backend (and the live smokes) drive.
|
|
@@ -23,7 +23,7 @@ import { fileURLToPath } from "node:url";
|
|
|
23
23
|
import { RequestError } from "@agentclientprotocol/sdk";
|
|
24
24
|
import { enrichMcpServersWithEnvelope } from "./config.js";
|
|
25
25
|
import { loadEngraving } from "./engraving.js";
|
|
26
|
-
import { CORTEX_MODEL_PREFIX, curatedClaudeModels, curatedCortexModels,
|
|
26
|
+
import { CLAUDE_ACP_EFFORT_VALUES, CORTEX_MODEL_PREFIX, curatedClaudeModels, curatedCortexModels, LEGACY_CORTEX_MODEL_IDS, SUPPORTED_ANTHROPIC_MODEL_IDS, } from "./models.js";
|
|
27
27
|
import { claudeLaunchEnvDefaults, ensureClaudeConfigOverlay, ensureCortexDualHomeOverlay } from "./overlay.js";
|
|
28
28
|
import { buildClaudeSessionMeta } from "./tool-surface.js";
|
|
29
29
|
// POSIX-safe single-quote wrapper for shell arg interpolation. Byte-for-byte
|
|
@@ -114,7 +114,7 @@ export const CLAUDE_FOREGROUND_ONLY_ENV = { CLAUDE_CODE_DISABLE_BACKGROUND_TASKS
|
|
|
114
114
|
* The set-model answer is read back before the prompt (#128). claude-agent-acp resolves the
|
|
115
115
|
* requested id onto one of the rows it offers and answers with that row's `value` as the model
|
|
116
116
|
* option's `currentValue` (0.85.1 `dist/acp-agent.js:5459-5499`). `[측정 2026-10-12]` 0.85.1, no
|
|
117
|
-
* prompt: `claude-fable-5-1` answers its own row, while `claude-sonnet-5-5` and `claude-opus-5-5`
|
|
117
|
+
* prompt: `claude-fable-5-1` answers its own row (and, at 0.106.0 #142, so does `claude-fable-5-2`), while `claude-sonnet-5-5` and `claude-opus-5-5`
|
|
118
118
|
* answer the alias rows `sonnet` and `opus`, named `Sonnet 5.5` and `Opus 5.5`. The id an alias row
|
|
119
119
|
* resolves to (`ModelInfo.resolvedModel`) is not on the ACP wire, so an alias answer is accepted
|
|
120
120
|
* only as the exact measured row, value AND name, declared here for the requested id. No other
|
|
@@ -148,6 +148,46 @@ export function confirmClaudeSetModelAnswer(response, nativeModelId) {
|
|
|
148
148
|
`name the requested model. Refusing before the prompt so a different model cannot answer under this id; ` +
|
|
149
149
|
`the installed driver and the curated id disagree, and repeating the turn cannot change that.`);
|
|
150
150
|
}
|
|
151
|
+
const CLAUDE_EFFORT_VALUE_SET = new Set(CLAUDE_ACP_EFFORT_VALUES);
|
|
152
|
+
/** The named refusal for a thinking level ACP Claude cannot carry (#142). The text is fixed and
|
|
153
|
+
* must read as NON-transient to pi's retry and overflow classifiers: replaying the turn cannot
|
|
154
|
+
* change the level that was asked for. */
|
|
155
|
+
export const ACP_THINKING_LEVEL_UNSUPPORTED = "acp-thinking-level-unsupported";
|
|
156
|
+
/**
|
|
157
|
+
* Map pi's requested level to the claude-agent-acp effort value, or refuse by name (#142).
|
|
158
|
+
* The value is the registered row's `thinkingLevelMap` entry for that level — the documented pi
|
|
159
|
+
* meaning of the map — and must be one of the measured effort values. `undefined` (pi off, or no
|
|
160
|
+
* level) is refused rather than left at the vendor `default`: that would let the turn run at a
|
|
161
|
+
* depth nobody chose while the operator believes a level was applied.
|
|
162
|
+
*/
|
|
163
|
+
export function resolveClaudeEffort(modelId, reasoning, thinkingLevelMap) {
|
|
164
|
+
const value = reasoning === undefined ? undefined : thinkingLevelMap?.[reasoning];
|
|
165
|
+
if (typeof value === "string" && CLAUDE_EFFORT_VALUE_SET.has(value))
|
|
166
|
+
return value;
|
|
167
|
+
const asked = reasoning === undefined
|
|
168
|
+
? "no thinking level (pi sends none for `off`, and none when no level is set; the two are indistinguishable here)"
|
|
169
|
+
: `thinking level ${JSON.stringify(reasoning)}`;
|
|
170
|
+
throw new Error(`entwurf: ${ACP_THINKING_LEVEL_UNSUPPORTED} — ${modelId} over Claude ACP carries effort ` +
|
|
171
|
+
`${CLAUDE_ACP_EFFORT_VALUES.join("/")} only, and was asked for ${asked}. Choose one of those levels; ` +
|
|
172
|
+
`the turn was not sent, and repeating it at the same level cannot change that.`);
|
|
173
|
+
}
|
|
174
|
+
function claudeEffortRefusal(modelId, value, what, cause) {
|
|
175
|
+
const error = new Error(`entwurf: claude-agent-acp ${what} for effort ${JSON.stringify(value)} on ${modelId}. Refusing before the prompt ` +
|
|
176
|
+
`so the turn cannot run at an effort nobody chose; repeating the turn cannot change the driver's answer.`);
|
|
177
|
+
if (cause !== undefined)
|
|
178
|
+
error.cause = cause;
|
|
179
|
+
return error;
|
|
180
|
+
}
|
|
181
|
+
/** Refuse unless the set-effort answer reads back the requested value. */
|
|
182
|
+
export function confirmClaudeEffortAnswer(response, modelId, value) {
|
|
183
|
+
const configOptions = response?.configOptions;
|
|
184
|
+
const effort = Array.isArray(configOptions)
|
|
185
|
+
? configOptions.find((option) => option?.id === "effort")
|
|
186
|
+
: undefined;
|
|
187
|
+
if (effort?.currentValue === value)
|
|
188
|
+
return;
|
|
189
|
+
throw claudeEffortRefusal(modelId, value, effort == null ? "answered with no effort option" : `answered effort ${jsonText(effort.currentValue)}`);
|
|
190
|
+
}
|
|
151
191
|
export const claudeAdapter = {
|
|
152
192
|
backend: "claude",
|
|
153
193
|
// Claude owns its UNPREFIXED curated ids only (rail “Adapter contract”). The native id
|
|
@@ -206,6 +246,23 @@ export const claudeAdapter = {
|
|
|
206
246
|
});
|
|
207
247
|
confirmClaudeSetModelAnswer(answer, nativeModelId);
|
|
208
248
|
},
|
|
249
|
+
resolveThinking({ modelId, reasoning, thinkingLevelMap }) {
|
|
250
|
+
return resolveClaudeEffort(modelId, reasoning, thinkingLevelMap);
|
|
251
|
+
},
|
|
252
|
+
async enforceThinking({ connection, acpSessionId, modelId, value }) {
|
|
253
|
+
const setConfig = connection.setSessionConfigOption;
|
|
254
|
+
if (typeof setConfig !== "function") {
|
|
255
|
+
throw new Error(`setSessionConfigOption unsupported — cannot set effort for ${modelId}`);
|
|
256
|
+
}
|
|
257
|
+
let answer;
|
|
258
|
+
try {
|
|
259
|
+
answer = await setConfig.call(connection, { sessionId: acpSessionId, configId: "effort", value });
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
throw claudeEffortRefusal(modelId, value, "refused the set", err);
|
|
263
|
+
}
|
|
264
|
+
confirmClaudeEffortAnswer(answer, modelId, value);
|
|
265
|
+
},
|
|
209
266
|
configSignatureFields(_adapterSettings) {
|
|
210
267
|
// Claude folds no extra backend-specific fields beyond backend + nativeModelId
|
|
211
268
|
// (which backend.ts adds). A future backend reads its own stable id off
|
|
@@ -226,21 +283,15 @@ export const claudeAdapter = {
|
|
|
226
283
|
// ---------------------------------------------------------------------------
|
|
227
284
|
// Registry — modelId → adapter
|
|
228
285
|
// ---------------------------------------------------------------------------
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
// cortex adapter — Snowflake Cortex Code, the first non-claude backend on the
|
|
235
|
-
// rail (docs/acp-backend-rail.md, “Shipped adapters”). It adds ZERO to the common layer:
|
|
236
|
-
// everything cortex-specific lives here + models.ts + overlay.ts + the gates.
|
|
237
|
-
// ---------------------------------------------------------------------------
|
|
238
|
-
const SUPPORTED_CORTEX_IDS = new Set(SUPPORTED_CORTEX_MODEL_IDS);
|
|
286
|
+
// The former Cortex implementation is retained privately for historical source
|
|
287
|
+
// continuity only. The exported adapter below denies every side-effecting entry,
|
|
288
|
+
// and the production registry does not contain this implementation.
|
|
289
|
+
const SUPPORTED_CORTEX_IDS = new Set(LEGACY_CORTEX_MODEL_IDS);
|
|
239
290
|
/** The env var an operator sets to pin a Snowflake connection per-shell without
|
|
240
291
|
* editing settings.json. Wins over `entwurfProvider.cortexConnection`. The
|
|
241
292
|
* ENTWURF_ACP_* convention (PR #40's legacy PI_SHELL_ACP* cortex-connection var renamed). */
|
|
242
293
|
export const CORTEX_CONNECTION_ENV = "ENTWURF_ACP_CORTEX_CONNECTION";
|
|
243
|
-
|
|
294
|
+
const retiredCortexAdapter = {
|
|
244
295
|
backend: "cortex",
|
|
245
296
|
// Cortex owns the reserved `cortex-` prefix (rail “Adapter contract”). routeModel strips it to the
|
|
246
297
|
// native id: `cortex-auto` → "auto", `cortex-claude-sonnet-5` → "claude-sonnet-5".
|
|
@@ -373,7 +424,29 @@ export const cortexAdapter = {
|
|
|
373
424
|
// server rejects a prompt with, so every cortex failure keeps the verbatim-first
|
|
374
425
|
// errorMessage. Borrowing claude's `incomplete_tool_call` reader would be a guess.
|
|
375
426
|
};
|
|
376
|
-
const
|
|
427
|
+
const cortexDisabled = () => {
|
|
428
|
+
throw new Error("entwurf: cortex-acp-disabled: Cortex ACP is not supported");
|
|
429
|
+
};
|
|
430
|
+
/** Retained exported shape refuses the retired adapter before a launch or overlay can be made. */
|
|
431
|
+
export const cortexAdapter = {
|
|
432
|
+
...retiredCortexAdapter,
|
|
433
|
+
routeModel: () => undefined,
|
|
434
|
+
curatedModels: () => [],
|
|
435
|
+
resolveAdapterSettings: cortexDisabled,
|
|
436
|
+
resolveLaunch: cortexDisabled,
|
|
437
|
+
launchEnvDefaults: cortexDisabled,
|
|
438
|
+
ensureOverlay: cortexDisabled,
|
|
439
|
+
loadCarrier: cortexDisabled,
|
|
440
|
+
buildSessionMeta: cortexDisabled,
|
|
441
|
+
enforceModel: cortexDisabled,
|
|
442
|
+
configSignatureFields: cortexDisabled,
|
|
443
|
+
};
|
|
444
|
+
const ADAPTERS = [claudeAdapter];
|
|
445
|
+
/** A declared backend is only a guard, never a replacement routing authority. */
|
|
446
|
+
export function assertSupportedDeclaredBackend(backend) {
|
|
447
|
+
if (backend?.toLowerCase() === "cortex")
|
|
448
|
+
cortexDisabled();
|
|
449
|
+
}
|
|
377
450
|
/**
|
|
378
451
|
* Resolve the backend adapter that owns `modelId`.
|
|
379
452
|
*
|
|
@@ -385,6 +458,9 @@ const ADAPTERS = [claudeAdapter, cortexAdapter];
|
|
|
385
458
|
* so callers get `{ adapter, nativeModelId }` and never re-parse the id string.
|
|
386
459
|
*/
|
|
387
460
|
export function resolveAcpBackendAdapter(modelId) {
|
|
461
|
+
if (modelId.toLowerCase().startsWith(CORTEX_MODEL_PREFIX)) {
|
|
462
|
+
throw new Error(`entwurf: cortex-acp-disabled: ${JSON.stringify(modelId)} is not supported`);
|
|
463
|
+
}
|
|
388
464
|
const matches = [];
|
|
389
465
|
for (const adapter of ADAPTERS) {
|
|
390
466
|
const routed = adapter.routeModel(modelId);
|