@junghanacs/entwurf 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +338 -0
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +15 -0
- package/FAQ.md +3 -3
- package/README.md +30 -16
- package/VERIFY.md +14 -11
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +29 -3
- package/docs/setup-clean-host.md +31 -23
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +46 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/model-ids.js +67 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +9 -20
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +4 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +11 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +27 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +2 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +99 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +13 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +178 -25
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-classify.js +44 -0
- package/mcp/entwurf-bridge/tsconfig.build.json +5 -0
- package/package.json +20 -15
- package/pi/pi-durable/bootstrap.mjs +12 -8
- package/pi/pi-durable/carrier/runtime.js +52 -2
- package/pi/pi-durable/carrier/subagent.js +6 -1
- package/pi/pi-durable/carrier/tui.js +31 -4
- package/pi/pi-durable/carrier-relocation.mjs +2 -2
- package/pi/pi-durable/overlay/upstream-pin.json +79 -36
- package/pi-extensions/lib/acp/backend-adapter.ts +52 -2
- package/pi-extensions/lib/acp/model-ids.js +77 -0
- package/pi-extensions/lib/acp/models.ts +15 -22
- package/pi-extensions/lib/codex-fresh-preflight.ts +4 -3
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +12 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +25 -1
- package/pi-extensions/lib/herdr-fresh-call.ts +5 -0
- package/pi-extensions/lib/mux-fresh-call.ts +3 -0
- package/pi-extensions/lib/native-push/codex-ws-client.ts +129 -18
- package/pi-extensions/lib/pi-durable-runtime.ts +4 -4
- package/pi-extensions/lib/resume-launch-identity.ts +12 -4
- package/pi-extensions/meta-bridge-pi-durable.ts +230 -34
- package/run.sh +193 -58
- package/scripts/check-acp-backend-preflight.ts +10 -2
- package/scripts/check-acp-cortex.ts +7 -6
- package/scripts/check-acp-prompt-lifecycle.ts +2 -2
- package/scripts/check-acp-provider-surface.ts +1 -1
- package/scripts/check-acp-session-reuse.ts +5 -5
- package/scripts/check-acp-stop-reason.ts +2 -2
- package/scripts/check-acp-stream-hooks.ts +2 -2
- package/scripts/check-acp-usage-accounting.ts +2 -2
- package/scripts/check-codex-app-server-launch.ts +55 -12
- package/scripts/check-codex-native-push.ts +231 -12
- package/scripts/check-fresh-call-dispatch.ts +47 -4
- package/scripts/check-gate-qualification.ts +8 -6
- package/scripts/check-probe-ordering.ts +13 -6
- package/scripts/check-resume-launch-identity.ts +38 -0
- package/scripts/codex-app-server-launch.sh +54 -29
- package/scripts/codex-socket-classify.ts +46 -0
- package/scripts/emit-pi-durable-carrier.ts +159 -168
- package/scripts/mutants/acp-cortex.json +17 -8
- package/scripts/mutants/acp-set-model-readback.json +28 -0
- package/scripts/mutants/codex-app-server-launch.json +39 -12
- package/scripts/mutants/codex-native.json +15 -0
- package/scripts/mutants/control-send-receipt.json +1 -1
- package/scripts/mutants/fresh-call-dispatch.json +11 -0
- package/scripts/mutants/herdr-dev-checkout.json +70 -0
- package/scripts/mutants/pack-install.json +3 -3
- package/scripts/mutants/pi-durable.json +282 -16
- package/scripts/mutants/pi-floor.json +5 -5
- package/scripts/mutants/resume-launch-identity.json +15 -0
- package/scripts/mutants/v2-visible-resume.json +11 -0
- package/scripts/pi-durable-contact-host.mjs +1 -1
- package/scripts/resolve-acp-bridge.ts +1 -1
- package/scripts/smoke-acp-bundled-mcp-live.ts +1 -1
- package/scripts/smoke-acp-carrier-augment-live.ts +1 -1
- package/scripts/smoke-acp-long-turn-live.ts +1 -1
- package/scripts/smoke-acp-mcp-live.ts +1 -1
- package/scripts/smoke-acp-memory-containment-live.ts +1 -1
- package/scripts/smoke-acp-ordering-probe-live.ts +1 -1
- package/scripts/smoke-acp-overlay-live.ts +1 -1
- package/scripts/smoke-acp-provider-live.ts +1 -1
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-session-reuse-live.ts +1 -1
- package/scripts/smoke-acp-skill-live.ts +1 -1
- package/scripts/smoke-acp-socket-citizen-live.ts +2 -2
- package/scripts/smoke-acp-v2-send-live.ts +1 -1
- package/scripts/smoke-entwurf-chain-live.ts +1 -1
- package/scripts/smoke-mux-lifecycle-live.ts +1 -1
- package/pi/pi-durable/overlay/runtime-contacts.patch +0 -283
package/run.sh
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Model id convention (see AGENTS.md Hard Rule #1):
|
|
4
4
|
# - User-facing examples use the qualified form `entwurf/<backend-model>`
|
|
5
|
-
# (e.g. `entwurf/claude-sonnet-5`); the prefix routes to this provider
|
|
5
|
+
# (e.g. `entwurf/claude-sonnet-5-5`); the prefix routes to this provider
|
|
6
6
|
# so `--provider` is redundant and is dropped in docs.
|
|
7
7
|
# - Smoke helpers that feed `ensureBridgeSession({modelId})` directly (cancel,
|
|
8
|
-
# model-switch) pass BARE backend ids (`claude-sonnet-5`, `gpt-5.4`)
|
|
8
|
+
# model-switch) pass BARE backend ids (`claude-sonnet-5-5`, `gpt-5.4`)
|
|
9
9
|
# because the bridge library contract is bare. Smoke helpers that invoke pi
|
|
10
10
|
# via the CLI still pin `--provider entwurf` and can accept either
|
|
11
11
|
# bare or qualified model, but we keep bare here to match the bridge-level
|
|
@@ -32,7 +32,7 @@ TARGET_PROJECT_DIR=${2:-$PROJECT_DIR_DEFAULT}
|
|
|
32
32
|
# and install path against the same scope for traceability.
|
|
33
33
|
PACKAGE_NAME="@junghanacs/entwurf"
|
|
34
34
|
# Runtime provider id — DO NOT change. Embedded in model strings
|
|
35
|
-
# (`entwurf/claude-sonnet-5`), settings keys (`entwurfProvider`),
|
|
35
|
+
# (`entwurf/claude-sonnet-5-5`), settings keys (`entwurfProvider`),
|
|
36
36
|
# log prefixes (`[entwurf:bootstrap]`), and the `--provider entwurf`
|
|
37
37
|
# CLI surface. Renaming this would break every consumer transcript and every
|
|
38
38
|
# saved session anchor.
|
|
@@ -116,9 +116,55 @@ run_vitest() {
|
|
|
116
116
|
(cd "$REPO_DIR" && "$bin" run "$@")
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
# A source-only UI over the existing owned developer-return procedure. No second
|
|
120
|
+
# installer, npm acquisition, automatic mode switch or ownership writer lives here.
|
|
121
|
+
developer_menu() {
|
|
122
|
+
[ "$#" -eq 0 ] || { echo 'usage: ./run.sh menu' >&2; return 2; }
|
|
123
|
+
if [ ! -d "$REPO_DIR/.git" ] || [ ! -f "$REPO_DIR/plugins/herdr/use-dev-checkout.sh" ]; then
|
|
124
|
+
echo '[menu] developer-checkout-source-only — run ./run.sh menu from the prepared source checkout, not the npm installation.' >&2
|
|
125
|
+
return 1
|
|
126
|
+
fi
|
|
127
|
+
if [ ! -t 0 ]; then
|
|
128
|
+
echo '[menu] menu-tty-required — interactive stdin is required; nothing was changed.' >&2
|
|
129
|
+
return 2
|
|
130
|
+
fi
|
|
131
|
+
local choice confirm
|
|
132
|
+
printf '\nHerdr — npm installation check → developer board (local checkout)\n'
|
|
133
|
+
printf 'Checkout: %s\n' "$REPO_DIR"
|
|
134
|
+
printf ' 1) Readiness check — read-only (not a full certificate of installation prerequisites)\n'
|
|
135
|
+
printf ' 2) Switch to the developer board — Pi / Claude Code / Herdr local link\n'
|
|
136
|
+
printf ' 0) Quit — nothing is changed\n'
|
|
137
|
+
if ! IFS= read -r -p 'Choose [0/1/2]: ' choice; then
|
|
138
|
+
echo '[menu] menu-input-closed — nothing was changed.' >&2
|
|
139
|
+
return 2
|
|
140
|
+
fi
|
|
141
|
+
case "$choice" in
|
|
142
|
+
0) return 0 ;;
|
|
143
|
+
1) bash "$REPO_DIR/plugins/herdr/use-dev-checkout.sh" --check ;;
|
|
144
|
+
2)
|
|
145
|
+
# Show the real owner's readiness first. Busy sessions or bad ownership
|
|
146
|
+
# stop here; confirmation grants no authority to bypass those guards.
|
|
147
|
+
bash "$REPO_DIR/plugins/herdr/use-dev-checkout.sh" --check || return $?
|
|
148
|
+
printf '\nClose the affected sessions yourself, and start no new session while switching.\n'
|
|
149
|
+
printf 'The currently owned installation is deactivated, then this checkout is linked. There is no automatic recovery or retry.\n'
|
|
150
|
+
if ! IFS= read -r -p 'Type DEV to switch (anything else cancels): ' confirm; then
|
|
151
|
+
echo '[menu] menu-input-closed — apply was not called.' >&2
|
|
152
|
+
return 2
|
|
153
|
+
fi
|
|
154
|
+
if [ "$confirm" != DEV ]; then
|
|
155
|
+
echo '[menu] menu-cancelled — installation wiring is unchanged.'
|
|
156
|
+
return 0
|
|
157
|
+
fi
|
|
158
|
+
bash "$REPO_DIR/plugins/herdr/use-dev-checkout.sh" --apply
|
|
159
|
+
;;
|
|
160
|
+
*) echo '[menu] invalid-menu-choice — choose 0, 1 or 2; nothing was changed.' >&2; return 2 ;;
|
|
161
|
+
esac
|
|
162
|
+
}
|
|
163
|
+
|
|
119
164
|
usage() {
|
|
120
165
|
cat <<'EOF'
|
|
121
166
|
Usage:
|
|
167
|
+
./run.sh menu # source-only Herdr developer board: read-only readiness check or explicitly confirmed return to checkout wiring; never closes sessions or acquires npm artifacts
|
|
122
168
|
./run.sh setup [project-dir] # ONE presence-driven composition (#86): per-component PASS/SKIP/FAIL for pi/claude/agy/copilot/omp/codex + stable dev bins + v2 install smoke. Codex birth/MCP/status-line atoms are composed at user scope; setup stays non-green until the operator trusts the birth declaration once in visible Codex. Absent harness = zero-state SKIP, detected-incomplete = named FAIL + nonzero exit. Never installs a harness, starts a daemon, touches credentials, or writes the vendor trust receipt
|
|
123
169
|
./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, doctor-pi-provider, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live) + the Codex Pi -> visible Codex -> visible Pi receipt chain (smoke-codex-fresh-live; initial Pi and the Codex it opens in one session S, explicit operator-owned app-server PID in a DIFFERENT session — that separation is the lane B claim). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
|
|
124
170
|
./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
|
|
@@ -135,7 +181,7 @@ Usage:
|
|
|
135
181
|
./run.sh check-pi-durable-contact # #129 L-identity gate, CHECKOUT-ONLY (an installed package refuses it in run_ts). Opens the REAL durable app from THIS checkout's verified carrier (pi/pi-durable/carrier/ under its resolver, against the checkout's pinned pi SDK set — refused by name first) with the pi-durable contact, births the citizen and runs its tools adapter-direct through a privately emitted branch bridge (ENTWURF_PI_DURABLE_BRIDGE_ENTRY; its registry must match this checkout). Matrix roots {default, explicit} x {fresh, --continue}: one record keyed by the durable session dir, sender marker on the spawned host pid, exactly one bridge child whose /proc PPID is that host, poisoned identity carriers kept from it, its four root carriers bound to the roots written, the receiver ARMED (raw marker owned by the host pid + start key as pi-durable-host, joined to the sender marker; removed on close), self = the record and replyable, peers from an outside bridge, same garden id on reopen; the bundle's compiled owner join must admit pi-durable-host. Reaps only the host/bridges it owns on any failure. Harness-bypassed: no model turn, no ToolTask. Missing inputs → SKIP (97), never a pass
|
|
136
182
|
./run.sh check-pi-durable-send # #129 L-send FIRST CELL, CHECKOUT-ONLY. Same input as check-pi-durable-contact. One native durable turn: the app's own controller input → GenerationTask → ToolTask → the contact's entwurf_v2 → the private bridge → a seeded self-fetch target (C2 recipe, owned by a separate idle child). S = a 127.0.0.1 scripted OpenAI-compatible endpoint configured through the app's documented models.json (dummy key, PI_OFFLINE); H = the native ToolTask path; V (a vendor turn) is NOT run. Oracles: the wire (tools offered, v2 schema = the bridge's tools/list, exactly two requests, the tool result + receipt in the second), the raw mailbox (.msg 1, signal, sender = the host record), the native view (one call, its settled result, root current). Not exactly-once, not recovery, not a recipient turn. Missing inputs → SKIP (97)
|
|
137
183
|
./run.sh check-demo-reproducibility # source-checkout-only: discover offline demo Node tests; no LIVE, model, render or publication
|
|
138
|
-
./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over the six pi-durable beside tests (adapter, carrier/SDK-set verifier, preflight, owner join, carrier relocation/resolver
|
|
184
|
+
./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over the six pi-durable beside tests (adapter, carrier/SDK-set verifier, preflight, owner join, carrier relocation/resolver, owned-source emission and typecheck fence, task footer, native-module file and directory ingress) — the pi-durable qualification lane's gate argv. Same files check-tests-beside-behavior already runs.
|
|
139
185
|
./run.sh check-pi-durable-receive # #129 L-receive FIRST CELL, CHECKOUT-ONLY. Same input (the bundle must carry the pi-durable-host owner join). The host driver gives the app NO input; a seeded claude-code sender (marker owned by a separate idle child, handed to a gate-owned sending bridge through ENTWURF_META_SENDER_MARKER) enqueues one message, the host's doorbell admits its notice to the ROOT conversation, and the scripted model drains it with entwurf_inbox_read. S withholds response #1 until the ENQUEUE receipt, the .delivered bytes, the signal, the host-owned receiver marker and the notice are captured. Oracles: receiver() admission, native view, raw archive (.delivered.read + lastReadAt), wire (exactly two requests, drain result = view), and after a green reap a forensic read-only SQLite copy (submission row by request id = reported id, root, input, done, placed entry = the notice). V NOT run. Missing inputs → SKIP (97)
|
|
140
186
|
./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
|
|
141
187
|
./run.sh check-copilot-launch # #82 RAIL 7 gate: the MANAGED launch `entwurf copilot`, driven through its public address against a FAKE VENDOR on a sandbox PATH. Receiver precondition refusals, EXTENSIONS token + operator token preservation, injected defaults before the `--` terminator, byte-identical argv, the 11 explicit permission/surface policy overrides that suppress `--yolo`, exec (not fork) pid identity, exit passthrough, recursion refusal. Hermetic; no Copilot, no model turn
|
|
@@ -223,11 +269,11 @@ Usage:
|
|
|
223
269
|
./run.sh check-meta-manifest-schema # 0.12.2 meta-bridge: CLI-version-INDEPENDENT static guard — plugin manifests pinned to the minimal keyset that validates on the lowest supported Claude (closed-schema regression that broke 0.12.1 install on floor) + desired_mcp installed-vs-clone dual-mode. Offline (deps: python3)
|
|
224
270
|
./run.sh smoke-claude-native-resume-live # LIVE-only: Claude Code native fresh→--resume continuity + meta-record uniqueness; proves meta-bridge records identity without touching the backend resume path
|
|
225
271
|
|
|
226
|
-
./run.sh install-meta-bridge # INTERNAL
|
|
272
|
+
./run.sh install-meta-bridge # INTERNAL wiring leaf: setup, the source-only developer menu and doctor recovery compose it; stateful GLOBAL install (plugin + USER MCP + settings keyset, honest uninstall state)
|
|
227
273
|
./run.sh uninstall-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL uninstall (restore only keys/items captured in install-state)
|
|
228
274
|
./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is NOT CERTIFIED and stays nonzero, on ONE narrowed reason — start-key and ppid are portable there (`ps` fallback, same string the TS core mints), so what holds the lane closed is per-process environment DISCOVERY alone, pending physical-host evidence. Detection power is held by check-meta-doctor-oracle
|
|
229
275
|
./run.sh copilot [args...] # #82 RAIL 7: the MANAGED Copilot launch. exec()s the vendor CLI in THIS terminal (cwd/pid/exit preserved) with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS set for that one invocation — entwurf owns no part of your shell and writes nothing to it, but it owns the process it is about to become. Without that flag Copilot skips the extension scan SILENTLY. Refuses to launch unless the receiver unit is actually installed. Injects `--model auto` when no --model was given and `--yolo` when no explicit permission/surface policy flag was given, both BEFORE any `--`; every argument you pass is forwarded byte-identical. RUNNING THIS IS YOUR CONSENT to that profile — use plain `copilot` for stock vendor behaviour. Not tmux, not fresh-call, mints no citizen (birth is still the first prompt)
|
|
230
|
-
./run.sh pi-durable [args...] # #129/#130: the MANAGED durable launch. Verifies this package's pi-durable CARRIER (pi/pi-durable/carrier/,
|
|
276
|
+
./run.sh pi-durable [args...] # #129/#130: the MANAGED durable launch. Verifies this package's pi-durable CARRIER (pi/pi-durable/carrier/, emitted deterministically from Entwurf's owned source pi/pi-durable/src) and the pi SDK set it resolves — every declared member/direct edge at exactly the pinned version — refusing by name (package-incomplete / carrier-drift / sdk-absent / sdk-mismatch, exit 3), then exec()s node under the carrier resolver with the packaged bootstrap (pi/pi-durable/bootstrap.mjs) in this terminal. A new session needs --provider <p> --model <exact catalog id> --width task-wide [--entwurf-bootstrap <{v:2,task}>]; --continue takes none of them. Nothing outside the package is located, installed or repaired
|
|
231
277
|
./run.sh install-copilot-bridge # #82: GLOBAL install of the Copilot BIRTH plugin (own marketplace root; node+entry baked into the no-argv exec string). MCP wiring and the RECEIVER extension are separate install surfaces (install-copilot-mcp, install-copilot-receive). Also retires the stale Claude unit (--keep-stale-claude-unit opts out)
|
|
232
278
|
./run.sh uninstall-copilot-bridge # #86 C3a: honest inverse of install-copilot-bridge from the package-owned install-state (exact qualified id + exact marketplace name/path + recorded assembly only; state deleted LAST; a failing vendor list is UNKNOWN and refuses; never --force, never the stale Claude unit)
|
|
233
279
|
./run.sh doctor-copilot-bridge # #82/#86: fail-loud surface for that unit — runtime axis (red = a hook that RAN and failed, or a broken/unbaked artifact; "installed with zero records" is NOT red and is reported as NOT-YET: a Copilot session is born on its FIRST PROMPT, not when the window opens, measured) + ownership axis (install-state shape/binding, marketplace path drift, UNKNOWN vendor lists; legacy no-state install = named non-green, repair = install-copilot-bridge adoption). Either axis red = red
|
|
@@ -291,7 +337,7 @@ Usage:
|
|
|
291
337
|
./run.sh check-fresh-cut-gate # SOURCE cell of the generation-boundary proof (IN pnpm run check:full): drives real install/setup/fresh-cut in a sandbox; certification refusal is pre-write, quiescence is fail-closed, archives preserve bytes, and the #54 exit matrix distinguishes complete / no-move / usage / incomplete transition / complete-with-cleanup-residue. No model/network/cost
|
|
292
338
|
./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke + the #116 M3-b1 herdr-plugin RUNTIME VERIFIER run against that actual installed tarball (the focused check-herdr-runtime-bootstrap drives a fixture; THIS is where the real package is proven) with the pinned pi SDK (pins derived from the package.json exact production dependency; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + the INSTALLED generation lifecycle on a seeded previous-generation host (REFUSE before activation writes / zero Claude invocations → installed fresh-cut archives + opens empty → install-meta-bridge PASSES) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for) + the INSTALLED #129 durable bootstrap (pi/pi-durable/bootstrap.mjs) imported from node_modules: main/openDurableCitizen exported and PACKAGED_BRIDGE_ENTRY = the installed package's own dist entry — import only, no runtime/TUI/birth/bridge started + the #130 pi-durable CARRIER on actual npm consumers (Entwurf-only / aligned host / isolated global): the installed verifier, a consumer-contained resolution oracle with a same-sandbox neighbour control, every physical SDK edge at the pin, a mixed tree refused as pi-durable-sdk-mismatch though it still imports, and the public `entwurf pi-durable` reaching the app's own explicit-model refusal before any birth, also with one explicit `--native-module` (the synthetic fixture, initialized first; a reserved extension name refused before anything opens) (offline, sandbox HOME)
|
|
293
339
|
./run.sh check-install-container # 0.12.8 (#51 C): Linux artifact-CONSUMER gate — one candidate .tgz handed read-only to a checkout-invisible node:<engines-major>-bookworm cell. Default packs once to temp; ENTWURF_CANDIDATE_TGZ=/absolute/preserved.tgz consumes those exact bytes with no re-pack and prints canonical path+sha256 for release. Non-root global PATH install, frozen package, MCP tools/list, fake-Claude install-meta-bridge, path+sha256 fence, strict doctor, and the GENERATION host-state matrix (clean / v3-only store bytes unchanged / previous-generation REFUSE→fresh-cut→retry PASS) seeded inline. Docker missing = honest SKIP; ENTWURF_REQUIRE_DOCKER=1 makes that RED (required CI)
|
|
294
|
-
./run.sh install [project-dir] # INTERNAL
|
|
340
|
+
./run.sh install [project-dir] # INTERNAL wiring leaf: setup/npm-consumer entry and the source-only developer menu compose it; prefer those owning procedures
|
|
295
341
|
./run.sh remove [project-dir] # remove entwurf entries from project .pi/settings.json (project scope only; global user-scope citizen left intact)
|
|
296
342
|
./run.sh install-user-scope [--plugin-runtime <root>] # #116 M3-b2 PLUGIN-ONLY forward seam: compose ONLY the global pi citizen (user-scope packages[] + provider), never a project `.pi/settings.json` — a Herdr plugin activation has no project and must not invent one. With --plugin-runtime the provider's managed command becomes the absolute bridge under that certified stable runtime (a clean host has nothing entwurf on PATH); without it every byte is the historical bare-bin install. Inverse: remove-user-scope
|
|
297
343
|
./run.sh herdr-plugin-activate <backend...> # #116 M3-b2 public forward activation. Takes a subset of {pi, claude-code} and NOTHING else — the stable runtime root is DERIVED from XDG here and handed down, and both harness writers accept a named root only when it IS that derived one. Certifies the runtime-ready journal AND the installed tree, certifies the existing ledger and its recorded harness roots, plans ADD-ONLY (a backend absent from this request is retained, never removed), preflights every selected component before the first byte, then writes each component `pending` BEFORE mutating and checkpoints `active` the moment its writer returns — so a run that dies halfway leaves a record of how far it got. Which backends is M3-b3's question: this verb is handed the answer, it does not read Herdr's integration status
|
|
@@ -1395,6 +1441,14 @@ check_acp_foreground_only() {
|
|
|
1395
1441
|
run_vitest pi-extensions/lib/acp/backend-adapter.test.ts
|
|
1396
1442
|
}
|
|
1397
1443
|
|
|
1444
|
+
check_acp_set_model_readback() {
|
|
1445
|
+
# NOT a discovery path — check-tests-beside-behavior finds the test by glob. This case is the
|
|
1446
|
+
# MUTANT EXECUTION COORDINATE for scripts/mutants/acp-set-model-readback.json (#128): the claude
|
|
1447
|
+
# adapter refuses a set-model answer that does not name the requested model, before the prompt.
|
|
1448
|
+
section "claude ACP set-model readback (mutant execution coordinate)"
|
|
1449
|
+
run_vitest pi-extensions/lib/acp/backend-adapter.test.ts
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1398
1452
|
check_acp_vendor_rejection() {
|
|
1399
1453
|
# Behaviour-adjacent tests are discovered by the glob lane. This narrow case is the
|
|
1400
1454
|
# mutant execution coordinate: include the adapter admission and real turn pipeline,
|
|
@@ -1552,7 +1606,7 @@ check_pi_durable_beside() {
|
|
|
1552
1606
|
# from re-running every beside test. The six files are one contact: the adapter and its packaged
|
|
1553
1607
|
# bootstrap, the installed carrier/SDK-set verifier (which also reads the three checkout gates as
|
|
1554
1608
|
# SOURCE), the fresh preflight, the owner join that admits the durable host, the carrier's
|
|
1555
|
-
# relocation grammar, resolver
|
|
1609
|
+
# relocation grammar, resolver, owned-source emitter (#130, #141) and source fence with the carrier's task footer (#134),
|
|
1556
1610
|
# and the bootstrap's explicit --native-module ingress (#130 P2) and its --native-module-dir (#134).
|
|
1557
1611
|
run_vitest pi-extensions/meta-bridge-pi-durable.test.ts pi-extensions/lib/pi-durable-runtime.test.ts pi-extensions/lib/pi-durable-fresh-preflight.test.ts pi-extensions/lib/entwurf-deliverability.owner-join.test.ts pi-extensions/lib/pi-durable-carrier.test.ts pi-extensions/lib/pi-durable-native-module.test.ts
|
|
1558
1612
|
}
|
|
@@ -1603,12 +1657,12 @@ check_mux_launch_tmux() {
|
|
|
1603
1657
|
smoke_acp_socket_citizen_live() {
|
|
1604
1658
|
# S1 acceptance smoke (ACP plugin on v2) — OUT of pnpm check, needs LIVE=1.
|
|
1605
1659
|
# Spawns a REAL `pi --entwurf-control` resident on an ACP model
|
|
1606
|
-
# (entwurf/claude-opus-5) and proves it is a first-class socket-citizen:
|
|
1660
|
+
# (entwurf/claude-opus-5-5) and proves it is a first-class socket-citizen:
|
|
1607
1661
|
# the control socket stands up, get_info answers with the ACP model (model-lock
|
|
1608
1662
|
# did NOT revert — QM1), idle/cwd are reported, and the fail-loud streamSimple
|
|
1609
1663
|
# stub never fires (turn-free launch — QM2). No prompt is sent: S1 proves
|
|
1610
1664
|
# citizenship, never a backend turn (that is S2). Honest skip when LIVE!=1.
|
|
1611
|
-
# Model override: ENTWURF_S1_MODEL (default claude-opus-5).
|
|
1665
|
+
# Model override: ENTWURF_S1_MODEL (default claude-opus-5-5).
|
|
1612
1666
|
# LIVE=1 ./run.sh smoke-acp-socket-citizen-live
|
|
1613
1667
|
run_ts scripts/smoke-acp-socket-citizen-live.ts
|
|
1614
1668
|
}
|
|
@@ -1622,7 +1676,7 @@ smoke_acp_raw_turn_live() {
|
|
|
1622
1676
|
# plus captured raw NDJSON bytes. NO provider/overlay/streamSimple/_meta — the
|
|
1623
1677
|
# raw backend pipe only. Launch source must be the package bin (PATH fallback
|
|
1624
1678
|
# fails acceptance unless ENTWURF_ACP_RAW_TURN_ALLOW_PATH_FALLBACK=1, debug).
|
|
1625
|
-
# Model override: ENTWURF_ACP_RAW_TURN_MODEL (default claude-sonnet-5).
|
|
1679
|
+
# Model override: ENTWURF_ACP_RAW_TURN_MODEL (default claude-sonnet-5-5).
|
|
1626
1680
|
# LIVE=1 ./run.sh smoke-acp-raw-turn-live
|
|
1627
1681
|
run_ts scripts/smoke-acp-raw-turn-live.ts
|
|
1628
1682
|
}
|
|
@@ -1640,7 +1694,7 @@ smoke_acp_overlay_live() {
|
|
|
1640
1694
|
# meta-store for mailbox absence (flaky — concurrent sessions); the honest
|
|
1641
1695
|
# claim is overlay-supplies-hooks:{}. Launch must be the package bin (PATH
|
|
1642
1696
|
# fallback fails acceptance unless ENTWURF_ACP_OVERLAY_ALLOW_PATH_FALLBACK=1).
|
|
1643
|
-
# Model override: ENTWURF_ACP_OVERLAY_MODEL (default claude-sonnet-5).
|
|
1697
|
+
# Model override: ENTWURF_ACP_OVERLAY_MODEL (default claude-sonnet-5-5).
|
|
1644
1698
|
# LIVE=1 ./run.sh smoke-acp-overlay-live
|
|
1645
1699
|
run_ts scripts/smoke-acp-overlay-live.ts
|
|
1646
1700
|
}
|
|
@@ -1655,7 +1709,7 @@ smoke_acp_memory_containment_live() {
|
|
|
1655
1709
|
# can stop a memory write is the lever — not us. Fails loud if engraving.md is
|
|
1656
1710
|
# empty (carrier OFF = no containment). Launch must be the package bin (PATH
|
|
1657
1711
|
# fallback fails acceptance unless ENTWURF_ACP_MEMORY_ALLOW_PATH_FALLBACK=1).
|
|
1658
|
-
# Model override: ENTWURF_ACP_MEMORY_MODEL (default claude-sonnet-5).
|
|
1712
|
+
# Model override: ENTWURF_ACP_MEMORY_MODEL (default claude-sonnet-5-5).
|
|
1659
1713
|
# LIVE=1 ./run.sh smoke-acp-memory-containment-live
|
|
1660
1714
|
run_ts scripts/smoke-acp-memory-containment-live.ts
|
|
1661
1715
|
}
|
|
@@ -1682,7 +1736,7 @@ smoke_acp_provider_live() {
|
|
|
1682
1736
|
# assistant reply (live model proof) + the removed S0 stub error never appears
|
|
1683
1737
|
# (provider path actually opened) + pi exits 0. Tool-free prompt; the
|
|
1684
1738
|
# event-mapper gate owns the tool→notice contract.
|
|
1685
|
-
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5).
|
|
1739
|
+
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5-5).
|
|
1686
1740
|
# LIVE=1 ./run.sh smoke-acp-provider-live
|
|
1687
1741
|
run_ts scripts/smoke-acp-provider-live.ts
|
|
1688
1742
|
}
|
|
@@ -1696,7 +1750,7 @@ smoke_acp_session_reuse_live() {
|
|
|
1696
1750
|
# is the verdict; codeword recall is supporting only (a rebuilt full transcript
|
|
1697
1751
|
# could recall it too). The one-shot exit0 half is owned by
|
|
1698
1752
|
# smoke-acp-provider-live.
|
|
1699
|
-
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5).
|
|
1753
|
+
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5-5).
|
|
1700
1754
|
# LIVE=1 ./run.sh smoke-acp-session-reuse-live
|
|
1701
1755
|
run_ts scripts/smoke-acp-session-reuse-live.ts
|
|
1702
1756
|
}
|
|
@@ -1708,7 +1762,7 @@ smoke_acp_carrier_augment_live() {
|
|
|
1708
1762
|
# secret (the augment rode the wire to the model) and the EMPTY default carrier
|
|
1709
1763
|
# must bill clean (exit 0, no HTTP-400 canary — 핀1 live). Optional tiny carrier
|
|
1710
1764
|
# check via SMOKE_ACP_CARRIER_PRESENT=1 (non-blocking).
|
|
1711
|
-
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5).
|
|
1765
|
+
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5-5).
|
|
1712
1766
|
# LIVE=1 ./run.sh smoke-acp-carrier-augment-live
|
|
1713
1767
|
run_ts scripts/smoke-acp-carrier-augment-live.ts
|
|
1714
1768
|
}
|
|
@@ -1794,9 +1848,9 @@ smoke_acp_rgg_live() {
|
|
|
1794
1848
|
# birth, record-keyed socket, attach-on-reopen, no pi-session-id socket) holds
|
|
1795
1849
|
# under the ACP provider too — the logic is provider-agnostic. The POSITIVE cell
|
|
1796
1850
|
# (one real turn completing transcriptPath/model) is the only model-in-loop part.
|
|
1797
|
-
# Target override: ENTWURF_RGG_TARGET (default entwurf/claude-sonnet-5).
|
|
1851
|
+
# Target override: ENTWURF_RGG_TARGET (default entwurf/claude-sonnet-5-5).
|
|
1798
1852
|
# ./run.sh smoke-acp-rgg-live
|
|
1799
|
-
local target="${ENTWURF_RGG_TARGET:-entwurf/claude-sonnet-5}"
|
|
1853
|
+
local target="${ENTWURF_RGG_TARGET:-entwurf/claude-sonnet-5-5}"
|
|
1800
1854
|
(cd "$REPO_DIR" && ENTWURF_LIVE_TARGET="$target" SMOKE_RGG_POSITIVE=0 bash scripts/smoke-resident-garden-guard.sh)
|
|
1801
1855
|
}
|
|
1802
1856
|
|
|
@@ -2381,8 +2435,8 @@ const nextFloorRange = `>=${piAi} <${piMaj}.${piMin + 1}`;
|
|
|
2381
2435
|
const floorGaps = [];
|
|
2382
2436
|
const PI_CONSTELLATION = [
|
|
2383
2437
|
'@earendil-works/pi-ai', '@earendil-works/pi-coding-agent', '@earendil-works/pi-tui',
|
|
2384
|
-
'@earendil-works/pi-agent-core', '@earendil-works/pi-
|
|
2385
|
-
|
|
2438
|
+
'@earendil-works/pi-agent-core', '@earendil-works/pi-telemetry', '@earendil-works/chord',
|
|
2439
|
+
// `[측정 2026-10-13]` 1.2.0: pi-client and pi-protocol left — upstream removed both, E404 at 1.2.0.
|
|
2386
2440
|
// `[측정 2026-09-30]` 0.99.1: pi-coding-agent's runtime deps gained the built-in extensions' packages.
|
|
2387
2441
|
'@earendil-works/pi-codemode', '@earendil-works/pi-mcp',
|
|
2388
2442
|
// #130: the durable library the pi-durable carrier imports — a production dependency since 0.32.
|
|
@@ -2523,6 +2577,65 @@ for (const [file, re, min, label] of SITES) {
|
|
|
2523
2577
|
bound += hits.length;
|
|
2524
2578
|
}
|
|
2525
2579
|
|
|
2580
|
+
// #141: the pi-durable carrier's emitter identity, the one place CI runs an EXACT Node. The check job
|
|
2581
|
+
// runs the Node the pin records as the carrier's emitter (CI-BOUND), and its check:full floor step runs
|
|
2582
|
+
// with ENTWURF_REQUIRE_EMITTER_NODE=1 (CI-REQUIRED), so that floor proves the emission under that
|
|
2583
|
+
// identity; every other job floats the major so the newest release of the lane stays covered. Both are
|
|
2584
|
+
// read from ACTIVE workflow lines only — the step's own `with:` / `env:` mapping, comments stripped —
|
|
2585
|
+
// so commented-out text or the same key on another step can never stand in for them.
|
|
2586
|
+
const EMITTER_BOUND_QK = '[QK:PI-DURABLE-EMITTER-NODE-CI-BOUND]';
|
|
2587
|
+
const EMITTER_REQUIRED_QK = '[QK:PI-DURABLE-EMITTER-NODE-CI-REQUIRED]';
|
|
2588
|
+
const emitterNode = JSON.parse(readFileSync('pi/pi-durable/overlay/upstream-pin.json', 'utf8')).carrier?.emitter?.node;
|
|
2589
|
+
assert.match(String(emitterNode), /^v\d+\.\d+\.\d+$/,
|
|
2590
|
+
`${EMITTER_BOUND_QK} pi/pi-durable/overlay/upstream-pin.json carrier.emitter.node must be an exact vX.Y.Z (got ${emitterNode})`);
|
|
2591
|
+
// jobs → steps → active lines. A full-line comment is dropped and a trailing ` # …` is cut; job heads are
|
|
2592
|
+
// the two-space keys under `jobs:`, a step starts at its six-space `- ` item.
|
|
2593
|
+
const ciJobs = new Map();
|
|
2594
|
+
{
|
|
2595
|
+
let inJobs = false;
|
|
2596
|
+
let steps;
|
|
2597
|
+
for (const raw of readFileSync('.github/workflows/ci.yml', 'utf8').split('\n')) {
|
|
2598
|
+
const line = raw.replace(/\s+#.*$/, '');
|
|
2599
|
+
if (/^\s*#/.test(raw) || line.trim() === '') continue;
|
|
2600
|
+
if (/^\S/.test(line)) { inJobs = line === 'jobs:'; steps = undefined; continue; }
|
|
2601
|
+
if (!inJobs) continue;
|
|
2602
|
+
const head = /^ ([a-z][a-z0-9-]*):$/.exec(line);
|
|
2603
|
+
if (head) { steps = []; ciJobs.set(head[1], steps); continue; }
|
|
2604
|
+
if (steps === undefined) continue;
|
|
2605
|
+
if (/^ - /.test(line)) steps.push([` ${line.slice(8)}`]);
|
|
2606
|
+
else if (steps.length > 0 && /^ /.test(line)) steps.at(-1).push(line);
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
// A step's own value for `key` (`with:` / `env:` child at ten spaces), or a top-level step key at eight.
|
|
2610
|
+
const stepKey = (step, key) => step.find((l) => l.startsWith(` ${key}: `))?.slice(10 + key.length);
|
|
2611
|
+
const stepChild = (step, block, key) => {
|
|
2612
|
+
const at = step.indexOf(` ${block}:`);
|
|
2613
|
+
if (at === -1) return undefined;
|
|
2614
|
+
const children = [];
|
|
2615
|
+
for (const l of step.slice(at + 1)) { if (!l.startsWith(' ')) break; children.push(l); }
|
|
2616
|
+
return children.find((l) => l.startsWith(` ${key}: `))?.slice(12 + key.length);
|
|
2617
|
+
};
|
|
2618
|
+
const setupNodes = (job) => (ciJobs.get(job) ?? []).filter((s) => /^actions\/setup-node@/.test(stepKey(s, 'uses') ?? ''));
|
|
2619
|
+
const checkSetup = setupNodes('check');
|
|
2620
|
+
assert.equal(checkSetup.length, 1,
|
|
2621
|
+
`${EMITTER_BOUND_QK} .github/workflows/ci.yml: the check job must have exactly one actions/setup-node step (found ${checkSetup.length})`);
|
|
2622
|
+
const checkNode = stepChild(checkSetup[0], 'with', 'node-version');
|
|
2623
|
+
assert.equal(`v${checkNode}`, emitterNode,
|
|
2624
|
+
`${EMITTER_BOUND_QK} .github/workflows/ci.yml: the check job's setup-node runs Node ${checkNode}, but the carrier's pinned emitter is ${emitterNode} — its floor would prove the emission under another identity`);
|
|
2625
|
+
const floors = (ciJobs.get('check') ?? []).filter((s) => stepKey(s, 'run') === 'pnpm run check:full');
|
|
2626
|
+
assert.equal(floors.length, 1,
|
|
2627
|
+
`${EMITTER_REQUIRED_QK} .github/workflows/ci.yml: the check job must run \`pnpm run check:full\` in exactly one step (found ${floors.length})`);
|
|
2628
|
+
assert.equal(stepChild(floors[0], 'env', 'ENTWURF_REQUIRE_EMITTER_NODE'), '"1"',
|
|
2629
|
+
`${EMITTER_REQUIRED_QK} .github/workflows/ci.yml: the check job's check:full step does not set ENTWURF_REQUIRE_EMITTER_NODE: "1" in its own env, so an emitter-identity mismatch on that floor would be a skip, not red`);
|
|
2630
|
+
for (const [job] of ciJobs) {
|
|
2631
|
+
if (job === 'check') continue;
|
|
2632
|
+
for (const s of setupNodes(job)) {
|
|
2633
|
+
const value = stepChild(s, 'with', 'node-version');
|
|
2634
|
+
assert.match(String(value), /^\d+$/,
|
|
2635
|
+
`${EMITTER_BOUND_QK} .github/workflows/ci.yml: job ${job} pins node-version ${value}; only the check job is exact, the others float the major`);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2526
2639
|
// Sweep the tracked first-party contract TEXT surfaces, NOT just the files
|
|
2527
2640
|
// already registered above. The first cut of this gate swept only SITES' own
|
|
2528
2641
|
// files and therefore could not see a declaration in an UNREGISTERED file —
|
|
@@ -2573,7 +2686,7 @@ assert.ok(swept >= 6,
|
|
|
2573
2686
|
// `bound` is the real declaration count; SITES.length is the number of RULES
|
|
2574
2687
|
// (the CI rule alone matches two node-version lines). Naming the rule count a
|
|
2575
2688
|
// declaration count would be this gate telling a small lie about its own reach.
|
|
2576
|
-
console.log(`[check-node-floor-coherence] ok — Node >=${MAJOR} is coherent across engines.node, ${bound} bound declarations from ${SITES.length} site rules, and ${swept} declarations swept over ${tracked.length} tracked first-party contract text surfaces (single supported axis, no legacy lane). Non-text carriers (e.g. a Dockerfile \`FROM node:*\`) are OUTSIDE this pattern and must be bound by their own gate.`);
|
|
2689
|
+
console.log(`[check-node-floor-coherence] ok — Node >=${MAJOR} is coherent across engines.node, ${bound} bound declarations from ${SITES.length} site rules, and ${swept} declarations swept over ${tracked.length} tracked first-party contract text surfaces (single supported axis, no legacy lane); the pi-durable emitter ${emitterNode} is the CI check job's exact Node. Non-text carriers (e.g. a Dockerfile \`FROM node:*\`) are OUTSIDE this pattern and must be bound by their own gate.`);
|
|
2577
2690
|
EOF
|
|
2578
2691
|
)
|
|
2579
2692
|
}
|
|
@@ -2862,7 +2975,7 @@ check_pi_import_surface() {
|
|
|
2862
2975
|
# resolve under the extension loader.
|
|
2863
2976
|
#
|
|
2864
2977
|
# #130 CARRIER FENCE (replaces the #129 bootstrap exception, retired with the source runtime):
|
|
2865
|
-
# the pi-durable carrier (pi/pi-durable/carrier/*.js,
|
|
2978
|
+
# the pi-durable carrier (pi/pi-durable/carrier/*.js, emitted from Entwurf's owned source by
|
|
2866
2979
|
# scripts/emit-pi-durable-carrier.ts) is the ONE place pi-coding-agent internals are reached,
|
|
2867
2980
|
# and only through the reserved `entwurf-pi-dist:` scheme its resolver answers. The fence is
|
|
2868
2981
|
# declared and asserted here, not inferred from the root-only scan staying quiet:
|
|
@@ -3646,7 +3759,7 @@ check_pack() {
|
|
|
3646
3759
|
"package.json" "README.md" "LICENSE" "CHANGELOG.md"
|
|
3647
3760
|
"protocol.js" "run.sh"
|
|
3648
3761
|
"pi-extensions/acp-provider.ts"
|
|
3649
|
-
"pi-extensions/lib/acp/models.ts" "pi-extensions/lib/acp/backend.ts"
|
|
3762
|
+
"pi-extensions/lib/acp/models.ts" "pi-extensions/lib/acp/model-ids.js" "pi-extensions/lib/acp/backend.ts"
|
|
3650
3763
|
"pi-extensions/lib/acp/overlay.ts" "pi-extensions/lib/acp/tool-surface.ts"
|
|
3651
3764
|
"pi-extensions/lib/acp/event-mapper.ts" "pi-extensions/lib/acp/context.ts"
|
|
3652
3765
|
"pi-extensions/entwurf-control.ts"
|
|
@@ -3704,7 +3817,6 @@ check_pack() {
|
|
|
3704
3817
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js"
|
|
3705
3818
|
"pi/pi-durable/bootstrap.mjs"
|
|
3706
3819
|
"pi/pi-durable/overlay/upstream-pin.json"
|
|
3707
|
-
"pi/pi-durable/overlay/runtime-contacts.patch"
|
|
3708
3820
|
"pi/pi-durable/carrier-resolver.mjs"
|
|
3709
3821
|
"pi/pi-durable/carrier-relocation.mjs"
|
|
3710
3822
|
"pi/pi-durable/carrier/runtime.js"
|
|
@@ -3791,6 +3903,11 @@ check_pack() {
|
|
|
3791
3903
|
# Maintainer take/renderer/helper TS is source-only, including non-test implementations.
|
|
3792
3904
|
'^demo/.*\.ts$'
|
|
3793
3905
|
)
|
|
3906
|
+
# #141: the owned pi-durable source, its typecheck fence and the retired overlay patch are maintainer
|
|
3907
|
+
# input. An installed consumer runs the emitted carrier JS (and its LICENSE) only — Rule 11: compiled
|
|
3908
|
+
# JS for installed entrypoints. The files array lists pi/pi-durable per FILE, so nothing admits these
|
|
3909
|
+
# today; this tripwire is what fails loud the day an allowlist entry would.
|
|
3910
|
+
local durable_source_pattern='^pi/pi-durable/(src/|tsconfig\.json$|overlay/runtime-contacts\.patch$)'
|
|
3794
3911
|
|
|
3795
3912
|
local pass=1 f pat hit
|
|
3796
3913
|
|
|
@@ -3810,6 +3927,13 @@ check_pack() {
|
|
|
3810
3927
|
fi
|
|
3811
3928
|
done
|
|
3812
3929
|
|
|
3930
|
+
hit=$(grep -E "$durable_source_pattern" <<<"$file_list" || true)
|
|
3931
|
+
if [ -n "$hit" ]; then
|
|
3932
|
+
fail "[check-pack] [QK:PI-DURABLE-SOURCE-NOT-SHIPPED] the owned pi-durable source rides the tarball — an installed consumer runs the carrier JS only:"
|
|
3933
|
+
echo "$hit" | sed 's/^/ /' >&2
|
|
3934
|
+
pass=0
|
|
3935
|
+
fi
|
|
3936
|
+
|
|
3813
3937
|
local total
|
|
3814
3938
|
total=$(printf '%s\n' "$file_list" | wc -l | tr -d ' ')
|
|
3815
3939
|
echo "[check-pack] $total files in tarball"
|
|
@@ -3844,7 +3968,7 @@ check_pack() {
|
|
|
3844
3968
|
# which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
|
|
3845
3969
|
# below so a future suffix change cannot pass vacuously.)
|
|
3846
3970
|
pack_install_leaked_pi() {
|
|
3847
|
-
grep '^@earendil-works+' | grep -Ev '@1\.
|
|
3971
|
+
grep '^@earendil-works+' | grep -Ev '@1\.2\.0(_|$)' || true
|
|
3848
3972
|
}
|
|
3849
3973
|
|
|
3850
3974
|
# Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
|
|
@@ -3864,15 +3988,15 @@ check_pack_pin_matcher() {
|
|
|
3864
3988
|
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`0.86.0-beta.1`,
|
|
3865
3989
|
# the prerelease shape that an unanchored match would bless) and an off-pin version.
|
|
3866
3990
|
matcher_probe=$(printf '%s\n' \
|
|
3867
|
-
'@earendil-works+pi-ai@1.
|
|
3868
|
-
'@earendil-works+pi-ai@1.
|
|
3869
|
-
'@earendil-works+pi-ai@1.
|
|
3870
|
-
'@earendil-works+pi-ai@1.
|
|
3871
|
-
'@earendil-works+pi-ai@1.
|
|
3991
|
+
'@earendil-works+pi-ai@1.2.0' \
|
|
3992
|
+
'@earendil-works+pi-ai@1.2.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
|
|
3993
|
+
'@earendil-works+pi-ai@1.2.0_ws@8.21.3' \
|
|
3994
|
+
'@earendil-works+pi-ai@1.2.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
|
|
3995
|
+
'@earendil-works+pi-ai@1.2.0-beta.1' \
|
|
3872
3996
|
'@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
|
|
3873
|
-
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.
|
|
3997
|
+
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.2.0-beta.1
|
|
3874
3998
|
@earendil-works+pi-agent-core@0.87.1' ]; then
|
|
3875
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.
|
|
3999
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.2.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.2.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
|
|
3876
4000
|
return 1
|
|
3877
4001
|
fi
|
|
3878
4002
|
|
|
@@ -3880,11 +4004,11 @@ check_pack_pin_matcher() {
|
|
|
3880
4004
|
# (0.85.0 onward, still true at 0.99.1). An off-pin chord MUST leak; the pinned one must not. A matcher narrowed
|
|
3881
4005
|
# back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
|
|
3882
4006
|
matcher_probe=$(printf '%s\n' \
|
|
3883
|
-
'@earendil-works+chord@1.
|
|
4007
|
+
'@earendil-works+chord@1.2.0' \
|
|
3884
4008
|
'@earendil-works+chord@0.87.1' \
|
|
3885
|
-
'@earendil-works+pi-ai@1.
|
|
4009
|
+
'@earendil-works+pi-ai@1.2.0' | pack_install_leaked_pi)
|
|
3886
4010
|
if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
|
|
3887
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE] the pin-leak matcher must cover every @earendil-works closure member, not just the pi-* families — an off-pin @earendil-works/chord has to leak (it
|
|
4011
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE] the pin-leak matcher must cover every @earendil-works closure member, not just the pi-* families — an off-pin @earendil-works/chord has to leak (it was a runtime dependency of pi-coding-agent, pi-agent-core, pi-client and pi-protocol — `[측정 2026-09-20]` at 0.86.0, re-measured `[측정 2026-09-22]` at 0.87.0, `[측정 2026-09-23]` at 0.87.1 and `[측정 2026-09-30]` at 0.99.1, where pi-coding-agent adds pi-codemode and pi-mcp beside them; `[측정 2026-10-13]` at 1.2.0 pi-durable alone declares it) — got: ${matcher_probe:-<nothing leaked>}"
|
|
3888
4012
|
return 1
|
|
3889
4013
|
fi
|
|
3890
4014
|
|
|
@@ -4059,8 +4183,8 @@ ORACLE
|
|
|
4059
4183
|
# copied from this checkout into the sandbox — it never ships (test/ is outside the package files).
|
|
4060
4184
|
# Its initialization appends `init` to the receipt this cell names; the same public launch must then
|
|
4061
4185
|
# reach the same carrier model refusal, so the installed bootstrap loaded the module before the
|
|
4062
|
-
# runtime and the native registry installed its extension (carrier/runtime.js
|
|
4063
|
-
# refusal (runtime.js
|
|
4186
|
+
# runtime and the native registry installed its extension (carrier/runtime.js 159) before that
|
|
4187
|
+
# refusal (runtime.js 175). A module claiming an extension name the app installs itself is refused by
|
|
4064
4188
|
# name before anything opens: no durable session directory, no citizen, in a fresh sandbox home.
|
|
4065
4189
|
# Native SDK file opens here are ordinary (the module runs in the native process); none is judged.
|
|
4066
4190
|
local mod_dir="$box/pd-module" mod_home="$box/pd-module-home" mod_env=()
|
|
@@ -4115,7 +4239,7 @@ ORACLE
|
|
|
4115
4239
|
# aligned host binary (the pinned version, installed above for L1) must be inside the range.
|
|
4116
4240
|
# The previous host version is a variable, not a quoted `@pkg@x.y.z` spec: check-dep-versions reads
|
|
4117
4241
|
# those specs as this repo's install pins, and this one is deliberately off the floor.
|
|
4118
|
-
local host_home host_proj host_old="$box/pd-host-old" host_env=() host_old_ver="1.0
|
|
4242
|
+
local host_home host_proj host_old="$box/pd-host-old" host_env=() host_old_ver="1.1.0"
|
|
4119
4243
|
mkdir -p "$host_old"
|
|
4120
4244
|
for spec in "${host_specs[@]}"; do host_old_specs+=("${spec%@*}@$host_old_ver"); done
|
|
4121
4245
|
out=$(cd "$host_old" && npm install "${host_old_specs[@]}" --no-audit --no-fund 2>&1) || {
|
|
@@ -4133,7 +4257,7 @@ ORACLE
|
|
|
4133
4257
|
fail "[check-pack-install] host $cell: the native pi-durable row must stay PASS whatever the PATH host is:"; echo "$out" | tail -20 | sed 's/^/ /' >&2; return 1
|
|
4134
4258
|
fi
|
|
4135
4259
|
if [ "$cell" = old ]; then
|
|
4136
|
-
if [ "$rc" -eq 0 ] || ! grep -q "pi: FAIL — detected pi $host_old_ver is outside the supported range >=$pin_ver <1.
|
|
4260
|
+
if [ "$rc" -eq 0 ] || ! grep -q "pi: FAIL — detected pi $host_old_ver is outside the supported range >=$pin_ver <1.3" <<<"$out" || [ -e "$host_proj/.pi" ] || [ -e "$host_home/.pi/agent/settings.json" ]; then
|
|
4137
4261
|
fail "[QK:PACK-INSTALL-HOST-RANGE-SEPARATE] [check-pack-install] a real pi $host_old_ver host beside the $pin_ver native set must be a named pi FAIL with zero Pi wiring and a non-green setup (rc=$rc):"
|
|
4138
4262
|
echo "$out" | tail -20 | sed 's/^/ /' >&2
|
|
4139
4263
|
return 1
|
|
@@ -4294,7 +4418,7 @@ _check_pack_install_impl() {
|
|
|
4294
4418
|
"package.json" "README.md" "LICENSE" "CHANGELOG.md"
|
|
4295
4419
|
"protocol.js" "run.sh"
|
|
4296
4420
|
"pi-extensions/acp-provider.ts"
|
|
4297
|
-
"pi-extensions/lib/acp/models.ts" "pi-extensions/lib/acp/backend.ts"
|
|
4421
|
+
"pi-extensions/lib/acp/models.ts" "pi-extensions/lib/acp/model-ids.js" "pi-extensions/lib/acp/backend.ts"
|
|
4298
4422
|
"pi-extensions/lib/acp/overlay.ts" "pi-extensions/lib/acp/tool-surface.ts"
|
|
4299
4423
|
"pi-extensions/lib/acp/event-mapper.ts" "pi-extensions/lib/acp/context.ts"
|
|
4300
4424
|
"pi-extensions/entwurf-control.ts"
|
|
@@ -4341,7 +4465,6 @@ _check_pack_install_impl() {
|
|
|
4341
4465
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js"
|
|
4342
4466
|
"pi/pi-durable/bootstrap.mjs"
|
|
4343
4467
|
"pi/pi-durable/overlay/upstream-pin.json"
|
|
4344
|
-
"pi/pi-durable/overlay/runtime-contacts.patch"
|
|
4345
4468
|
"pi/pi-durable/carrier-resolver.mjs"
|
|
4346
4469
|
"pi/pi-durable/carrier-relocation.mjs"
|
|
4347
4470
|
"pi/pi-durable/carrier/runtime.js"
|
|
@@ -4504,21 +4627,26 @@ _check_pack_install_impl() {
|
|
|
4504
4627
|
# pi-ai, pi-codemode, pi-mcp, pi-tui}, pi-ai {pi-telemetry}, pi-agent-core {pi-ai}, pi-durable
|
|
4505
4628
|
# {chord, pi-ai}, and `npm view` gives pi-client {chord, pi-protocol}, pi-protocol {chord}. All
|
|
4506
4629
|
# eleven are published at 1.1.0.
|
|
4507
|
-
|
|
4630
|
+
# `[측정 2026-10-13]` at 1.2.0 the constellation SHRINKS to the nine-member SDK set: upstream
|
|
4631
|
+
# removed pi-client and pi-protocol (7472cda0d), and `npm view <pkg>@1.2.0` answers E404 for both,
|
|
4632
|
+
# so their pins cannot move with the floor and leave instead of staying redundant. The 1.2.0
|
|
4633
|
+
# registry manifests give pi-coding-agent {pi-agent-core, pi-ai, pi-codemode, pi-mcp, pi-tui}
|
|
4634
|
+
# (chord dropped), pi-ai {pi-telemetry}, pi-agent-core {pi-ai}, pi-durable {chord, pi-ai,
|
|
4635
|
+
# pi-codemode}; all nine are published at 1.2.0
|
|
4636
|
+
# (.tmp-verify/141-kickoff/s1a/map-receipts/01-tarballs.txt).
|
|
4637
|
+
echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.2.x SDK + chord + codemode + mcp + durable + typebox)"
|
|
4508
4638
|
local install_log
|
|
4509
4639
|
install_log=$(cd "$tmp" && pnpm add \
|
|
4510
4640
|
"$tgz_path" \
|
|
4511
|
-
"@earendil-works/pi-ai@1.
|
|
4512
|
-
"@earendil-works/pi-coding-agent@1.
|
|
4513
|
-
"@earendil-works/pi-tui@1.
|
|
4514
|
-
"@earendil-works/pi-agent-core@1.
|
|
4515
|
-
"@earendil-works/pi-
|
|
4516
|
-
"@earendil-works/
|
|
4517
|
-
"@earendil-works/pi-
|
|
4518
|
-
"@earendil-works/
|
|
4519
|
-
"@earendil-works/pi-
|
|
4520
|
-
"@earendil-works/pi-mcp@1.1.0" \
|
|
4521
|
-
"@earendil-works/pi-durable@1.1.0" \
|
|
4641
|
+
"@earendil-works/pi-ai@1.2.0" \
|
|
4642
|
+
"@earendil-works/pi-coding-agent@1.2.0" \
|
|
4643
|
+
"@earendil-works/pi-tui@1.2.0" \
|
|
4644
|
+
"@earendil-works/pi-agent-core@1.2.0" \
|
|
4645
|
+
"@earendil-works/pi-telemetry@1.2.0" \
|
|
4646
|
+
"@earendil-works/chord@1.2.0" \
|
|
4647
|
+
"@earendil-works/pi-codemode@1.2.0" \
|
|
4648
|
+
"@earendil-works/pi-mcp@1.2.0" \
|
|
4649
|
+
"@earendil-works/pi-durable@1.2.0" \
|
|
4522
4650
|
"typebox@latest" \
|
|
4523
4651
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
4524
4652
|
fail "[check-pack-install] pnpm add failed:"
|
|
@@ -4534,11 +4662,11 @@ _check_pack_install_impl() {
|
|
|
4534
4662
|
local leaked_pi
|
|
4535
4663
|
leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
|
|
4536
4664
|
if [ -n "$leaked_pi" ]; then
|
|
4537
|
-
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.
|
|
4665
|
+
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.2.0):"
|
|
4538
4666
|
printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
|
|
4539
4667
|
return 1
|
|
4540
4668
|
fi
|
|
4541
|
-
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.
|
|
4669
|
+
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.2.0 (chord and pi-durable included)"
|
|
4542
4670
|
|
|
4543
4671
|
# Resolve the installed package.json and confirm pi.extensions
|
|
4544
4672
|
# arrived intact. If pi.extensions is empty or missing, the
|
|
@@ -4720,8 +4848,8 @@ _check_pack_install_impl() {
|
|
|
4720
4848
|
loader_ids=$(awk '$1 == "entwurf" { print $2 }' <<<"$loader_out" | LC_ALL=C sort)
|
|
4721
4849
|
loader_expected=$(printf '%s\n' \
|
|
4722
4850
|
claude-fable-5-1 \
|
|
4723
|
-
claude-opus-5 \
|
|
4724
|
-
claude-sonnet-5 \
|
|
4851
|
+
claude-opus-5-5 \
|
|
4852
|
+
claude-sonnet-5-5 \
|
|
4725
4853
|
cortex-auto \
|
|
4726
4854
|
cortex-claude-opus-5 \
|
|
4727
4855
|
cortex-claude-sonnet-5 \
|
|
@@ -4830,7 +4958,7 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
|
|
|
4830
4958
|
echo "$foreign_out" | tail -10 | sed 's/^/ /' >&2
|
|
4831
4959
|
return 1
|
|
4832
4960
|
}
|
|
4833
|
-
if grep -qi "Unknown option" <<<"$foreign_out" || ! grep -q "claude-opus-5" <<<"$foreign_out"; then
|
|
4961
|
+
if grep -qi "Unknown option" <<<"$foreign_out" || ! grep -q "claude-opus-5-5" <<<"$foreign_out"; then
|
|
4834
4962
|
fail "[check-pack-install] foreign-cwd --entwurf-control did not load the entwurf extension from user scope:"
|
|
4835
4963
|
echo "$foreign_out" | tail -10 | sed 's/^/ /' >&2
|
|
4836
4964
|
return 1
|
|
@@ -7069,6 +7197,10 @@ release_gate() {
|
|
|
7069
7197
|
|
|
7070
7198
|
cmd=${1:-}
|
|
7071
7199
|
case "$cmd" in
|
|
7200
|
+
menu)
|
|
7201
|
+
shift
|
|
7202
|
+
developer_menu "$@"
|
|
7203
|
+
;;
|
|
7072
7204
|
setup)
|
|
7073
7205
|
setup_all "$TARGET_PROJECT_DIR"
|
|
7074
7206
|
;;
|
|
@@ -7243,6 +7375,9 @@ case "$cmd" in
|
|
|
7243
7375
|
check-acp-foreground-only)
|
|
7244
7376
|
check_acp_foreground_only
|
|
7245
7377
|
;;
|
|
7378
|
+
check-acp-set-model-readback)
|
|
7379
|
+
check_acp_set_model_readback
|
|
7380
|
+
;;
|
|
7246
7381
|
check-resume-launch-identity)
|
|
7247
7382
|
check_resume_launch_identity
|
|
7248
7383
|
;;
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
import { strict as assert } from "node:assert";
|
|
18
18
|
import { execFileSync } from "node:child_process";
|
|
19
|
-
import { readdirSync, readFileSync, rmSync } from "node:fs";
|
|
19
|
+
import { readdirSync, readFileSync, rmdirSync, rmSync } from "node:fs";
|
|
20
20
|
import { join, resolve } from "node:path";
|
|
21
21
|
import { pathToFileURL } from "node:url";
|
|
22
22
|
import type { Api, AssistantMessageEvent, Model, TranscriptContext } from "@earendil-works/pi-ai";
|
|
23
23
|
import { normalizeContext } from "@earendil-works/pi-ai";
|
|
24
24
|
|
|
25
|
-
const model = { id: "claude-sonnet-5" } as unknown as Model<Api>;
|
|
25
|
+
const model = { id: "claude-sonnet-5-5" } as unknown as Model<Api>;
|
|
26
26
|
|
|
27
27
|
// Declared tools exclude `read`, but the Claude child always exposes Read →
|
|
28
28
|
// declared != actual → the preflight must reject before spawning.
|
|
@@ -157,6 +157,14 @@ try {
|
|
|
157
157
|
);
|
|
158
158
|
} finally {
|
|
159
159
|
rmSync(TMP_EMIT, { recursive: true, force: true });
|
|
160
|
+
try {
|
|
161
|
+
// Same as check-acp-cortex: the emit created `.tmp-verify/` as TMP_EMIT's parent, and
|
|
162
|
+
// check-gate-qualification's snapshot manifest walks ignored paths, so a leftover empty
|
|
163
|
+
// parent reads as a dirty tree. Remove it when empty; a sibling's emit keeps it alive.
|
|
164
|
+
rmdirSync(".tmp-verify");
|
|
165
|
+
} catch {
|
|
166
|
+
// non-empty or already gone — fine either way
|
|
167
|
+
}
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
// ---------------------------------------------------------------------------
|