@junghanacs/entwurf 0.45.0 → 0.50.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 (105) hide show
  1. package/AGENTS.md +3 -2
  2. package/BASELINE.md +110 -1
  3. package/CHANGELOG.md +169 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/ENTWURF-CLI.json +1229 -0
  6. package/ENTWURF.json +63 -15
  7. package/FAQ.md +4 -4
  8. package/README.md +124 -188
  9. package/VERIFY.md +2 -2
  10. package/docs/external-mcp-host.md +3 -3
  11. package/docs/setup-clean-host.md +37 -12
  12. package/mcp/entwurf-bridge/dist/ENTWURF.json +63 -15
  13. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +19 -8
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +483 -18
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -10
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +613 -33
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-resume-call.js +497 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +3 -1
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +50 -5
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +7 -1
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-location.js +347 -0
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-owner-fence.js +1132 -0
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +1 -0
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +18 -11
  25. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +30 -15
  26. package/mcp/entwurf-bridge/dist/scripts/herdr-rails.mjs +40 -7
  27. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +1 -27
  28. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-prune.js +1 -1
  29. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
  30. package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +1 -1
  31. package/mcp/entwurf-bridge/src/index.ts +28 -8
  32. package/mcp/entwurf-bridge/tsconfig.build.json +7 -1
  33. package/native/win32-x64/manifest.json +1 -1
  34. package/package.json +6 -3
  35. package/pi/pi-durable/bootstrap.mjs +179 -6
  36. package/pi/pi-durable/carrier/runtime.js +108 -9
  37. package/pi/pi-durable/carrier/session-name.js +11 -0
  38. package/pi/pi-durable/carrier/sessions.js +96 -20
  39. package/pi/pi-durable/overlay/upstream-pin.json +11 -5
  40. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +710 -38
  41. package/pi-extensions/lib/fresh-call-dispatch.ts +64 -12
  42. package/pi-extensions/lib/herdr-fresh-call.ts +919 -40
  43. package/pi-extensions/lib/herdr-resume-call.ts +810 -0
  44. package/pi-extensions/lib/meta-session.ts +13 -8
  45. package/pi-extensions/lib/mux-resume-call.ts +77 -4
  46. package/pi-extensions/lib/pi-durable-fresh-preflight.ts +9 -2
  47. package/pi-extensions/lib/pi-durable-location.ts +446 -0
  48. package/pi-extensions/lib/pi-durable-owner-fence.ts +1461 -0
  49. package/pi-extensions/lib/pi-durable-runtime.ts +4 -0
  50. package/pi-extensions/lib/resume-launch-identity.ts +31 -12
  51. package/pi-extensions/meta-bridge-pi-durable.ts +29 -15
  52. package/run.sh +411 -657
  53. package/scripts/check-claude-launch.ts +341 -0
  54. package/scripts/check-entwurf-v2-surface.ts +2 -2
  55. package/scripts/check-fresh-call-dispatch.ts +70 -9
  56. package/scripts/check-fresh-cut-gate.sh +23 -9
  57. package/scripts/check-gate-qualification.ts +18 -13
  58. package/scripts/check-herdr-activation.ts +301 -23
  59. package/scripts/check-herdr-fresh-call.ts +161 -25
  60. package/scripts/check-herdr-plugin-build.ts +173 -1
  61. package/scripts/check-herdr-plugin.ts +28 -2
  62. package/scripts/check-herdr-runtime-bootstrap.ts +136 -0
  63. package/scripts/check-install-surface.ts +1236 -6
  64. package/scripts/check-meta-doctor-oracle.sh +1 -1
  65. package/scripts/check-meta-facts.ts +20 -1
  66. package/scripts/check-mux-resume-call.ts +28 -0
  67. package/scripts/check-peer-facts.ts +39 -2
  68. package/scripts/cli-help.mjs +222 -0
  69. package/scripts/dev-bin.sh +39 -2
  70. package/scripts/herdr-activation.mjs +28 -6
  71. package/scripts/herdr-operator-link.mjs +389 -0
  72. package/scripts/herdr-plugin-activate.mjs +5 -0
  73. package/scripts/herdr-plugin-deactivate.mjs +18 -5
  74. package/scripts/herdr-rails.mjs +43 -7
  75. package/scripts/herdr-runtime.mjs +27 -0
  76. package/scripts/meta-bridge-fresh-cut.ts +1 -27
  77. package/scripts/meta-bridge-prune.ts +1 -1
  78. package/scripts/meta-facts.ts +1 -1
  79. package/scripts/mutants/agent-guidance.json +4 -4
  80. package/scripts/mutants/claude-launch.json +142 -0
  81. package/scripts/mutants/cli-help-source.json +420 -0
  82. package/scripts/mutants/dev-gate-dispatch.json +109 -0
  83. package/scripts/mutants/fresh-call-dispatch.json +38 -2
  84. package/scripts/mutants/fresh-cut.json +15 -0
  85. package/scripts/mutants/herdr-activation.json +128 -0
  86. package/scripts/mutants/herdr-fresh-call.json +278 -5
  87. package/scripts/mutants/herdr-operator-link.json +318 -0
  88. package/scripts/mutants/herdr-plugin-build.json +55 -0
  89. package/scripts/mutants/herdr-plugin.json +17 -0
  90. package/scripts/mutants/herdr-resume-call.json +288 -0
  91. package/scripts/mutants/herdr-runtime-bootstrap.json +48 -0
  92. package/scripts/mutants/meta-facts.json +15 -0
  93. package/scripts/mutants/mux-resume-call.json +63 -2
  94. package/scripts/mutants/peer-facts.json +15 -0
  95. package/scripts/mutants/pi-durable.json +979 -1
  96. package/scripts/mutants/source-install.json +35 -0
  97. package/scripts/mutants/v2-surface.json +2 -2
  98. package/scripts/mutants/v2-visible-resume.json +255 -6
  99. package/scripts/peer-facts.ts +1 -1
  100. package/scripts/smoke-agy-install-state.sh +66 -0
  101. package/scripts/smoke-meta-prune.sh +23 -0
  102. package/scripts/smoke-omp-fresh-live.ts +12 -3
  103. package/scripts/smoke-omp-receive-live.ts +7 -1
  104. package/scripts/smoke-pi-durable-fresh-live.ts +12 -2
  105. package/team.json.example +39 -0
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * fresh-call-dispatch — the ONE place that decides which rail opens a sibling, and the only module
3
- * allowed to know that there are two.
3
+ * allowed to know that there are two. Since #157 G1a that rail choice is shared by the two visible
4
+ * lifecycle verbs, fresh call and same-id visible resume, through two separate entry points; each
5
+ * verb keeps its own lifecycle policy, and only the rail choice is common.
4
6
  *
