@junghanacs/entwurf 0.30.1 → 0.32.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 +4 -2
- package/CHANGELOG.md +194 -0
- package/DELIVERY.md +1 -0
- package/README.md +64 -14
- package/VERIFY.md +36 -1
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/setup-clean-host.md +121 -4
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +7 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-deliverability.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +34 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +28 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +28 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +92 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +236 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +746 -0
- package/mcp/entwurf-bridge/src/index.ts +7 -7
- package/mcp/entwurf-bridge/tsconfig.build.json +8 -1
- package/package.json +19 -19
- package/pi/entwurf-capabilities.json +1 -0
- package/pi/pi-durable/bootstrap.mjs +404 -0
- package/pi/pi-durable/carrier/LICENSE +21 -0
- package/pi/pi-durable/carrier/harness-setup.js +123 -0
- package/pi/pi-durable/carrier/prompt.js +74 -0
- package/pi/pi-durable/carrier/runtime.js +402 -0
- package/pi/pi-durable/carrier/sessions.js +56 -0
- package/pi/pi-durable/carrier/subagent.js +55 -0
- package/pi/pi-durable/carrier/tui.js +655 -0
- package/pi/pi-durable/carrier-relocation.mjs +160 -0
- package/pi/pi-durable/carrier-resolver.mjs +38 -0
- package/pi/pi-durable/overlay/runtime-contacts.patch +91 -0
- package/pi/pi-durable/overlay/upstream-pin.json +108 -0
- package/pi-extensions/lib/entwurf-deliverability.ts +7 -1
- package/pi-extensions/lib/fresh-call-composition.ts +42 -7
- package/pi-extensions/lib/meta-sender-identity.ts +7 -1
- package/pi-extensions/lib/meta-session.ts +28 -2
- package/pi-extensions/lib/mux-fresh-call.ts +32 -2
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +113 -0
- package/pi-extensions/lib/pi-durable-runtime.ts +318 -0
- package/pi-extensions/meta-bridge-pi-durable.ts +1024 -0
- package/run.sh +624 -184
- package/scripts/check-entwurf-bridge-pi-free.ts +7 -5
- package/scripts/check-fresh-call-dispatch.ts +18 -0
- package/scripts/check-gate-qualification.ts +87 -34
- package/scripts/check-herdr-activation.ts +5 -4
- package/scripts/check-pi-durable-contact.ts +767 -0
- package/scripts/check-pi-durable-receive.ts +1079 -0
- package/scripts/check-pi-durable-send.ts +1821 -0
- package/scripts/emit-pi-durable-carrier.ts +355 -0
- package/scripts/lib/mutation-qualify.ts +21 -45
- package/scripts/lib/pi-durable-fresh-live-receipts.ts +139 -0
- package/scripts/lib/pi-durable-operator-guard.ts +89 -0
- package/scripts/lib/pi-durable-r1.ts +100 -0
- package/scripts/lib/pi-durable-reap.ts +27 -0
- package/scripts/lib/pi-durable-receive.ts +137 -0
- package/scripts/lib/qualification-receipt.ts +7 -11
- package/scripts/mutants/control-send-receipt.json +1 -1
- package/scripts/mutants/copilot-birth.json +2 -2
- package/scripts/mutants/gate-qualification.json +13 -0
- package/scripts/mutants/meta-hook-session-switch.json +4 -2
- package/scripts/mutants/mux-fresh-call.json +69 -3
- package/scripts/mutants/omp-birth.json +2 -2
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/pack-install.json +3 -3
- package/scripts/mutants/pi-durable.json +723 -0
- package/scripts/mutants/pi-floor.json +24 -2
- package/scripts/pi-durable-contact-host.mjs +105 -0
- package/scripts/pi-durable-receive-host.mjs +127 -0
- package/scripts/pi-durable-send-host.mjs +284 -0
- package/scripts/smoke-herdr-raw-install-live.sh +1 -1
- package/scripts/smoke-pi-durable-fresh-live.ts +624 -0
- package/scripts/smoke-setup-verdict.sh +22 -3
- package/scripts/smoke-user-scope-citizen.sh +1 -1
- package/scripts/tsconfig.json +1 -0
package/docs/setup-clean-host.md
CHANGED
|
@@ -28,7 +28,8 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
28
28
|
| npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
|
|
29
29
|
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot/Codex wiring writers |
|
|
30
30
|
| entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
|
|
31
|
-
| pi | optional-by-presence, `>=1.0.
|
|
31
|
+
| pi | optional-by-presence, `>=1.0.4 <1.1` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
|
|
32
|
+
| Native durable (`pi-durable`) | supplied by the Entwurf package (#130): a pinned upstream v1.0.4 app carrier plus the exact 1.0.4 pi SDK set 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) |
|
|
32
33
|
| Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
|
|
33
34
|
| 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 |
|
|
34
35
|
| 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) |
|
|
@@ -190,7 +191,7 @@ on Darwin; that is wiring, not a rail receipt. If `pi` is on PATH,
|
|
|
190
191
|
Install the exact release floor, then wire the project:
|
|
191
192
|
|
|
192
193
|
```bash
|
|
193
|
-
npm install -g @earendil-works/pi-coding-agent@1.0.
|
|
194
|
+
npm install -g @earendil-works/pi-coding-agent@1.0.4
|
|
194
195
|
pi --version
|
|
195
196
|
|
|
196
197
|
cd ~/entwurf-smoke
|
|
@@ -198,7 +199,7 @@ entwurf install .
|
|
|
198
199
|
pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
|
|
199
200
|
```
|
|
200
201
|
|
|
201
|
-
The supported range is `>=1.0.
|
|
202
|
+
The supported range is `>=1.0.4 <1.1`. It is a hard minimum: installing this
|
|
202
203
|
release onto a 0.83.x pi host upgrades the runtime rather than keeping the older
|
|
203
204
|
minor. A host using only the external MCP bridge can skip pi until it needs a
|
|
204
205
|
control socket; no delivery rail launches a pi process.
|
|
@@ -282,6 +283,122 @@ emacsclient -s "${PI_EMACS_AGENT_SOCKET:-server}" --eval '(...)'
|
|
|
282
283
|
|
|
283
284
|
</details>
|
|
284
285
|
|
|
286
|
+
## 2b. Optional native durable (`pi-durable`)
|
|
287
|
+
|
|
288
|
+
**#130 / 0.32 installation contract: installing Entwurf supplies pi-durable.** The
|
|
289
|
+
operator clones, patches, builds and copies nothing. 0.31.0 shipped the earlier
|
|
290
|
+
source-runtime route (an operator checkout of Pi 1.0.2/source `cd32f77` at a fixed XDG
|
|
291
|
+
place); that route is retired, not kept as a fallback, and its runtime directory is never
|
|
292
|
+
read. Durable is a separate native harness contact, not an option automatically enabled
|
|
293
|
+
by `entwurf pi` or by registering ordinary Pi's adapter.
|
|
294
|
+
|
|
295
|
+
What the Entwurf package supplies:
|
|
296
|
+
|
|
297
|
+
1. **The pi SDK set** — `@earendil-works/{pi-coding-agent, pi-ai, pi-tui, pi-agent-core,
|
|
298
|
+
pi-durable, chord, pi-mcp, pi-codemode, pi-telemetry}` at exactly `1.0.4`, plus
|
|
299
|
+
`proper-lockfile`, as Entwurf's own production dependencies (declared once in
|
|
300
|
+
`pi/pi-durable/overlay/upstream-pin.json` `sdk`). Model data rides the published
|
|
301
|
+
`pi-ai`; there is no catalog copy.
|
|
302
|
+
2. **The native app/TUI carrier** — upstream does not publish the experimental durable
|
|
303
|
+
app (`pi-coding-agent` excludes `dist/experimental`; `pi-durable` on npm is the
|
|
304
|
+
library). Entwurf ships the six pinned app files from upstream tag `v1.0.4`
|
|
305
|
+
(`7c10bd4337495ee613f2224843ecdf349b80d1df`), its narrow runtime-contact overlay
|
|
306
|
+
applied to `runtime.ts`, type-stripped and relocated, in `pi/pi-durable/carrier/`
|
|
307
|
+
with upstream's MIT license. Each file's source blob and emitted digest are in the pin.
|
|
308
|
+
The carrier reaches pi-coding-agent's internals only through its resolver
|
|
309
|
+
(`pi/pi-durable/carrier-resolver.mjs`), which maps them to the published package's
|
|
310
|
+
own `dist/` as installed — never through a source checkout.
|
|
311
|
+
|
|
312
|
+
The upstream [library guide](https://github.com/earendil-works/pi/blob/v1.0.4/packages/durable/README.md)
|
|
313
|
+
and [experimental app guide](https://github.com/earendil-works/pi/blob/v1.0.4/packages/coding-agent/src/experimental/durable/README.md)
|
|
314
|
+
describe what the carrier is. Running the bare upstream app does not attach Entwurf's
|
|
315
|
+
garden contact. Entwurf does not rebuild the controller/model/auth/storage system or
|
|
316
|
+
proxy a subscription; native execution, models, auth, queues, SQLite and recovery stay
|
|
317
|
+
upstream-owned.
|
|
318
|
+
|
|
319
|
+
### What Entwurf checks
|
|
320
|
+
|
|
321
|
+
The carrier is runnable only when, read from where Entwurf is installed: every pinned
|
|
322
|
+
carrier file, the license, the resolver and its relocation grammar are present
|
|
323
|
+
(`pi-durable-package-incomplete`); each carrier file carries its pinned digest
|
|
324
|
+
(`pi-durable-carrier-drift`); every declared SDK package resolves from the carrier
|
|
325
|
+
(`pi-durable-sdk-absent`); and every reachable physical edge — from the carrier to each package,
|
|
326
|
+
and from every package copy it reaches, at any depth, to its own `@earendil-works` dependencies —
|
|
327
|
+
lands on a package of the declared name at exactly the pinned version (`pi-durable-sdk-mismatch`). The members declare one another with caret
|
|
328
|
+
ranges, so semver satisfaction is not the contract; the bytes an import binds are. Sharing
|
|
329
|
+
one copy with an aligned host install of the same version is an ordinary layout. A mixed
|
|
330
|
+
tree (for example an override that binds pi-durable's `pi-ai` one patch behind) still
|
|
331
|
+
*imports* — which is why the edge check exists — and is refused by name.
|
|
332
|
+
|
|
333
|
+
- `entwurf setup` reports a `pi-durable` row from that check and writes no durable wiring:
|
|
334
|
+
**PASS** when it verifies, **FAIL** with the named refusal otherwise — never SKIP, because
|
|
335
|
+
nothing is optional to detect: a missing piece is an incomplete installation, repaired by
|
|
336
|
+
reinstalling Entwurf.
|
|
337
|
+
- `entwurf pi-durable [args…]` is the managed launch. It verifies first (exit 3 on any
|
|
338
|
+
refusal), then runs the packaged bootstrap `pi/pi-durable/bootstrap.mjs` under the carrier
|
|
339
|
+
resolver, owning this terminal:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
entwurf pi-durable --provider <provider> --model <exact catalog id> --width task-wide
|
|
343
|
+
entwurf pi-durable --continue # this directory's newest durable session
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
The bootstrap imports only the compiled closure shipped beside it
|
|
347
|
+
(`mcp/entwurf-bridge/dist/…`); a source checkout needs a built bridge
|
|
348
|
+
(`pnpm run build-bridge`) and its development dependencies installed first.
|
|
349
|
+
`plugins/pi-durable/bootstrap.mjs` is only the checkout path that re-exports it.
|
|
350
|
+
|
|
351
|
+
- **One explicit native module (optional).** Add `--native-module <absolute file>` to either
|
|
352
|
+
launch to load ONE module of your own. It is an ordinary ES module: its top-level code (top-level
|
|
353
|
+
await included) runs first, before the TUI and the durable runtime load, so it can prepare the
|
|
354
|
+
environment the provider runtime and tool children will read; its default export is a native
|
|
355
|
+
durable `Extension` (`{ name, tools?, sections?, hooks?, … }`, the upstream durable API, not
|
|
356
|
+
ordinary Pi's `ExtensionAPI`), installed after Entwurf's contact. An env-only module exports
|
|
357
|
+
just `{ name }`. Refused by name before anything opens: a relative or non-file path
|
|
358
|
+
(`native-module-path-*`), a module that throws (`native-module-import-failed`), a function or
|
|
359
|
+
nameless default (`native-module-export-invalid`), an extension named like one the app installs
|
|
360
|
+
itself (`entwurf`, `coding-tools`, `pi-prompt`, `subagent`) or a tool named like one of Entwurf's
|
|
361
|
+
contact tools (`native-module-name-reserved`; any other duplicate is the native registry's to
|
|
362
|
+
judge), and a module whose initialization changes
|
|
363
|
+
`PI_SESSION_ID`, `PI_CODING_AGENT_DIR`, `HOME` or any `ENTWURF_*` (`native-module-env-identity-changed`)
|
|
364
|
+
or the process directory (`native-module-cwd-changed`). Your module is trusted code; those
|
|
365
|
+
checks catch mistakes, they do not sandbox it. Nothing else names a module — no directory,
|
|
366
|
+
setting or profile — and a sibling opened by `entwurf_fresh_call` starts without one.
|
|
367
|
+
|
|
368
|
+
- **Known warning.** Ordinary Pi may print `Host-provided extension packages must be declared in
|
|
369
|
+
peerDependencies with a "*" range, not dependencies: @earendil-works/…` for Entwurf's
|
|
370
|
+
`package.json`. Entwurf declares the exact pi SDK set as production dependencies on purpose:
|
|
371
|
+
the installed copies serve the separate pi-durable native process. Pi's check reads that
|
|
372
|
+
manifest field; it is a known constraint of supplying both from one package, accepted for 0.32,
|
|
373
|
+
not a claim that every load path is harmless.
|
|
374
|
+
|
|
375
|
+
The ordinary Pi CLI on PATH is a separate subject (§2, range `>=1.0.4 <1.1`): a host Pi is
|
|
376
|
+
not needed to run pi-durable, and a host inside the range need not be the same copy as the
|
|
377
|
+
carrier's. Native Pi owns credentials, model settings and durable SQLite sessions; keep them
|
|
378
|
+
in place rather than copying credentials or migrating ordinary Pi transcripts. Native
|
|
379
|
+
recovery does not prove exactly-once Entwurf dispatch or inbox read recovery. The contact
|
|
380
|
+
provides garden attachment, visible identity and announce-only root-directed self-fetch.
|
|
381
|
+
Package gates prove the installed carrier's load, its physical edges and the public launch up
|
|
382
|
+
to the app's own model resolution; the checkout contact/send/receive gates run the same carrier
|
|
383
|
+
with scripted S plus native H; Pi ↔ pi-durable LIVE on this supply surface is a separate release
|
|
384
|
+
proof ([#130](https://github.com/junghan0611/entwurf/issues/130)).
|
|
385
|
+
|
|
386
|
+
### Maintainer: re-emitting the carrier
|
|
387
|
+
|
|
388
|
+
Only a maintainer moving the pin runs `scripts/emit-pi-durable-carrier.ts`. It reads the
|
|
389
|
+
pinned commit's objects (`<commit>:<path>`, `--no-replace-objects`) from a repository the
|
|
390
|
+
maintainer names, applies the overlay patch with `git apply` in a private scratch tree,
|
|
391
|
+
type-strips with Node's own strip-only TypeScript, relocates, and checks that the six files
|
|
392
|
+
import exactly the pin's declared `distTargets`:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
node scripts/emit-pi-durable-carrier.ts --source <clone holding the pinned commit> --write
|
|
396
|
+
node scripts/emit-pi-durable-carrier.ts --source <clone holding the pinned commit> --check # rebuild equality
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`--check` without such a repository is a named SKIP, never rebuild acceptance; the emitter
|
|
400
|
+
version (Node) is part of the pin.
|
|
401
|
+
|
|
285
402
|
## 3. Optional Claude Code native citizen
|
|
286
403
|
|
|
287
404
|
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending physical
|
|
@@ -445,7 +562,7 @@ as a note rather than a fault.
|
|
|
445
562
|
|
|
446
563
|
**entwurf sets no OMP version floor in code, and will not grow one on schedule.** Detection is
|
|
447
564
|
presence-only (`command -v "${OMP_BIN:-omp}"`); there is no `entwurf.ompFloor`, no coherence
|
|
448
|
-
gate, and no exact pin — unlike Node (`engines.node`), pi (`>=1.0.
|
|
565
|
+
gate, and no exact pin — unlike Node (`engines.node`), pi (`>=1.0.4 <1.1`) and Claude Code
|
|
449
566
|
(`entwurf.claudeCodeFloor`), each of which has an enforcement point. That asymmetry is a
|
|
450
567
|
decision, not an omission. A floor is the answer to a vendor that fails SILENTLY — Claude Code
|
|
451
568
|
earned one because an older binary validates the exec manifest, drops `args` at runtime, and
|
|
@@ -630,8 +630,8 @@ server.tool("entwurf_register_native", "Register an ALREADY-RUNNING native conve
|
|
|
630
630
|
server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling beside you and hand it a first task. WHERE it opens is decided by where THIS " +
|
|
631
631
|
"agent runs, never by a parameter: inside herdr (HERDR_ENV=1) it opens a NEW UNFOCUSED TAB in your own " +
|
|
632
632
|
"herdr workspace, pi and claude-code " +
|
|
633
|
-
"only; everywhere else it opens in the operator's tmux with all
|
|
634
|
-
"claude-code, copilot, omp, codex). No fallback: an incomplete herdr context is refused by name, " +
|
|
633
|
+
"only; everywhere else it opens in the operator's tmux with all six backends (pi, " +
|
|
634
|
+
"claude-code, copilot, omp, codex, pi-durable). No fallback: an incomplete herdr context is refused by name, " +
|
|
635
635
|
"never by a tmux window you cannot see from herdr. The sibling's FIRST action " +
|
|
636
636
|
"is a callback to you carrying a nonce, whose sender envelope is its garden id — that is how you learn the " +
|
|
637
637
|
"address of a thing that did not exist a moment ago. This returns a LAUNCH receipt (the owner's " +
|
|
@@ -639,8 +639,8 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling beside you and
|
|
|
639
639
|
"the task was delivered. Those coordinates are a VIEW, never an address — a herdr tab/pane id can change under " +
|
|
640
640
|
"a running sibling. Nothing polls. " +
|
|
641
641
|
"For EXISTING citizens use entwurf_v2 — this tool only creates, and entwurf_peers only reports. Model is REQUIRED and passed to the chosen " +
|
|
642
|
-
"runtime CLI (`provider/model` for pi, an id/alias for Claude Code, a pattern for the rest). On tmux, copilot/omp/codex are refused BEFORE any window " +
|
|
643
|
-
"opens when their birth, MCP, receive or visible-identity units are absent, and codex also needs the " +
|
|
642
|
+
"runtime CLI (`provider/model` for pi, an id/alias for Claude Code, `provider/model id` exactly as the catalog names it for pi-durable, a pattern for the rest). On tmux, copilot/omp/codex/pi-durable are refused BEFORE any window " +
|
|
643
|
+
"opens when their birth, MCP, receive or visible-identity units are absent, pi-durable also on named refusals of its installed carrier or exact SDK set, and codex also needs the " +
|
|
644
644
|
"operator-owned app-server socket entwurf never starts; in herdr they are refused by name first. " +
|
|
645
645
|
"Optional placement.tmuxSession is a TMUX-ONLY seat naming ONE EXISTING session and " +
|
|
646
646
|
"ALWAYS wins; omitted, the seat follows the CALLER, never the backend opened: a CODEX CALLER opens beside " +
|
|
@@ -648,8 +648,8 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling beside you and
|
|
|
648
648
|
"NOTHING is created. In herdr the field is refused by name. Do not put secrets in the task — model and task argv are visible to same-user " +
|
|
649
649
|
"processes.", {
|
|
650
650
|
backend: z
|
|
651
|
-
.enum(["pi", "claude-code", "copilot", "omp", "codex"])
|
|
652
|
-
.describe("Which fixed runtime to open. Only these
|
|
651
|
+
.enum(["pi", "claude-code", "copilot", "omp", "codex", "pi-durable"])
|
|
652
|
+
.describe("Which fixed runtime to open. Only these six, and only pi/claude-code when this agent runs inside herdr; there is no arbitrary command."),
|
|
653
653
|
model: z
|
|
654
654
|
.string()
|
|
655
655
|
.min(1)
|
|
@@ -663,7 +663,7 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling beside you and
|
|
|
663
663
|
// as an unused suppression while the escape goes back to being fixable.
|
|
664
664
|
// biome-ignore lint/complexity/noUselessEscapeInRegex: emitted to a Rust regex validator, see above
|
|
665
665
|
.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\[\]-]*$/)
|
|
666
|
-
.describe("Required runtime model: canonical provider/model for pi, a Claude Code model id/alias, or a Copilot/OMP/Codex model name."),
|
|
666
|
+
.describe("Required runtime model: canonical provider/model for pi, provider/<exact catalog model id> for pi-durable, a Claude Code model id/alias, or a Copilot/OMP/Codex model name."),
|
|
667
667
|
task: z
|
|
668
668
|
.string()
|
|
669
669
|
.min(1)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"codex": { "wakeMode": "direct-inject", "deliveryLevel": "D6", "nativeIdLabel": "threadId" },
|
|
7
7
|
"copilot": { "wakeMode": "self-fetch", "deliveryLevel": "D6", "nativeIdLabel": "sessionId" },
|
|
8
8
|
"omp": { "wakeMode": "self-fetch", "deliveryLevel": "D6", "nativeIdLabel": "sessionId" },
|
|
9
|
+
"pi-durable": { "wakeMode": "self-fetch", "deliveryLevel": "D2", "nativeIdLabel": "sessionId" },
|
|
9
10
|
"pi": { "wakeMode": "direct-inject", "deliveryLevel": "D6", "nativeIdLabel": "sessionId" }
|
|
10
11
|
}
|
|
11
12
|
}
|
|
@@ -105,7 +105,13 @@ export function receiverMarkerMatchesIdentity(marker, identity) {
|
|
|
105
105
|
// previous garden in-process on its `/new` edge (the unarm the claude hook lacked),
|
|
106
106
|
// so the cell this join closes has a different owner there. Admitting it needs its
|
|
107
107
|
// own measurement, not this file's optimism.
|
|
108
|
-
|
|
108
|
+
//
|
|
109
|
+
// `pi-durable-host` is admitted because the join holds by construction: the durable contact
|
|
110
|
+
// writes its sender marker under the host's own pid and arms its watch in that same process
|
|
111
|
+
// (`meta-bridge-pi-durable.ts`), so the receiver marker's `ownerPid` IS the sender-marker key.
|
|
112
|
+
// Inside the scope a host whose sender marker is gone or names another garden is a retired
|
|
113
|
+
// watch, which is the refusal this admission exists to make reachable.
|
|
114
|
+
export const SENDER_JOINED_RECEIVER_OWNER_KINDS = ["claude-code-cli", "pi-durable-host"];
|
|
109
115
|
/** Does this watch owner share its pid with the backend's sender marker? */
|
|
110
116
|
export function receiverOwnerKindJoinsSender(ownerKind) {
|
|
111
117
|
return SENDER_JOINED_RECEIVER_OWNER_KINDS.includes(ownerKind);
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { randomBytes } from "node:crypto";
|
|
25
25
|
/** The backends this rail can open. Fixed set, not a profile — a further one is a decision,
|
|
26
|
-
* not a config entry. `copilot` was added by #82 RAIL 9, `omp` by #87 Bundle C,
|
|
27
|
-
* by #95 after its system birth and app-server rails were measured
|
|
26
|
+
* not a config entry. `copilot` was added by #82 RAIL 9, `omp` by #87 Bundle C, `codex`
|
|
27
|
+
* by #95 after its system birth and app-server rails were measured, and `pi-durable` by #129
|
|
28
|
+
* (the durable app this package carries, through the managed `entwurf pi-durable`). The set is joined to the
|
|
28
29
|
* citizen backends by `check-harness-admission-parity`: a harness that mints records but is
|
|
29
30
|
* missing HERE is not an unwired convenience, it is a release blocker. */
|
|
30
|
-
export const FRESH_CALL_BACKENDS = ["pi", "claude-code", "copilot", "omp", "codex"];
|
|
31
|
+
export const FRESH_CALL_BACKENDS = ["pi", "claude-code", "copilot", "omp", "codex", "pi-durable"];
|
|
31
32
|
/**
|
|
32
33
|
* The callback tool NAME differs per backend and that is not cosmetic: every session reaches the
|
|
33
34
|
* capability under whatever name its harness composes for the `entwurf-bridge` MCP server, and
|
|
@@ -64,6 +65,11 @@ export const FRESH_CALL_CALLBACK_TOOL = {
|
|
|
64
65
|
// Codex's tool process is the operator app-server, not the pane: the no-arg
|
|
65
66
|
// verb refuses there by name. First action stays the delivery verb with args.
|
|
66
67
|
codex: "mcp__entwurf_bridge__entwurf_v2",
|
|
68
|
+
// The durable contact registers the bridge verbs as its OWN tools under their bare names, and the
|
|
69
|
+
// app offers a tool to the model by its registered name (`durable/harness/registry.ts:99`
|
|
70
|
+
// @cd32f77). Measured for `entwurf_v2` on the S+native-H wire (receipt 4012c066); the callback
|
|
71
|
+
// spelling follows the same rule and is owed its first LIVE observation.
|
|
72
|
+
"pi-durable": "entwurf_callback",
|
|
67
73
|
};
|
|
68
74
|
/** Where a sibling SENDS the task result. Always the delivery verb, never the birth callback. */
|
|
69
75
|
export const FRESH_CALL_DELIVERY_TOOL = {
|
|
@@ -72,6 +78,7 @@ export const FRESH_CALL_DELIVERY_TOOL = {
|
|
|
72
78
|
copilot: "entwurf-bridge-entwurf_v2",
|
|
73
79
|
omp: "mcp__entwurf_bridge_entwurf_v",
|
|
74
80
|
codex: "mcp__entwurf_bridge__entwurf_v2",
|
|
81
|
+
"pi-durable": "entwurf_v2",
|
|
75
82
|
};
|
|
76
83
|
/**
|
|
77
84
|
* The same dialects, for the tool the framing OFFERS rather than requires.
|
|
@@ -93,6 +100,7 @@ export const FRESH_CALL_PEERS_TOOL = {
|
|
|
93
100
|
copilot: "entwurf-bridge-entwurf_peers",
|
|
94
101
|
omp: "mcp__entwurf_bridge_entwurf_peers",
|
|
95
102
|
codex: "mcp__entwurf_bridge__entwurf_peers",
|
|
103
|
+
"pi-durable": "entwurf_peers",
|
|
96
104
|
};
|
|
97
105
|
/**
|
|
98
106
|
* ONE BACKEND NEEDS TO BE TOLD HOW TO REACH ITS OWN CALLBACK TOOL `[측정 2026-09-18, n=5 + n=3]`.
|
|
@@ -132,6 +140,7 @@ export const FRESH_CALL_TOOL_LOAD_HINT = {
|
|
|
132
140
|
copilot: [],
|
|
133
141
|
omp: [],
|
|
134
142
|
codex: [],
|
|
143
|
+
"pi-durable": [],
|
|
135
144
|
};
|
|
136
145
|
/**
|
|
137
146
|
* The omp bootstrap flag, spelled WITHOUT dashes — the vendor's flag map is keyed by bare
|
|
@@ -311,6 +320,28 @@ export function composeBackendArgs(backend, composition, model, resolveCodexSock
|
|
|
311
320
|
"--dangerously-bypass-approvals-and-sandbox",
|
|
312
321
|
composition.prompt,
|
|
313
322
|
];
|
|
323
|
+
case "pi-durable": {
|
|
324
|
+
// The managed verb (`entwurf pi-durable`) carries the explicit model as pi's own
|
|
325
|
+
// `--provider`/`--model` pair, split at the FIRST `/` (the preflight already refused a model
|
|
326
|
+
// with no provider), the explicit width, and the WHOLE framing as the closed `{v,task}`
|
|
327
|
+
// payload — the durable bootstrap submits it to the root once, after the birth. The
|
|
328
|
+
// caller's address rides the env pair, never this payload.
|
|
329
|
+
const at = model.indexOf("/");
|
|
330
|
+
if (at <= 0 || at === model.length - 1) {
|
|
331
|
+
throw new Error(`fresh-call composition: pi-durable model ${JSON.stringify(model)} is not <provider>/<model id>`);
|
|
332
|
+
}
|
|
333
|
+
return [
|
|
334
|
+
"pi-durable",
|
|
335
|
+
"--provider",
|
|
336
|
+
model.slice(0, at),
|
|
337
|
+
"--model",
|
|
338
|
+
model.slice(at + 1),
|
|
339
|
+
"--width",
|
|
340
|
+
"task-wide",
|
|
341
|
+
`--${OMP_BOOTSTRAP_FLAG}`,
|
|
342
|
+
JSON.stringify({ v: OMP_BOOTSTRAP_VERSION, task: composition.prompt }),
|
|
343
|
+
];
|
|
344
|
+
}
|
|
314
345
|
}
|
|
315
346
|
}
|
|
316
347
|
/**
|
|
@@ -57,7 +57,13 @@ import { resolveNativePushAdapter } from "./native-push/adapter.js";
|
|
|
57
57
|
* exactly the #46 defect on agy, and copilot joined the list only once its own hook wrote one
|
|
58
58
|
* (#82 RAIL 5b). Membership says a marker may EXIST, never that a reply can land: the reply
|
|
59
59
|
* rail is chosen from `nativePushSupported` at the bridge, not from this list. */
|
|
60
|
-
export const META_SENDER_BACKENDS = [
|
|
60
|
+
export const META_SENDER_BACKENDS = [
|
|
61
|
+
"claude-code",
|
|
62
|
+
"antigravity",
|
|
63
|
+
"copilot",
|
|
64
|
+
"omp",
|
|
65
|
+
"pi-durable",
|
|
66
|
+
];
|
|
61
67
|
/**
|
|
62
68
|
* Two live native sessions claim this MCP process as their own. We can SEE both identities but
|
|
63
69
|
* cannot say which one is calling, so we send under neither. Choosing by lookup order, backend
|
|
@@ -76,7 +76,7 @@ export class MetaRecordError extends Error {
|
|
|
76
76
|
* every write, including its own. `./run.sh doctor-meta-bridge` already flags a
|
|
77
77
|
* stale deployed writer — run it after the change.
|
|
78
78
|
*/
|
|
79
|
-
export const META_BACKENDS = ["claude-code", "antigravity", "codex", "copilot", "omp"];
|
|
79
|
+
export const META_BACKENDS = ["claude-code", "antigravity", "codex", "copilot", "omp", "pi-durable"];
|
|
80
80
|
export const META_BACKEND_DESCRIPTORS = {
|
|
81
81
|
"claude-code": {
|
|
82
82
|
backend: "claude-code",
|
|
@@ -189,6 +189,24 @@ export const META_BACKEND_DESCRIPTORS = {
|
|
|
189
189
|
deliveryLevel: "D6",
|
|
190
190
|
nativeIdLabel: "sessionId",
|
|
191
191
|
},
|
|
192
|
+
// The long-running durable app (#129). Graded only as far as its RECORDED receipts reach.
|
|
193
|
+
// sessionId the durable session directory name `<13-digit ms>-<uuid>` that
|
|
194
|
+
// `selectSession` creates and `--continue` reopens
|
|
195
|
+
// (`experimental/durable/sessions.ts:18-55` @cd32f77), surfaced as
|
|
196
|
+
// `view.current().session.id`. Not `pi.provider.sessionId`, which a
|
|
197
|
+
// fork changes.
|
|
198
|
+
// D2 receiver armed: the contact mints the record, names the host as sender, and arms
|
|
199
|
+
// a root doorbell (the receiver marker is owned by the host pid as `pi-durable-host`,
|
|
200
|
+
// which the owner join admits). The addressed-enqueue → idle-wake → root-notice → drain
|
|
201
|
+
// chain has ONE recorded receipt, on the scripted-model + native-harness lab cell
|
|
202
|
+
// (`check-pi-durable-receive`, S+H, V not run) — lab evidence, not a vendor-turn D4–D6
|
|
203
|
+
// acceptance, and no sibling-isolation (D3) or LIVE fresh receipt exists yet.
|
|
204
|
+
"pi-durable": {
|
|
205
|
+
backend: "pi-durable",
|
|
206
|
+
wakeMode: "self-fetch",
|
|
207
|
+
deliveryLevel: "D2",
|
|
208
|
+
nativeIdLabel: "sessionId",
|
|
209
|
+
},
|
|
192
210
|
};
|
|
193
211
|
// ---------------------------------------------------------------------------
|
|
194
212
|
// Validation helpers (crash, don't warn)
|
|
@@ -261,7 +279,15 @@ function isoNow(now) {
|
|
|
261
279
|
/** The one live identity schema number. */
|
|
262
280
|
export const META_SCHEMA_VERSION_V3 = 3;
|
|
263
281
|
/** Every backend admitted by the one V3 record-citizen schema. */
|
|
264
|
-
export const META_CITIZEN_BACKENDS = [
|
|
282
|
+
export const META_CITIZEN_BACKENDS = [
|
|
283
|
+
"claude-code",
|
|
284
|
+
"antigravity",
|
|
285
|
+
"codex",
|
|
286
|
+
"copilot",
|
|
287
|
+
"omp",
|
|
288
|
+
"pi-durable",
|
|
289
|
+
"pi",
|
|
290
|
+
];
|
|
265
291
|
export function requireCitizenBackend(value) {
|
|
266
292
|
if (typeof value !== "string" || !META_CITIZEN_BACKENDS.includes(value)) {
|
|
267
293
|
throw new MetaRecordError(`meta-record "backend" must be one of ${META_CITIZEN_BACKENDS.join(" | ")} (got ${describe(value)}).`);
|
|
@@ -95,6 +95,8 @@ import { assertLaunchTarget, LaunchPreconditionError, resolveRuntimeOnPath, } fr
|
|
|
95
95
|
import { APPEND_FORMAT, assertSelector, assertTmuxOk, inspectPlacement, parseWindowFields, requireSameContext, runTmux, } from "./mux-placement.js";
|
|
96
96
|
import { resolveCodexDefaultSocketPath } from "./native-push/codex-ws-client.js";
|
|
97
97
|
import { OMP_PREFLIGHT_HINT, ompFreshPreflight } from "./omp-fresh-preflight.js";
|
|
98
|
+
import { PI_DURABLE_PREFLIGHT_HINT, piDurableFreshPreflight, } from "./pi-durable-fresh-preflight.js";
|
|
99
|
+
import { checkPiDurableRuntime, PI_DURABLE_RUNTIME_HINT, piDurablePackageLayout } from "./pi-durable-runtime.js";
|
|
98
100
|
import { classifyTmuxSessionName, resolveTmuxSessionId } from "./resolve-tmux-session.js";
|
|
99
101
|
/**
|
|
100
102
|
* THE COMPOSITION MOVED, THE RAIL DID NOT (#116 S2-a). Backend argv, the first-turn framing and
|
|
@@ -154,6 +156,10 @@ export const FRESH_CALL_RUNTIME = {
|
|
|
154
156
|
copilot: "entwurf",
|
|
155
157
|
omp: "omp",
|
|
156
158
|
codex: "codex",
|
|
159
|
+
// #129/#130: like copilot, a MANAGED verb and never the bare app. The durable app is this
|
|
160
|
+
// package's carrier and runs under the carrier's resolver; `entwurf pi-durable` is the one fixed
|
|
161
|
+
// invocation that verifies the carrier and its SDK set and execs the packaged bootstrap.
|
|
162
|
+
"pi-durable": "entwurf",
|
|
157
163
|
};
|
|
158
164
|
/**
|
|
159
165
|
* WHICH RULE PICKS THE TARGET SESSION. Three in strict order, and the order is the contract:
|
|
@@ -197,9 +203,9 @@ export function selectFreshCallSeat(placement) {
|
|
|
197
203
|
* for. That is sufficient and not a compromise: every reader of the carrier trims and tests
|
|
198
204
|
* truthiness (`index.ts:212-217`), so empty and absent are the same answer by construction.
|
|
199
205
|
*
|
|
200
|
-
* It is applied to all
|
|
206
|
+
* It is applied to all six backends because the leak is a property of tmux, not of a vendor. A
|
|
201
207
|
* scrub only on the backend whose measurement surfaced it would encode the claim that the other
|
|
202
|
-
*
|
|
208
|
+
* five are immune, which is false. It costs the legitimate case nothing: a carrier is only ever
|
|
203
209
|
* authoritative when the process that owns it exported it ITSELF, and a fresh `pi` sibling does
|
|
204
210
|
* exactly that after this argv has run. This is a fixed seam and deliberately NOT a general env
|
|
205
211
|
* carrier — an arbitrary `-e` passthrough would hand callers the environment-shaping power this
|
|
@@ -337,6 +343,24 @@ export function freshCall(params, env = process.env, nonce = mintNonce()) {
|
|
|
337
343
|
if (missing)
|
|
338
344
|
return { ok: false, reason: missing };
|
|
339
345
|
}
|
|
346
|
+
if (params.backend === "pi-durable") {
|
|
347
|
+
// The framed first input is computed here only to MEASURE it against the bootstrap payload
|
|
348
|
+
// cap; the same pure function composes it again below. The carrier is verified from THIS
|
|
349
|
+
// process's package closure, before anything is placed. The window runs whatever `entwurf`
|
|
350
|
+
// PATH names, and the managed verb re-verifies its own package before it execs anything, so
|
|
351
|
+
// the window never runs an unverified carrier — but when the two package closures differ, this
|
|
352
|
+
// verdict and the window's can disagree, and the window is left showing its own refusal.
|
|
353
|
+
const layout = piDurablePackageLayout();
|
|
354
|
+
const runtime = layout === null ? null : checkPiDurableRuntime(layout);
|
|
355
|
+
const missing = piDurableFreshPreflight({
|
|
356
|
+
model,
|
|
357
|
+
firstInput: buildFreshCallPrompt({ backend: params.backend, task, callerGardenId, nonce }),
|
|
358
|
+
layout,
|
|
359
|
+
runtime: runtime === null || runtime.ok ? null : runtime.reason,
|
|
360
|
+
});
|
|
361
|
+
if (missing)
|
|
362
|
+
return { ok: false, reason: missing };
|
|
363
|
+
}
|
|
340
364
|
const inspected = inspectPlacement(env);
|
|
341
365
|
if (!inspected.ok)
|
|
342
366
|
return { ok: false, reason: inspected.reason };
|
|
@@ -448,6 +472,8 @@ const REJECT_HINT = {
|
|
|
448
472
|
// the sentence an operator reads cannot drift away from the predicate that produced it.
|
|
449
473
|
...COPILOT_PREFLIGHT_HINT,
|
|
450
474
|
...OMP_PREFLIGHT_HINT,
|
|
475
|
+
...PI_DURABLE_RUNTIME_HINT,
|
|
476
|
+
...PI_DURABLE_PREFLIGHT_HINT,
|
|
451
477
|
...CODEX_PREFLIGHT_HINT,
|
|
452
478
|
...CODEX_CALLER_PREFLIGHT_HINT,
|
|
453
479
|
...CODEX_CALLER_SEAT_HINT,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pi-durable-fresh-preflight — the pre-mutation check a pi-durable fresh call needs (#129 W, L6;
|
|
3
|
+
* `docs/adding-a-harness.md` step 9 clauses 2, 3 and 5).
|
|
4
|
+
*
|
|
5
|
+
* Same question as `omp-fresh-preflight.ts`, at the same moment: before `mux-fresh-call` mutates the
|
|
6
|
+
* operator's tmux session, is everything the fresh contract needs actually in place? A refusal HERE,
|
|
7
|
+
* before placement, leaves no window behind. A pass here does not promise the window's own pass: the
|
|
8
|
+
* managed verb re-verifies inside the window, whose env is the tmux SERVER's and whose `entwurf` is
|
|
9
|
+
* whatever PATH names, so under env or package-closure skew it can still refuse after the window
|
|
10
|
+
* exists — and that refusal is left showing in it.
|
|
11
|
+
*
|
|
12
|
+
* ── The inputs, first ──
|
|
13
|
+
*
|
|
14
|
+
* model the fresh-call model grammar already admits `/`, and a durable launch needs the
|
|
15
|
+
* provider spelled out (the bootstrap refuses a model resolved by inference), so
|
|
16
|
+
* the model here is `<provider>/<model id>` split at the FIRST `/` — a catalog id
|
|
17
|
+
* that itself contains `/` keeps it. A model with no provider part is refused.
|
|
18
|
+
* first input the whole framing (callback first, then the task) rides the bootstrap's closed
|
|
19
|
+
* `{v:2,task}` payload, whose decoder caps `task` at 16000 characters. A framed
|
|
20
|
+
* task over that cap would open a window whose bootstrap refuses its own argv.
|
|
21
|
+
*
|
|
22
|
+
* ── Then the carrier, then the four units ──
|
|
23
|
+
*
|
|
24
|
+
* carrier the package's own pi-durable carrier and its declared SDK set, verified
|
|
25
|
+
* (`pi-durable-runtime.ts`); passed in as that check's verdict.
|
|
26
|
+
* birth the compiled contact in this package's closure — it mints the record.
|
|
27
|
+
* MCP hand the compiled entwurf-bridge entry in the same closure — the contact spawns it.
|
|
28
|
+
* receive the compiled owner join in the same closure admits `pi-durable-host`; without
|
|
29
|
+
* it every dispatch to the citizen is the honest `mailbox-undeliverable`.
|
|
30
|
+
* visible identity the packaged bootstrap — the one that hands the TUI the view whose conversation
|
|
31
|
+
* labels carry the garden id.
|
|
32
|
+
*
|
|
33
|
+
* ── What this deliberately does NOT claim ──
|
|
34
|
+
*
|
|
35
|
+
* Filesystem truth about ONE package closure and the SDK it resolves. It does not prove the durable
|
|
36
|
+
* app opens, the bridge connects, the doorbell arms or a garden id renders — that is runtime truth for
|
|
37
|
+
* the clause 7 LIVE receipt. No spawn, no network, no mutation; every path arrives as an argument, so
|
|
38
|
+
* this leaf imports nothing but types.
|
|
39
|
+
*/
|
|
40
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
41
|
+
/** The bootstrap decoder's task cap (`meta-bridge-omp.ts` OMP_BOOTSTRAP_TASK_MAX_CHARS); the beside
|
|
42
|
+
* test holds the two equal. */
|
|
43
|
+
export const PI_DURABLE_FIRST_INPUT_MAX_CHARS = 16000;
|
|
44
|
+
/** `<provider>/<model id>` at the FIRST `/`, or `null` when either side is empty. */
|
|
45
|
+
export function splitPiDurableModel(model) {
|
|
46
|
+
const at = model.indexOf("/");
|
|
47
|
+
if (at <= 0 || at === model.length - 1)
|
|
48
|
+
return null;
|
|
49
|
+
return { provider: model.slice(0, at), model: model.slice(at + 1) };
|
|
50
|
+
}
|
|
51
|
+
/** The compiled owner join, read from the emitted bytes the bridge actually loads. */
|
|
52
|
+
function ownerJoinAdmitsDurableHost(file) {
|
|
53
|
+
try {
|
|
54
|
+
const join = /SENDER_JOINED_RECEIVER_OWNER_KINDS\s*=\s*(\[[^\]]*\])/.exec(readFileSync(file, "utf8"))?.[1];
|
|
55
|
+
return join?.includes('"pi-durable-host"') ?? false;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Bounded environment probe: an unreadable file is a missing unit.
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** The FIRST missing thing, in repair order, or `null` when a fresh launch may proceed. */
|
|
63
|
+
export function piDurableFreshPreflight(params) {
|
|
64
|
+
if (splitPiDurableModel(params.model) === null)
|
|
65
|
+
return "pi-durable-model-not-provider-qualified";
|
|
66
|
+
if (params.firstInput.length > PI_DURABLE_FIRST_INPUT_MAX_CHARS)
|
|
67
|
+
return "pi-durable-first-input-too-long";
|
|
68
|
+
if (params.layout === null)
|
|
69
|
+
return "pi-durable-package-incomplete";
|
|
70
|
+
if (params.runtime !== null)
|
|
71
|
+
return params.runtime;
|
|
72
|
+
const layout = params.layout;
|
|
73
|
+
if (!existsSync(layout.adapter))
|
|
74
|
+
return "pi-durable-birth-unit-missing";
|
|
75
|
+
if (!existsSync(layout.bridgeEntry))
|
|
76
|
+
return "pi-durable-mcp-hand-missing";
|
|
77
|
+
if (!ownerJoinAdmitsDurableHost(layout.deliverability))
|
|
78
|
+
return "pi-durable-receive-unit-missing";
|
|
79
|
+
if (!existsSync(layout.bootstrap))
|
|
80
|
+
return "pi-durable-visible-identity-missing";
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/** Repair text beside the predicate that decides it. The runtime reasons keep theirs on the runtime
|
|
84
|
+
* leaf; `mux-fresh-call` spreads both tables. */
|
|
85
|
+
export const PI_DURABLE_PREFLIGHT_HINT = {
|
|
86
|
+
"pi-durable-model-not-provider-qualified": "a pi-durable model is `<provider>/<model id>` — the durable bootstrap resolves no provider by inference",
|
|
87
|
+
"pi-durable-first-input-too-long": "the framed first input exceeds the 16000-character bootstrap payload cap — shorten the task",
|
|
88
|
+
"pi-durable-birth-unit-missing": "this Entwurf package has no compiled pi-durable contact (mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js), so the sibling would mint no record — reinstall or rebuild Entwurf",
|
|
89
|
+
"pi-durable-mcp-hand-missing": "this Entwurf package has no compiled entwurf-bridge entry, so the sibling's callback tool would not exist — reinstall or rebuild Entwurf",
|
|
90
|
+
"pi-durable-receive-unit-missing": "this Entwurf package's compiled owner join does not admit pi-durable-host, so nothing could ever be delivered to the sibling — reinstall or rebuild Entwurf",
|
|
91
|
+
"pi-durable-visible-identity-missing": "this Entwurf package has no packaged pi-durable bootstrap (pi/pi-durable/bootstrap.mjs), so no TUI would carry the garden id — reinstall Entwurf",
|
|
92
|
+
};
|