@junghanacs/entwurf 0.31.0 → 0.33.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.
Files changed (58) hide show
  1. package/AGENTS.md +3 -2
  2. package/CHANGELOG.md +128 -0
  3. package/README.md +62 -117
  4. package/VERIFY.md +40 -21
  5. package/demo/README.md +1 -1
  6. package/docs/acp-backend-rail.md +1 -1
  7. package/docs/setup-clean-host.md +112 -95
  8. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +1 -1
  9. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +1 -1
  10. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +8 -10
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +3 -3
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +153 -205
  13. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +6 -4
  14. package/mcp/entwurf-bridge/src/index.ts +1 -1
  15. package/package.json +14 -17
  16. package/pi/pi-durable/bootstrap.mjs +142 -19
  17. package/pi/pi-durable/carrier/LICENSE +21 -0
  18. package/pi/pi-durable/carrier/harness-setup.js +123 -0
  19. package/pi/pi-durable/carrier/prompt.js +74 -0
  20. package/pi/pi-durable/carrier/runtime.js +402 -0
  21. package/pi/pi-durable/carrier/sessions.js +56 -0
  22. package/pi/pi-durable/carrier/subagent.js +55 -0
  23. package/pi/pi-durable/carrier/tui.js +655 -0
  24. package/pi/pi-durable/carrier-relocation.mjs +160 -0
  25. package/pi/pi-durable/carrier-resolver.mjs +38 -0
  26. package/pi/pi-durable/overlay/upstream-pin.json +103 -11
  27. package/pi-extensions/lib/fresh-call-composition.ts +1 -1
  28. package/pi-extensions/lib/mux-fresh-call.ts +8 -10
  29. package/pi-extensions/lib/pi-durable-fresh-preflight.ts +3 -3
  30. package/pi-extensions/lib/pi-durable-runtime.ts +197 -263
  31. package/pi-extensions/meta-bridge-pi-durable.ts +13 -7
  32. package/run.sh +519 -248
  33. package/scripts/check-entwurf-bridge-pi-free.ts +7 -5
  34. package/scripts/check-fresh-call-dispatch.ts +1 -1
  35. package/scripts/check-gate-qualification.ts +86 -34
  36. package/scripts/check-herdr-activation.ts +5 -4
  37. package/scripts/check-pi-durable-contact.ts +26 -93
  38. package/scripts/check-pi-durable-receive.ts +22 -59
  39. package/scripts/check-pi-durable-send.ts +235 -75
  40. package/scripts/check-release-gate-outcomes.ts +21 -17
  41. package/scripts/emit-pi-durable-carrier.ts +355 -0
  42. package/scripts/lib/mutation-qualify.ts +21 -45
  43. package/scripts/lib/pi-durable-operator-guard.ts +89 -0
  44. package/scripts/lib/qualification-receipt.ts +7 -11
  45. package/scripts/mutants/control-send-receipt.json +1 -1
  46. package/scripts/mutants/gate-qualification.json +13 -0
  47. package/scripts/mutants/mux-fresh-call.json +13 -13
  48. package/scripts/mutants/pack-install.json +3 -3
  49. package/scripts/mutants/pi-durable.json +442 -109
  50. package/scripts/mutants/pi-floor.json +24 -2
  51. package/scripts/mutants/release-gate.json +5 -3
  52. package/scripts/pi-durable-contact-host.mjs +3 -3
  53. package/scripts/pi-durable-receive-host.mjs +3 -3
  54. package/scripts/pi-durable-send-host.mjs +76 -5
  55. package/scripts/smoke-herdr-raw-install-live.sh +1 -1
  56. package/scripts/smoke-pi-durable-fresh-live.ts +3 -3
  57. package/scripts/smoke-setup-verdict.sh +15 -15
  58. package/scripts/smoke-user-scope-citizen.sh +1 -1