5
7
  * WHY A COMPOSITION ROOT AND NOT A BRANCH IN EACH SURFACE. Two surfaces once registered
6
8
  * `entwurf_fresh_call`: pi's own extension and the MCP bridge. Before this file each of them
@@ -21,8 +23,9 @@
21
23
  * somewhere the operator cannot see from where they are standing, and the receipt would look fine.
22
24
  *
23
25
  * WHAT THIS MODULE DOES NOT DO: it never reads the record store, never resolves a garden id, and
24
- * never turns the direct herdr witness into a second public address. The nonce callback remains
25
- * the one address path, on both rails.
26
+ * never turns the direct herdr witness into a second public address. For a FRESH call the nonce
27
+ * callback remains the one address path, on both rails. A visible resume has no nonce: its address
28
+ * is the record the v2 composition already resolved, and this module only picks where it reopens.
26
29
  */
27
30
 
28
31
  import { spawn as spawnChildProcess } from "node:child_process";
@@ -38,6 +41,7 @@ import {
38
41
  resolveHerdrContext,
39
42
  type SpawnFn,
40
43
  } from "./herdr-fresh-call.ts";
44
+ import { type HerdrResumeLaunchOutcome, launchHerdrResume } from "./herdr-resume-call.ts";
41
45
  import { type FreshCallResult, freshCall, renderFreshCall } from "./mux-fresh-call.ts";
42
46
  import { splitPiDurableModel } from "./pi-durable-fresh-preflight.ts";
43
47
 
@@ -61,8 +65,11 @@ export interface FreshCallRequest {
61
65
  * carrying that citizen's `nativeSessionId`. TMUX-RAIL INPUT ONLY: it exists to find the
62
66
  * caller's own pane by its terminal title (#95 lane B), and a herdr tab has no such pane. */
63
67
  readonly callerNativeSessionId?: string;
64
- /** The SAME codex citizen's record cwd, under the same condition (#95 lane C). Also tmux-only,
65
- * and consulted only when the call requested no cwd of its own. */
68
+ /** The SAME codex citizen's record cwd, under the same condition (#95 lane C), consulted only
69
+ * when the call requested no cwd of its own: by the tmux rail, and inside herdr by BOTH branches —
70
+ * the layout branch (pi-durable, #157 A-L1) and the agent-start branch (pi/claude-code, #157
71
+ * A-L1x) — which refuse rather than fall back to their own process cwd when a known Codex caller
72
+ * has none. */
66
73
  readonly callerCwd?: string;
67
74
  }
68
75
 
@@ -127,12 +134,13 @@ export function isUncuratedEntwurfPiModel(backend: string, model: string): boole
127
134
  * backend there, so running a Codex preflight would ask an irrelevant question and could fail for
128
135
  * a reason that has nothing to do with why the call is impossible.
129
136
  *
130
- * THE TWO CODEX CALLER INPUTS ARE TMUX-RAIL FACTS and stop at this boundary. #95 lane B/C measured
131
- * both against a tmux pane and an app-server-hosted bridge: a pane title is what carries the
132
- * caller's thread-id, and a record cwd exists as an input because THAT process's directory is the
133
- * app-server's. Inside herdr the tab is created by herdr, there is no pane title to anchor to, and
134
- * neither fact has been measured — so the herdr rail is handed the request without consulting
135
- * them rather than inheriting a rule from a topology it does not share.
137
+ * THE CODEX CALLER'S THREAD ID IS A TMUX-RAIL FACT and stops at this boundary. #95 lane B measured
138
+ * it against a tmux pane: a pane title is what carries the caller's thread-id, and inside herdr the
139
+ * tab is created by herdr, with no pane title to anchor to. The herdr rail therefore receives only a
140
+ * boolean saying the caller IS a record-backed Codex citizen, plus that citizen's record cwd (#95
141
+ * lane C: an app-server-hosted bridge's own directory is the app-server's). Both herdr branches read
142
+ * them (layout since #157 A-L1, agent start since A-L1x), to choose a cwd without falling back to
143
+ * this process's; this is not a Codex-in-herdr placement certification.
136
144
  */
137
145
  export async function dispatchFreshCall(
138
146
  request: FreshCallRequest,
@@ -161,7 +169,20 @@ export async function dispatchFreshCall(
161
169
  return { rail: "herdr", result: { ok: false, reason: context.reason } };
162
170
  }
163
171
  const run: HerdrRun = createHerdrRunner(context.context.bin, env, spawn ?? (spawnChildProcess as SpawnFn));
164
- return { rail: "herdr", result: await herdrFreshCall(request, run, env, nonce) };
172
+ // #157 A-L1: an explicit field list, not `...request`, so the Codex thread id STOPS here — it is
173
+ // a tmux seat input. The herdr rail learns only WHETHER the caller is a record-backed Codex
174
+ // citizen (one boolean) and that citizen's record cwd; the tmux request below is untouched.
175
+ const herdrRequest = {
176
+ backend: request.backend,
177
+ model: request.model,
178
+ task: request.task,
179
+ ...(request.cwd === undefined ? {} : { cwd: request.cwd }),
180
+ ...(request.placement === undefined ? {} : { placement: request.placement }),
181
+ callerGardenId: request.callerGardenId,
182
+ ...(request.callerCwd === undefined ? {} : { callerCwd: request.callerCwd }),
183
+ callerIsCodex: request.callerNativeSessionId !== undefined,
184
+ };
185
+ return { rail: "herdr", result: await herdrFreshCall(herdrRequest, run, env, nonce) };
165
186
  }
166
187
  // TWO capability axes, in this order, both pre-mutation and neither standing in for the
167
188
  // other. The TARGET axis first — "entwurf cannot open a Codex sibling here at all" is the
@@ -205,3 +226,34 @@ export async function dispatchFreshCall(
205
226
  export function renderDispatchedFreshCall(dispatched: DispatchedFreshCall): { text: string; isError: boolean } {
206
227
  return dispatched.rail === "herdr" ? renderHerdrFreshCall(dispatched.result) : renderFreshCall(dispatched.result);
207
228
  }
229
+
230
+ /** The record-derived launch input the v2 visible-resume seam hands its launcher. */
231
+ export interface ResumeLaunchInput {
232
+ readonly backend: string;
233
+ readonly cwd: string;
234
+ readonly runtimeArgs: readonly string[];
235
+ }
236
+
237
+ /**
238
+ * #157 G1a: pick the rail a same-id visible resume reopens on — the SAME capability fact fresh
239
+ * call reads, through the same `selectFreshCallRail`, never a parameter and never a fallback either
240
+ * way. This entry point owns that choice and nothing else:
241
+ *
242
+ * the v2 composition record, dormancy proof, lock, identity, observation — it calls the surface's
243
+ * launcher and never learns that there are rails
244
+ * the surface supplies the fixed tmux launch mapping as `tmuxLaunch`; it reads no rail fact
245
+ * herdr-resume-call owns the herdr behaviour, including refusing a non-pi target before anything
246
+ * is created
247
+ *
248
+ * Inside herdr the tmux mapping is never called, so a refused or failed herdr launch stays a herdr
249
+ * answer. Outside herdr the tmux mapping keeps its own herdr guard for any direct caller.
250
+ */
251
+ export function dispatchResumeLaunch<I extends ResumeLaunchInput, T>(
252
+ input: I,
253
+ tmuxLaunch: (input: I) => T,
254
+ env: NodeJS.ProcessEnv = process.env,
255
+ spawn?: SpawnFn,
256
+ ): T | Promise<HerdrResumeLaunchOutcome> {
257
+ if (selectFreshCallRail(env) !== "herdr") return tmuxLaunch(input);
258
+ return launchHerdrResume(input, env, spawn ?? (spawnChildProcess as SpawnFn));
259
+ }