@@ -0,0 +1,160 @@
1
+ // The pi-durable carrier's ONE relocation grammar (#130), shared by the maintainer emitter
2
+ // (scripts/emit-pi-durable-carrier.ts) and the runtime resolver (./carrier-resolver.mjs), so the
3
+ // rewrite the emitter performs and the resolution the runtime accepts cannot drift apart.
4
+ //
5
+ // The durable app is source-only upstream: published `@earendil-works/pi-coding-agent` ships no
6
+ // `dist/experimental`, and its exports map exposes no deep `dist/` path. The carrier is the pinned
7
+ // upstream app files, type-stripped, with exactly two rewrites:
8
+ //
9
+ // "./<sibling>.ts" → "./<sibling>.js" a carrier file importing another carrier file
10
+ // "../../<path>.ts" → entwurf-pi-dist:<path>.js the app importing a pi-coding-agent internal
11
+ //
12
+ // Every other specifier — `@earendil-works/*`, `proper-lockfile`, `node:*` — is left untouched and
13
+ // resolves by Node's own rules from where the carrier is installed. The reserved scheme is chosen so
14
+ // that a carrier loaded WITHOUT the resolver fails by name (Node refuses an unknown URL scheme) rather
15
+ // than quietly resolving `../../` inside this package. The resolver accepts the scheme only from a
16
+ // declared carrier file and only for a declared target, and maps it to that file under the PHYSICAL
17
+ // root of the pi-coding-agent the carrier itself resolves — never through the exports map.
18
+ //
19
+ // Plain JS on purpose: the resolver is preloaded with `node --import` from an installed package,
20
+ // where Node does not strip types, and it must not depend on the compiled bridge closure.
21
+
22
+ import { existsSync, readFileSync } from "node:fs";
23
+ import path from "node:path";
24
+ import { fileURLToPath, pathToFileURL } from "node:url";
25
+
26
+ export const CARRIER_SCHEME = "entwurf-pi-dist:";
27
+ export const CODING_AGENT = "@earendil-works/pi-coding-agent";
28
+
29
+ /** A refusal with a stable name first, as the bootstrap's. */
30
+ function refuse(reason, detail) {
31
+ return Object.assign(new Error(`${reason}: ${detail}`), { reason });
32
+ }
33
+
34
+ /** A static import, export-from or side-effect import statement as upstream writes them: double quotes,
35
+ * the statement ending its line. A multi-line `import {` … `} from "x";` matches from its `import`. */
36
+ const STATEMENT = /^(\s*(?:import|export|\})[^;]*?\bfrom\s*")([^"]+)(";[ \t]*)$|^(\s*import\s*")([^"]+)(";[ \t]*)$/gm;
37
+ /** Every place a module specifier can begin — the statements above, and anything they would miss. */
38
+ const ANY_SPECIFIER = /\b(?:from|import)\s*["'(]/g;
39
+
40
+ /**
41
+ * Rewrite one type-stripped carrier source. `siblings` are the carrier file names (`runtime.js` …),
42
+ * `targets` the declared pi-coding-agent dist paths (`core/model-runtime.js` …). Throws by name on a
43
+ * relative specifier outside those two rules, a dynamic import, or a specifier the statement grammar
44
+ * did not recognize. Returns the rewritten code and the targets it used.
45
+ *
46
+ * @param {string} file
47
+ * @param {string} code
48
+ * @param {{ siblings: readonly string[], targets: readonly string[] }} declared
49
+ * @returns {{ code: string, used: string[] }}
50
+ */
51
+ export function relocateCarrierSource(file, code, { siblings, targets }) {
52
+ const used = new Set();
53
+ let statements = 0;
54
+ const rewrite = (spec) => {
55
+ if (spec.startsWith("./")) {
56
+ const name = `${spec.slice(2, -3)}.js`;
57
+ if (!spec.endsWith(".ts") || !siblings.includes(name)) {
58
+ throw refuse("carrier-relocation-unknown-sibling", `${file} imports ${spec}`);
59
+ }
60
+ return `./${name}`;
61
+ }
62
+ if (spec.startsWith("../../")) {
63
+ const target = `${spec.slice(6, -3)}.js`;
64
+ if (!spec.endsWith(".ts") || !targets.includes(target)) {
65
+ throw refuse("carrier-relocation-unknown-target", `${file} imports ${spec}`);
66
+ }
67
+ used.add(target);
68
+ return `${CARRIER_SCHEME}${target}`;
69
+ }
70
+ if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith(CARRIER_SCHEME)) {
71
+ throw refuse("carrier-relocation-unknown-relative", `${file} imports ${spec}`);
72
+ }
73
+ return spec;
74
+ };
75
+ const out = code.replace(STATEMENT, (_all, headA, specA, tailA, headB, specB, tailB) => {
76
+ statements++;
77
+ return headA !== undefined ? `${headA}${rewrite(specA)}${tailA}` : `${headB}${rewrite(specB)}${tailB}`;
78
+ });
79
+ const begun = (code.match(ANY_SPECIFIER) ?? []).length;
80
+ if (begun !== statements) {
81
+ throw refuse(
82
+ "carrier-relocation-unrecognized-import",
83
+ `${file}: ${begun} specifier openings, ${statements} recognized statements (a dynamic import, a single-quoted or mid-line import)`,
84
+ );
85
+ }
86
+ return { code: out, used: [...used].sort() };
87
+ }
88
+
89
+ /**
90
+ * The carrier section of the packaged pin, validated. A malformed pin throws (crash, don't warn).
91
+ *
92
+ * @param {URL | string} pinPath
93
+ * @returns {{ files: string[], targets: string[] }}
94
+ */
95
+ export function readCarrierDeclaration(pinPath) {
96
+ const pin = JSON.parse(readFileSync(pinPath, "utf8"));
97
+ const carrier = pin?.carrier;
98
+ const names = Array.isArray(carrier?.files) ? carrier.files.map((f) => f?.name) : undefined;
99
+ const targets = carrier?.distTargets;
100
+ const plain = (s) => typeof s === "string" && /^[a-z0-9-]+(?:\/[a-z0-9-]+)*\.js$/.test(s);
101
+ if (!names?.every(plain) || names.length === 0 || names.some((n) => n.includes("/"))) {
102
+ throw refuse("pi-durable-carrier-pin-malformed", "carrier.files must name plain carrier .js files");
103
+ }
104
+ if (!Array.isArray(targets) || targets.length === 0 || !targets.every(plain)) {
105
+ throw refuse("pi-durable-carrier-pin-malformed", "carrier.distTargets must list relative dist .js paths");
106
+ }
107
+ return { files: names, targets };
108
+ }
109
+
110
+ /**
111
+ * The PHYSICAL `dist/` directory of the pi-coding-agent the carrier resolves, from the URL its root
112
+ * export resolved to (`<root>/dist/index.js`). Refuses by name when that is not the package root.
113
+ *
114
+ * @param {string} rootExportUrl
115
+ */
116
+ export function codingAgentDistFrom(rootExportUrl) {
117
+ const entry = rootExportUrl.startsWith("file:") ? fileURLToPath(rootExportUrl) : "";
118
+ const dist = path.dirname(entry);
119
+ const root = path.dirname(dist);
120
+ const manifest = path.join(root, "package.json");
121
+ if (path.basename(entry) !== "index.js" || path.basename(dist) !== "dist" || !existsSync(manifest)) {
122
+ throw refuse(
123
+ "pi-durable-carrier-host-root",
124
+ `${CODING_AGENT} resolved to ${rootExportUrl}, not <root>/dist/index.js`,
125
+ );
126
+ }
127
+ const name = JSON.parse(readFileSync(manifest, "utf8")).name;
128
+ if (name !== CODING_AGENT) {
129
+ throw refuse("pi-durable-carrier-host-root", `${manifest} names ${JSON.stringify(name)}, not ${CODING_AGENT}`);
130
+ }
131
+ return dist;
132
+ }
133
+
134
+ /**
135
+ * Resolve one reserved specifier. Only a declared carrier file may ask, only for a declared target,
136
+ * and the target file must exist. Returns the file URL; everything else throws by name.
137
+ *
138
+ * @param {string} specifier begins with CARRIER_SCHEME
139
+ * @param {string | undefined} parentURL
140
+ * @param {{ carrierDirUrl: string, files: readonly string[], targets: readonly string[], dist: () => string }} declared
141
+ */
142
+ export function resolveCarrierSpecifier(specifier, parentURL, { carrierDirUrl, files, targets, dist }) {
143
+ const parent =
144
+ typeof parentURL === "string" && parentURL.startsWith(carrierDirUrl) ? parentURL.slice(carrierDirUrl.length) : "";
145
+ if (!files.includes(parent)) {
146
+ throw refuse(
147
+ "pi-durable-carrier-specifier-origin",
148
+ `${specifier} asked from ${parentURL ?? "no parent"}, not a carrier file`,
149
+ );
150
+ }
151
+ const target = specifier.slice(CARRIER_SCHEME.length);
152
+ if (!targets.includes(target)) {
153
+ throw refuse("pi-durable-carrier-specifier-unknown", `${specifier} (from ${parent}) is not a declared target`);
154
+ }
155
+ const file = path.join(dist(), target);
156
+ if (!existsSync(file)) {
157
+ throw refuse("pi-durable-carrier-target-missing", `${specifier} → ${file} does not exist`);
158
+ }
159
+ return pathToFileURL(file).href;
160
+ }
@@ -0,0 +1,38 @@
1
+ // The pi-durable carrier's resolver (#130): preloaded as `node --import <this file>` before the
2
+ // packaged bootstrap. It registers ONE synchronous resolve hook that answers only the carrier's
3
+ // reserved entwurf-pi-dist: specifiers (grammar and refusals: ./carrier-relocation.mjs) and hands
4
+ // every other specifier to Node unchanged. It is not a source/TypeScript resolver and it never reads
5
+ // a checkout, a runtime directory or an environment variable: the pi-coding-agent it maps into is the
6
+ // one the carrier itself resolves from where this package is installed.
7
+
8
+ import { registerHooks } from "node:module";
9
+ import {
10
+ CARRIER_SCHEME,
11
+ CODING_AGENT,
12
+ codingAgentDistFrom,
13
+ readCarrierDeclaration,
14
+ resolveCarrierSpecifier,
15
+ } from "./carrier-relocation.mjs";
16
+
17
+ const { files, targets } = readCarrierDeclaration(new URL("./overlay/upstream-pin.json", import.meta.url));
18
+ const carrierDirUrl = new URL("./carrier/", import.meta.url).href;
19
+ let dist;
20
+
21
+ registerHooks({
22
+ resolve(specifier, context, nextResolve) {
23
+ if (!specifier.startsWith(CARRIER_SCHEME)) return nextResolve(specifier, context);
24
+ const url = resolveCarrierSpecifier(specifier, context.parentURL, {
25
+ carrierDirUrl,
26
+ files,
27
+ targets,
28
+ // From a carrier file, as the carrier's own bare imports resolve — never the exports map.
29
+ dist: () => {
30
+ dist ??= codingAgentDistFrom(
31
+ nextResolve(CODING_AGENT, { ...context, parentURL: `${carrierDirUrl}${files[0]}` }).url,
32
+ );
33
+ return dist;
34
+ },
35
+ });
36
+ return { url, shortCircuit: true };
37
+ },
38
+ });
@@ -1,16 +1,108 @@
1
1
  {
2
- "schemaVersion": 1,
3
- "comment": "The one upstream commit this overlay is maintained against, and the patch it applies. The experimental durable app is source-only upstream, so its runtime is a pinned checkout plus this patch, provisioned by the operator; Entwurf ships neither. A checkout's TRACKED tree counts as this overlay only when HEAD equals commit, `git diff` equals the patch bytes exactly, and no non-ignored file is untracked. Git-ignored runtime inputs are outside that equality and are described separately below.",
2
+ "schemaVersion": 2,
3
+ "comment": "The one upstream commit the pi-durable carrier is emitted from, the patch it applies, the emitted files and the published SDK set they run against. The experimental durable app is source-only upstream (published pi-coding-agent ships no dist/experimental), so Entwurf ships it as a CARRIER: the pinned app files, type-stripped, with the relocation in pi/pi-durable/carrier-relocation.mjs. scripts/emit-pi-durable-carrier.ts is the only writer of the generated fields (blob, sha256, emitter.node) and the only reader of upstream source; an installed package never reads source. Model data rides the published @earendil-works/pi-ai.",
4
4
  "repository": "https://github.com/earendil-works/pi",
5
- "commit": "cd32f7725fdbddbaecdff5b1e68491563394e0ca",
5
+ "tag": "v1.1.0",
6
+ "commit": "abe508e1b89912adde45528136c3221eb69acdd7",
6
7
  "patches": ["runtime-contacts.patch"],
7
- "modelData": {
8
- "comment": "Git-ignored generated model data the runtime imports at open. Upstream hydrates it by calling vendor model-list endpoints; this overlay instead takes the bytes the v1.0.2 release built from this commit, from the published package below, with no network. aggregateSha256 is sha256 over one `<sha256 of file> <file name>\\n` line per *.json file (dotfiles excluded), names in C byte order. upstream `packages/ai/scripts/check-model-data.ts` validates these bytes against the manifest.",
9
- "path": "packages/ai/src/providers/data",
10
- "source": "@earendil-works/pi-ai@1.0.2 dist/providers/data",
11
- "integrity": "sha512-JP59xGlSAQ/HhQ6EHN5qmneBlWfDyu6FPPryAvAaSo8A38ubAsEpS9YisCM5Jbbns0srrDffLZonE0OXorHI5A==",
12
- "files": 42,
13
- "aggregateSha256": "e71a1965a81c29fbe83d6e90dc1f4595602588281374603dee9e3bcbb7f46fa4",
14
- "manifestSha256": "d373b99c0860f84281d786d493e8d8237c0a9fd98f142ef31c6db187c5089bc5"
8
+ "carrier": {
9
+ "comment": "files: the six app files bootstrap.mjs needs (main.ts is replaced by the bootstrap). distTargets: every pi-coding-agent internal the stripped files import, as paths under the published dist/; the emitter refuses a set that differs and the resolver answers no other.",
10
+ "emitter": {
11
+ "tool": "node:module.stripTypeScriptTypes",
12
+ "mode": "strip",
13
+ "node": "v24.18.1"
14
+ },
15
+ "license": {
16
+ "source": "LICENSE",
17
+ "blob": "b0a8e9b81083294360c69b4ec45d3d39a2b28197",
18
+ "sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
19
+ },
20
+ "files": [
21
+ {
22
+ "name": "runtime.js",
23
+ "source": "packages/coding-agent/src/experimental/durable/runtime.ts",
24
+ "patched": true,
25
+ "blob": "fcd6e5239e999ebf17dea0335fb3dc9cd7f7ce45",
26
+ "sha256": "ca0ee10dc904a57d46bb33b37fcc4efe48dfa08b2f0d10e79e7bc22136871f0a"
27
+ },
28
+ {
29
+ "name": "tui.js",
30
+ "source": "packages/coding-agent/src/experimental/durable/tui.ts",
31
+ "patched": false,
32
+ "blob": "397b276cbeb1c90e1fbc96c3cc59e6a25f92dad7",
33
+ "sha256": "bef1ea4bd6e8ab150273752a9b94552d8ce672b518ba3d1ed1a209b8405be1b3"
34
+ },
35
+ {
36
+ "name": "harness-setup.js",
37
+ "source": "packages/coding-agent/src/experimental/durable/harness-setup.ts",
38
+ "patched": false,
39
+ "blob": "4d6a4be5333ebb8e0f17eac1987f18ea35a91fc7",
40
+ "sha256": "712b630145dc6f5c655a85b01b1e20299c962542e63c5494d2202bb7bf414a15"
41
+ },
42
+ {
43
+ "name": "prompt.js",
44
+ "source": "packages/coding-agent/src/experimental/durable/prompt.ts",
45
+ "patched": false,
46
+ "blob": "8e2f5d5273fba58da8b314766956ed62951c3814",
47
+ "sha256": "ff1ff765fa313e5cb1c3d7537c901c23adb0562ee433bd050fd35eb3d892134c"
48
+ },
49
+ {
50
+ "name": "sessions.js",
51
+ "source": "packages/coding-agent/src/experimental/durable/sessions.ts",
52
+ "patched": false,
53
+ "blob": "34f9ceeeaeaf874f7478d56115da2e4f726c6a85",
54
+ "sha256": "1bd1a04ece66f5ab3ca22860c64dba9abc848334d11a27a9afe543f1365c7254"
55
+ },
56
+ {
57
+ "name": "subagent.js",
58
+ "source": "packages/coding-agent/src/experimental/durable/subagent.ts",
59
+ "patched": false,
60
+ "blob": "e767d54e0bf2908e0f4f41c6933b55c00562eb25",
61
+ "sha256": "8a75d934ef87efbb0bb2006a2141f1ea932560851f1718b991ed947d6efdb54c"
62
+ }
63
+ ],
64
+ "distTargets": [
65
+ "config.js",
66
+ "core/http-dispatcher.js",
67
+ "core/keybindings.js",
68
+ "core/model-resolver.js",
69
+ "core/model-runtime.js",
70
+ "core/resource-loader.js",
71
+ "core/settings-manager.js",
72
+ "core/skills.js",
73
+ "core/system-prompt.js",
74
+ "core/tools/bash.js",
75
+ "core/tools/edit.js",
76
+ "core/tools/read.js",
77
+ "core/tools/renderers/index.js",
78
+ "core/tools/write.js",
79
+ "modes/interactive/components/assistant-message.js",
80
+ "modes/interactive/components/custom-editor.js",
81
+ "modes/interactive/components/dynamic-border.js",
82
+ "modes/interactive/components/footer.js",
83
+ "modes/interactive/components/keybinding-hints.js",
84
+ "modes/interactive/components/status-indicator.js",
85
+ "modes/interactive/components/tool-execution.js",
86
+ "modes/interactive/components/user-message.js",
87
+ "modes/interactive/theme/theme-controller.js",
88
+ "modes/interactive/theme/theme.js"
89
+ ]
90
+ },
91
+ "sdk": {
92
+ "comment": "The published library set the carrier runs against, derived from the 1.1.0 declarations. members: the @earendil-works packages the carrier imports and every @earendil-works package those declare. direct: a third-party package a carrier file imports itself (sessions.ts: proper-lockfile, at the version pi-coding-agent 1.1.0 pins). Each is an exact production dependency in package.json; the members' own edges are caret ranges, so the installed physical edges, not semver, are what must equal these versions.",
93
+ "members": {
94
+ "@earendil-works/chord": "1.1.0",
95
+ "@earendil-works/pi-agent-core": "1.1.0",
96
+ "@earendil-works/pi-ai": "1.1.0",
97
+ "@earendil-works/pi-codemode": "1.1.0",
98
+ "@earendil-works/pi-coding-agent": "1.1.0",
99
+ "@earendil-works/pi-durable": "1.1.0",
100
+ "@earendil-works/pi-mcp": "1.1.0",
101
+ "@earendil-works/pi-telemetry": "1.1.0",
102
+ "@earendil-works/pi-tui": "1.1.0"
103
+ },
104
+ "direct": {
105
+ "proper-lockfile": "4.1.2"
106
+ }
15
107
  }
16
108
  }
@@ -27,7 +27,7 @@ import { randomBytes } from "node:crypto";
27
27
  /** The backends this rail can open. Fixed set, not a profile — a further one is a decision,
28
28
  * not a config entry. `copilot` was added by #82 RAIL 9, `omp` by #87 Bundle C, `codex`
29
29
  * by #95 after its system birth and app-server rails were measured, and `pi-durable` by #129
30
- * (the operator-provided durable app through the managed `entwurf pi-durable`). The set is joined to the
30
+ * (the durable app this package carries, through the managed `entwurf pi-durable`). The set is joined to the
31
31
  * citizen backends by `check-harness-admission-parity`: a harness that mints records but is
32
32
  * missing HERE is not an unwired convenience, it is a release blocker. */
33
33
  export const FRESH_CALL_BACKENDS = ["pi", "claude-code", "copilot", "omp", "codex", "pi-durable"] as const;
@@ -244,9 +244,9 @@ export const FRESH_CALL_RUNTIME: Record<FreshCallBackend, string> = {
244
244
  copilot: "entwurf",
245
245
  omp: "omp",
246
246
  codex: "codex",
247
- // #129: like copilot, a MANAGED verb and never the bare app. The durable app is source-only
248
- // and runs under the operator-provided runtime's own resolver; `entwurf pi-durable` is the one
249
- // fixed invocation that verifies that runtime against the pin and execs the packaged bootstrap.
247
+ // #129/#130: like copilot, a MANAGED verb and never the bare app. The durable app is this
248
+ // package's carrier and runs under the carrier's resolver; `entwurf pi-durable` is the one fixed
249
+ // invocation that verifies the carrier and its SDK set and execs the packaged bootstrap.
250
250
  "pi-durable": "entwurf",
251
251
  };
252
252
 
@@ -519,15 +519,13 @@ export function freshCall(
519
519
  }
520
520
  if (params.backend === "pi-durable") {
521
521
  // The framed first input is computed here only to MEASURE it against the bootstrap payload
522
- // cap; the same pure function composes it again below. The runtime is located and verified
523
- // with THIS process's env, before anything is placed. That is the bridge's env, not
524
- // necessarily the window's: a new-window pane inherits the tmux SERVER's environment
525
- // (measured, see SCRUBBED_INHERITED_ENV), and the window runs whatever `entwurf` PATH names.
526
- // The managed verb re-verifies with its own env before it execs anything, so the window never
527
- // runs an unverified runtime — but when HOME/XDG_DATA_HOME or the package closure differ, this
522
+ // cap; the same pure function composes it again below. The carrier is verified from THIS
523
+ // process's package closure, before anything is placed. The window runs whatever `entwurf`
524
+ // PATH names, and the managed verb re-verifies its own package before it execs anything, so
525
+ // the window never runs an unverified carrier — but when the two package closures differ, this
528
526
  // verdict and the window's can disagree, and the window is left showing its own refusal.
529
527
  const layout = piDurablePackageLayout();
530
- const runtime = layout === null ? null : checkPiDurableRuntime(env, layout);
528
+ const runtime = layout === null ? null : checkPiDurableRuntime(layout);
531
529
  const missing = piDurableFreshPreflight({
532
530
  model,
533
531
  firstInput: buildFreshCallPrompt({ backend: params.backend, task, callerGardenId, nonce }),
@@ -19,9 +19,9 @@
19
19
  * `{v:2,task}` payload, whose decoder caps `task` at 16000 characters. A framed
20
20
  * task over that cap would open a window whose bootstrap refuses its own argv.
21
21
  *
22
- * ── Then the runtime, then the four units ──
22
+ * ── Then the carrier, then the four units ──
23
23
  *
24
- * runtime operator-provided at the fixed locator and verified against the pin
24
+ * carrier the package's own pi-durable carrier and its declared SDK set, verified
25
25
  * (`pi-durable-runtime.ts`); passed in as that check's verdict.
26
26
  * birth the compiled contact in this package's closure — it mints the record.
27
27
  * MCP hand the compiled entwurf-bridge entry in the same closure — the contact spawns it.
@@ -32,7 +32,7 @@
32
32
  *
33
33
  * ── What this deliberately does NOT claim ──
34
34
  *
35
- * Filesystem truth about ONE package closure and ONE runtime directory. It does not prove the durable
35
+ * Filesystem truth about ONE package closure and the SDK it resolves. It does not prove the durable
36
36
  * app opens, the bridge connects, the doorbell arms or a garden id renders — that is runtime truth for
37
37
  * the clause 7 LIVE receipt. No spawn, no network, no mutation; every path arrives as an argument, so
38
38
  * this leaf imports nothing but types.