@wrenai/genbi 0.0.4 → 0.0.5

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 (230) hide show
  1. package/README.md +64 -2
  2. package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
  3. package/dist/assets/index-A_Wrq7Ey.css +1 -0
  4. package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
  5. package/dist/index.html +2 -2
  6. package/dist-server/harness/bundle/schema.d.ts +38 -0
  7. package/dist-server/harness/bundle/schema.js +18 -10
  8. package/dist-server/harness/bundle/version.js +1 -1
  9. package/dist-server/harness/cli-args.d.ts +10 -2
  10. package/dist-server/harness/cli-args.js +24 -8
  11. package/dist-server/harness/cli.js +75 -9
  12. package/dist-server/harness/compile/cache.js +4 -4
  13. package/dist-server/harness/compile/compose-profile.d.ts +51 -14
  14. package/dist-server/harness/compile/compose-profile.js +85 -19
  15. package/dist-server/harness/compile/context-loader.d.ts +56 -0
  16. package/dist-server/harness/compile/context-loader.js +237 -0
  17. package/dist-server/harness/compile/errors.d.ts +19 -0
  18. package/dist-server/harness/compile/errors.js +35 -0
  19. package/dist-server/harness/compile/index.d.ts +4 -3
  20. package/dist-server/harness/compile/index.js +4 -3
  21. package/dist-server/harness/compile/pipeline.d.ts +15 -7
  22. package/dist-server/harness/compile/pipeline.js +62 -14
  23. package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
  24. package/dist-server/harness/compile/resolve-binary.js +3 -3
  25. package/dist-server/harness/compile/types.d.ts +29 -0
  26. package/dist-server/harness/compile/warble-identity.d.ts +11 -0
  27. package/dist-server/harness/compile/warble-identity.js +13 -0
  28. package/dist-server/harness/components/ai-step.d.ts +4 -0
  29. package/dist-server/harness/components/ai-step.js +40 -0
  30. package/dist-server/harness/components/broker.d.ts +60 -0
  31. package/dist-server/harness/components/broker.js +117 -0
  32. package/dist-server/harness/components/capability-card.d.ts +98 -0
  33. package/dist-server/harness/components/capability-card.js +169 -0
  34. package/dist-server/harness/components/context.d.ts +2 -0
  35. package/dist-server/harness/components/context.js +30 -0
  36. package/dist-server/harness/components/display.d.ts +6 -0
  37. package/dist-server/harness/components/display.js +29 -0
  38. package/dist-server/harness/components/egress-judge.d.ts +12 -0
  39. package/dist-server/harness/components/egress-judge.js +27 -0
  40. package/dist-server/harness/components/egress.d.ts +142 -0
  41. package/dist-server/harness/components/egress.js +469 -0
  42. package/dist-server/harness/components/in-process.d.ts +7 -0
  43. package/dist-server/harness/components/in-process.js +245 -0
  44. package/dist-server/harness/components/normalize.d.ts +35 -0
  45. package/dist-server/harness/components/normalize.js +245 -0
  46. package/dist-server/harness/components/plan.d.ts +31 -0
  47. package/dist-server/harness/components/plan.js +184 -0
  48. package/dist-server/harness/components/report.d.ts +207 -0
  49. package/dist-server/harness/components/report.js +309 -0
  50. package/dist-server/harness/components/runner.d.ts +193 -0
  51. package/dist-server/harness/components/runner.js +492 -0
  52. package/dist-server/harness/components/semantic-guards.d.ts +20 -0
  53. package/dist-server/harness/components/semantic-guards.js +42 -0
  54. package/dist-server/harness/components/terminal-json.d.ts +18 -0
  55. package/dist-server/harness/components/terminal-json.js +82 -0
  56. package/dist-server/harness/components/usage.d.ts +14 -0
  57. package/dist-server/harness/components/usage.js +19 -0
  58. package/dist-server/harness/components/wren-access.d.ts +29 -0
  59. package/dist-server/harness/components/wren-access.js +233 -0
  60. package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
  61. package/dist-server/harness/components/zone-dry-run.js +107 -0
  62. package/dist-server/harness/components/zone-gate.d.ts +76 -0
  63. package/dist-server/harness/components/zone-gate.js +144 -0
  64. package/dist-server/harness/events/index.d.ts +1 -1
  65. package/dist-server/harness/events/types.d.ts +52 -0
  66. package/dist-server/harness/index.d.ts +9 -2
  67. package/dist-server/harness/index.js +5 -1
  68. package/dist-server/harness/loop/executor.d.ts +1 -0
  69. package/dist-server/harness/loop/executor.js +37 -15
  70. package/dist-server/harness/npm-package-resolve.d.ts +2 -0
  71. package/dist-server/harness/npm-package-resolve.js +16 -4
  72. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
  73. package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
  74. package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
  75. package/dist-server/harness/providers/adapters/openai.js +6 -0
  76. package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
  77. package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
  78. package/dist-server/harness/providers/binding.d.ts +45 -4
  79. package/dist-server/harness/providers/binding.js +44 -8
  80. package/dist-server/harness/providers/index.d.ts +9 -3
  81. package/dist-server/harness/providers/index.js +10 -3
  82. package/dist-server/harness/providers/zone.d.ts +96 -0
  83. package/dist-server/harness/providers/zone.js +112 -0
  84. package/dist-server/harness/render/answering-query.d.ts +29 -0
  85. package/dist-server/harness/render/answering-query.js +62 -0
  86. package/dist-server/harness/render/envelope.d.ts +6 -0
  87. package/dist-server/harness/render/envelope.js +10 -3
  88. package/dist-server/harness/route/adapter-spec.d.ts +8 -9
  89. package/dist-server/harness/route/adapter-spec.js +13 -14
  90. package/dist-server/harness/route/codex-ask-events.js +2 -1
  91. package/dist-server/harness/route/codex-ask.d.ts +4 -0
  92. package/dist-server/harness/route/codex-ask.js +69 -30
  93. package/dist-server/harness/route/codex-host-query.d.ts +52 -0
  94. package/dist-server/harness/route/codex-host-query.js +191 -0
  95. package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
  96. package/dist-server/harness/route/codex-local-manifest.js +96 -15
  97. package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
  98. package/dist-server/harness/route/codex-wren-proxy.js +227 -0
  99. package/dist-server/harness/route/describe.js +45 -6
  100. package/dist-server/harness/route/in-process.d.ts +22 -1
  101. package/dist-server/harness/route/in-process.js +54 -4
  102. package/dist-server/harness/route/route.js +5 -2
  103. package/dist-server/harness/route/types.d.ts +30 -1
  104. package/dist-server/harness/session/run.js +30 -10
  105. package/dist-server/harness/setup/runner.d.ts +2 -0
  106. package/dist-server/harness/setup/runner.js +38 -17
  107. package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
  108. package/dist-server/harness/setup/yaml-scalar.js +62 -0
  109. package/dist-server/server/adopt.js +1 -17
  110. package/dist-server/server/app.d.ts +1 -0
  111. package/dist-server/server/app.js +163 -14
  112. package/dist-server/server/auth-choice.d.ts +13 -1
  113. package/dist-server/server/auth-choice.js +66 -1
  114. package/dist-server/server/bin.js +177 -25
  115. package/dist-server/server/codex-local-grounding.d.ts +18 -0
  116. package/dist-server/server/codex-local-grounding.js +92 -0
  117. package/dist-server/server/codex-native-session.d.ts +25 -0
  118. package/dist-server/server/codex-native-session.js +1 -0
  119. package/dist-server/server/codex-runtime-composition.d.ts +34 -0
  120. package/dist-server/server/codex-runtime-composition.js +250 -0
  121. package/dist-server/server/codex-structured-ask.d.ts +23 -0
  122. package/dist-server/server/codex-structured-ask.js +102 -0
  123. package/dist-server/server/conn-config.js +4 -16
  124. package/dist-server/server/conversation-socket.d.ts +16 -0
  125. package/dist-server/server/conversation-socket.js +65 -0
  126. package/dist-server/server/db.d.ts +46 -0
  127. package/dist-server/server/db.js +88 -9
  128. package/dist-server/server/env-detect.d.ts +6 -0
  129. package/dist-server/server/env-detect.js +7 -0
  130. package/dist-server/server/fold.d.ts +22 -4
  131. package/dist-server/server/fold.js +37 -5
  132. package/dist-server/server/harness.d.ts +2 -2
  133. package/dist-server/server/harness.js +33 -7
  134. package/dist-server/server/interactive-terminal.d.ts +10 -6
  135. package/dist-server/server/interactive-terminal.js +33 -8
  136. package/dist-server/server/managed-wren-runtime.d.ts +116 -0
  137. package/dist-server/server/managed-wren-runtime.js +670 -0
  138. package/dist-server/server/native-artifacts.d.ts +23 -7
  139. package/dist-server/server/native-artifacts.js +43 -12
  140. package/dist-server/server/native-component-context.d.ts +19 -0
  141. package/dist-server/server/native-component-context.js +76 -0
  142. package/dist-server/server/native-component-preparation.d.ts +20 -0
  143. package/dist-server/server/native-component-preparation.js +129 -0
  144. package/dist-server/server/native-component-runtime.d.ts +36 -0
  145. package/dist-server/server/native-component-runtime.js +92 -0
  146. package/dist-server/server/native-components.d.ts +84 -0
  147. package/dist-server/server/native-components.js +160 -0
  148. package/dist-server/server/native-dispatch-process.d.ts +8 -0
  149. package/dist-server/server/native-dispatch-process.js +65 -0
  150. package/dist-server/server/native-runtime-spec.d.ts +89 -0
  151. package/dist-server/server/native-runtime-spec.js +169 -0
  152. package/dist-server/server/native-sessions.d.ts +172 -19
  153. package/dist-server/server/native-sessions.js +839 -244
  154. package/dist-server/server/native-shutdown.d.ts +6 -0
  155. package/dist-server/server/native-shutdown.js +28 -0
  156. package/dist-server/server/native-wren-home.d.ts +18 -0
  157. package/dist-server/server/native-wren-home.js +262 -0
  158. package/dist-server/server/node-pty-host.d.ts +2 -1
  159. package/dist-server/server/node-pty-host.js +3 -2
  160. package/dist-server/server/profile-registry.d.ts +139 -0
  161. package/dist-server/server/profile-registry.js +620 -0
  162. package/dist-server/server/runtime-binding.d.ts +4 -1
  163. package/dist-server/server/runtime-binding.js +50 -2
  164. package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
  165. package/dist-server/server/runtime-host/claude-component-step.js +186 -0
  166. package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
  167. package/dist-server/server/runtime-host/codex-app-server.js +239 -0
  168. package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
  169. package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
  170. package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
  171. package/dist-server/server/runtime-host/codex-component-step.js +308 -0
  172. package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
  173. package/dist-server/server/runtime-host/codex-conversation.js +295 -0
  174. package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
  175. package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
  176. package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
  177. package/dist-server/server/runtime-host/codex-events.js +66 -0
  178. package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
  179. package/dist-server/server/runtime-host/codex-policy.js +151 -0
  180. package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
  181. package/dist-server/server/runtime-host/codex-process.js +96 -0
  182. package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
  183. package/dist-server/server/runtime-host/codex-rpc.js +182 -0
  184. package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
  185. package/dist-server/server/runtime-host/codex-session.js +492 -0
  186. package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
  187. package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
  188. package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
  189. package/dist-server/server/runtime-host/codex-terminal.js +120 -0
  190. package/dist-server/server/runtime-host/index.d.ts +5 -0
  191. package/dist-server/server/runtime-host/index.js +3 -0
  192. package/dist-server/server/runtime-host/local.d.ts +29 -0
  193. package/dist-server/server/runtime-host/local.js +90 -0
  194. package/dist-server/server/runtime-host/policy.d.ts +8 -0
  195. package/dist-server/server/runtime-host/policy.js +78 -0
  196. package/dist-server/server/runtime-host/types.d.ts +52 -0
  197. package/dist-server/server/runtime-host/types.js +62 -0
  198. package/dist-server/server/structured-query-evidence.d.ts +7 -0
  199. package/dist-server/server/structured-query-evidence.js +47 -0
  200. package/dist-server/server/structured-runtime.d.ts +52 -0
  201. package/dist-server/server/structured-runtime.js +130 -0
  202. package/dist-server/server/structured-scope.d.ts +4 -0
  203. package/dist-server/server/structured-scope.js +12 -0
  204. package/dist-server/server/turn.d.ts +6 -1
  205. package/dist-server/server/turn.js +82 -25
  206. package/dist-server/server/wire-types.d.ts +67 -3
  207. package/dist-server/server/wren-profiles.js +1 -1
  208. package/managed-wren/README.md +60 -0
  209. package/managed-wren/dependency-wheels.json +282 -0
  210. package/managed-wren/manifest.json +373 -0
  211. package/managed-wren/release-inputs.json +45 -0
  212. package/managed-wren/runtime-tree.cjs +115 -0
  213. package/managed-wren/source-inputs.json +40 -0
  214. package/package.json +15 -6
  215. package/profiles/genbi-default/context/binding.yml +17 -7
  216. package/profiles/genbi-default/context/context.json +146 -0
  217. package/profiles/genbi-default/ir.golden.json +39 -43
  218. package/profiles/genbi-default/profile.yml +31 -0
  219. package/profiles/genbi-enrich-context/context/binding.yml +14 -3
  220. package/profiles/genbi-enrich-context/context/context.json +146 -0
  221. package/profiles/genbi-enrich-context/ir.golden.json +4 -5
  222. package/profiles/genbi-monitor/context/binding.yml +13 -5
  223. package/profiles/genbi-monitor/context/context.json +146 -0
  224. package/profiles/genbi-monitor/ir.golden.json +13 -7
  225. package/profiles/genbi-report/context/binding.yml +19 -0
  226. package/profiles/genbi-report/context/context.json +223 -0
  227. package/profiles/genbi-report/ir.golden.json +328 -0
  228. package/profiles/genbi-report/profile.yml +38 -0
  229. package/profiles/genbi-setup/ir.golden.json +3 -1
  230. package/dist/assets/index-Xs4q7pNo.css +0 -1
@@ -1,13 +1,16 @@
1
+ import { GATEWAY_API_KEY_ENV } from "./env-detect.js";
1
2
  /**
2
3
  * Maps the wizard's persisted `RuntimeSettings` to the harness-level `AuthChoice` that should
3
4
  * actually be dispatched with. `RuntimeSettings.authMode` is the wire/persisted vocabulary
4
- * (`"subscription" | "byo" | "local"`); `AuthChoice.mode` is the harness vocabulary
5
+ * (`"subscription" | "byo" | "local" | "gateway"`); `AuthChoice.mode` is the harness vocabulary
5
6
  * (`"subscription" | "api-key" | "local" | "gateway"`) — `"byo"` maps to `"api-key"`.
6
7
  *
7
8
  * For the `openai-compatible` adapter, the BFF reads `OPENAI_API_KEY` from its own process env
8
9
  * and injects it explicitly as `config.apiKey` — that adapter has no env self-read of its own.
9
10
  * For `anthropic`, `config.apiKey` is deliberately omitted so the adapter self-reads
10
11
  * `ANTHROPIC_API_KEY` from the process env itself (see `harness/providers/adapters/anthropic.ts`).
12
+ * For `gateway` (the `pi-ai` adapter), the BFF reads `GENBI_GATEWAY_API_KEY` and injects it as
13
+ * `config.apiKey` the same way — pi-ai never reads env vars or credential files on its own.
11
14
  * The key value is never read into `RuntimeSettings`, SQLite, or any log — only referenced here,
12
15
  * transiently, to build the in-memory `AuthChoice` passed straight to the adapter factory.
13
16
  */
@@ -17,6 +20,19 @@ export function toAuthChoiceFromRuntimeSettings(settings) {
17
20
  return { mode: "subscription", provider: settings.subscriptionProvider ?? "claude" };
18
21
  case "local":
19
22
  return { mode: "local" };
23
+ case "gateway": {
24
+ const config = {};
25
+ if (settings.gatewayProvider !== undefined)
26
+ config["provider"] = settings.gatewayProvider;
27
+ if (settings.gatewayModel !== undefined)
28
+ config["model"] = settings.gatewayModel;
29
+ if (settings.gatewayBaseURL?.trim())
30
+ config["baseUrl"] = settings.gatewayBaseURL;
31
+ const apiKey = process.env[GATEWAY_API_KEY_ENV];
32
+ if (apiKey?.trim())
33
+ config["apiKey"] = apiKey;
34
+ return { mode: "gateway", config };
35
+ }
20
36
  case "byo": {
21
37
  const adapter = settings.apiKeyAdapter ?? "anthropic";
22
38
  if (adapter === "openai-compatible") {
@@ -34,3 +50,52 @@ export function toAuthChoiceFromRuntimeSettings(settings) {
34
50
  }
35
51
  }
36
52
  }
53
+ const RUNTIME_AUTH_MODES = new Set(["subscription", "byo", "local", "gateway"]);
54
+ /** True for the wire `authMode` values `toAuthChoiceFromRuntimeSettings` can map. */
55
+ export function isRuntimeAuthMode(value) {
56
+ return typeof value === "string" && RUNTIME_AUTH_MODES.has(value);
57
+ }
58
+ function stringField(config, key) {
59
+ const value = config?.[key];
60
+ return typeof value === "string" && value.trim().length > 0 ? value : undefined;
61
+ }
62
+ /**
63
+ * The inverse of `toAuthChoiceFromRuntimeSettings`, for reporting the auth the BFF booted with
64
+ * (`WREN_HARNESS_MODE` / `MODEL` / `ENDPOINT` / flags) before the user has saved a runtime.
65
+ * Overlays only the auth-selection fields onto `base`; tier rows, deployment and hybrid stay the
66
+ * store's. Credentials never appear in the result. A boot adapter the wizard cannot represent
67
+ * leaves `base` untouched.
68
+ */
69
+ export function runtimeSettingsFromAuthChoice(choice, base) {
70
+ switch (choice.mode) {
71
+ case "subscription":
72
+ return { ...base, authMode: "subscription", subscriptionProvider: choice.provider };
73
+ case "local":
74
+ return { ...base, authMode: "local" };
75
+ case "gateway": {
76
+ const provider = stringField(choice.config, "provider");
77
+ const model = stringField(choice.config, "model");
78
+ const baseURL = stringField(choice.config, "baseUrl");
79
+ return {
80
+ ...base,
81
+ authMode: "gateway",
82
+ ...(provider !== undefined ? { gatewayProvider: provider } : {}),
83
+ ...(model !== undefined ? { gatewayModel: model } : {}),
84
+ ...(baseURL !== undefined ? { gatewayBaseURL: baseURL } : {}),
85
+ };
86
+ }
87
+ case "api-key": {
88
+ if (choice.adapter !== "anthropic" && choice.adapter !== "openai-compatible")
89
+ return base;
90
+ const model = stringField(choice.config, "model");
91
+ const baseURL = choice.adapter === "openai-compatible" ? stringField(choice.config, "baseURL") : undefined;
92
+ return {
93
+ ...base,
94
+ authMode: "byo",
95
+ apiKeyAdapter: choice.adapter,
96
+ ...(model !== undefined ? { apiKeyModel: model } : {}),
97
+ ...(baseURL !== undefined ? { apiKeyBaseURL: baseURL } : {}),
98
+ };
99
+ }
100
+ }
101
+ }
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import { createCodexRuntimeComposition, readCodexBootConfiguration } from "./codex-runtime-composition.js";
3
+ import { createCodexStructuredAdapter } from "./codex-structured-ask.js";
4
+ import { StructuredRuntime } from "./structured-runtime.js";
5
+ import { shutdownNativeResources } from "./native-shutdown.js";
2
6
  /**
3
7
  * The BFF's process entrypoint. This is the ONLY file
4
8
  * in `server/` that wires real production values (env vars, a real `Store`
@@ -30,15 +34,21 @@
30
34
  * clear 500 at dispatch time instead.
31
35
  * WREN_HARNESS_PROFILE profileSource; default resolveDefaultProfileSource()
32
36
  * WREN_HARNESS_MODE subscription|api-key|local|gateway (auth mode)
33
- * WREN_HARNESS_PROVIDER subscription provider: claude|codex
37
+ * WREN_HARNESS_PROVIDER subscription provider: claude|codex; gateway mode: pi-ai provider id
34
38
  * WREN_HARNESS_ADAPTER api-key adapter name
35
39
  * WREN_HARNESS_API_KEY api-key credential
36
40
  * WREN_HARNESS_MODEL model override
37
- * WREN_HARNESS_ENDPOINT local/gateway endpoint URL
41
+ * WREN_HARNESS_ENDPOINT local endpoint URL; gateway mode: optional custom base URL
42
+ * GENBI_GATEWAY_API_KEY gateway-mode credential for the selected pi-ai provider. Read by
43
+ * the BFF and passed per request; never persisted, and pi-ai's own
44
+ * provider env vars / credential files are never consulted
38
45
  * WREN_HARNESS_WARBLE_BIN path to the warble CLI binary
39
- * WREN_HARNESS_WREN_SHIM optional absolute server-owned Wren shim for
40
- * native Codex runtime permissions; defaults to
41
- * the fixed local installation when unset
46
+ * WREN_HARNESS_CONTEXT_LOADER_BIN
47
+ * path to the wren-context-loader binary, which renders the bound
48
+ * wren project into the prepared-context document a `kind: prepared`
49
+ * binding reads. Falls back to this repo's own Rust build; resolution
50
+ * loud-fails rather than degrading to Warble's built-in MDL adapter
51
+ * (see harness/compile/context-loader.ts)
42
52
  * WREN_HARNESS_AGENT_SDK_BIN path to the agent-sdk CLI binary (dispatched)
43
53
  * WREN_HARNESS_CODEX_LOCAL_BIN path to the warble-codex-local dispatcher CLI
44
54
  * WREN_HARNESS_CODEX_BIN optional path to the Codex CLI used by that dispatcher
@@ -95,7 +105,8 @@
95
105
  */
96
106
  import { serve } from "@hono/node-server";
97
107
  import { createRequire } from "node:module";
98
- import { existsSync } from "node:fs";
108
+ import { existsSync, realpathSync } from "node:fs";
109
+ import os from "node:os";
99
110
  import path from "node:path";
100
111
  import { fileURLToPath } from "node:url";
101
112
  import { WebSocketServer } from "ws";
@@ -104,6 +115,8 @@ import { buildTierBindingFromFlags, parseChatTimeoutMs, parseSetupMaxTurns, reso
104
115
  import { createApp } from "./app.js";
105
116
  import { recoverBootstrapProjectBinding } from "./bootstrap-project-binding.js";
106
117
  import { createHarnessProfileSources } from "./harness-profile-sources.js";
118
+ import { ProfileRegistry } from "./profile-registry.js";
119
+ import { getWarbleIdentity } from "../harness/compile/warble-identity.js";
107
120
  import { compileProfile, compileRawProfile } from "../harness/compile/index.js";
108
121
  import { resolveWarbleBinary } from "../harness/compile/resolve-binary.js";
109
122
  import { toAuthChoiceFromRuntimeSettings } from "./auth-choice.js";
@@ -114,6 +127,9 @@ import { dispatchInteractiveArtifacts, getInteractiveTerminalReadiness, Interact
114
127
  import { createProbedPtyFactory, ensureDarwinNodePtySpawnHelper } from "./node-pty-host.js";
115
128
  import { NativeSessionService } from "./native-sessions.js";
116
129
  import { NativeArtifactService } from "./native-artifacts.js";
130
+ import { RuntimeHost } from "./runtime-host/local.js";
131
+ import { resolveManagedWrenRuntime } from "./managed-wren-runtime.js";
132
+ import { assertNativeExecutableIdentity, assertNativeRuntimeSpec, attestNativeExecutable, buildNativeChildEnvironment, buildNativeRuntimeSpec, resolveNativeExecutable } from "./native-runtime-spec.js";
117
133
  import { initializeNativeSessionStateBase, legacyInteractiveWorkspace, validateLegacyInteractiveWorkspace } from "./native-session-workspace.js";
118
134
  import { codexModelsForRuntime, compileUnboundProfileTierNames, effectiveTierModel, materializeRuntimeRouteOptions, } from "./runtime-binding.js";
119
135
  import { invalidateBundleAgentIdsCache } from "./turn.js";
@@ -187,6 +203,26 @@ async function main() {
187
203
  return undefined;
188
204
  });
189
205
  const warbleBinOption = resolvedWarbleBin !== undefined ? { warbleBin: resolvedWarbleBin } : {};
206
+ const nativeHome = realpathSync(os.homedir());
207
+ const nodeExecutable = attestNativeExecutable("node", process.execPath);
208
+ const producerExecutable = resolvedWarbleBin ? attestNativeExecutable("producer", resolvedWarbleBin) : undefined;
209
+ const bootPath = process.env["PATH"];
210
+ const claudeExecutable = resolveNativeExecutable("vendor", "claude", bootPath);
211
+ const codexExecutable = resolveNativeExecutable("vendor", "codex", bootPath);
212
+ const vendorExecutables = Object.freeze({
213
+ ...(claudeExecutable ? { claude: claudeExecutable } : {}),
214
+ ...(codexExecutable ? { codex: codexExecutable } : {}),
215
+ });
216
+ const nativeWrenExecutable = resolveNativeExecutable("wren", "wren", bootPath);
217
+ const nativeToolDirectories = Object.freeze([...new Set([
218
+ path.dirname(nodeExecutable.executable),
219
+ // Native auth and CLI tools need fixed OS utilities, even with a sanitized PATH.
220
+ ...(["darwin", "linux"].includes(process.platform) ? ["/usr/bin", "/bin", "/usr/sbin", "/sbin"].map((directory) => realpathSync(directory)) : []),
221
+ ...(nativeWrenExecutable ? [path.dirname(nativeWrenExecutable.executable)] : []),
222
+ ...Object.values(vendorExecutables).filter((value) => value !== undefined).map((value) => path.dirname(value.executable)),
223
+ ...(producerExecutable ? [path.dirname(producerExecutable.executable)] : []),
224
+ ])]);
225
+ const nativeHostEnvironment = buildNativeChildEnvironment({ toolDirectories: nativeToolDirectories, home: nativeHome });
190
226
  const profileSource = flags.profile ?? resolveDefaultProfileSource();
191
227
  // Frozen at boot: GET /api/harness accepts only a purpose and must never
192
228
  // inherit a caller-supplied RouteOptions.profileSource.
@@ -203,6 +239,19 @@ async function main() {
203
239
  // Initialization creates the fixed private namespaces once at startup.
204
240
  const nativeMaterializationState = initializeNativeSessionStateBase(dbPath);
205
241
  const store = new Store(dbPath);
242
+ // Conversation profiles a session may start inside: the shipped ones under this package's
243
+ // `profiles/` plus any the operator adds under the workspace. Registration compiles each one, so
244
+ // it runs in the background; the first `/api/profiles` request awaits the same promise.
245
+ const profileRegistry = new ProfileRegistry({
246
+ store,
247
+ builtinProfilesDir: path.dirname(harnessProfileSources.analysis),
248
+ userProfilesDir: path.join(workspaceRoot, "profiles"),
249
+ compileRaw: (source) => compileRawProfile({ profileSource: source, mode: "native", ...warbleBinOption }),
250
+ warbleIdentity: async () => (resolvedWarbleBin !== undefined ? getWarbleIdentity(resolvedWarbleBin) : "warble:unresolved"),
251
+ });
252
+ void profileRegistry.ensureReady().catch((error) => {
253
+ process.stderr.write(`warning: profile registry failed to initialize: ${error instanceof Error ? error.message : String(error)}\n`);
254
+ });
206
255
  const getCodexModels = () => codexModelsForRuntime(store.getRuntimeSettings());
207
256
  const getRuntimeTierNames = () => compileUnboundProfileTierNames({
208
257
  profileSource,
@@ -260,9 +309,11 @@ async function main() {
260
309
  boundProject = identity.path;
261
310
  const { revokedNativeSessionIds } = store.activateEnrichmentBindingAndRevokeBoundNativeSessions(identity);
262
311
  nativeSessions.revokeBindingCapabilities(revokedNativeSessionIds);
312
+ deps?.structuredRuntime?.revoke();
263
313
  invalidateBundleAgentIdsCache(deps);
264
314
  }
265
315
  function unbindProject() {
316
+ deps?.structuredRuntime?.revoke();
266
317
  boundProject = undefined;
267
318
  invalidateBundleAgentIdsCache(deps);
268
319
  }
@@ -291,6 +342,10 @@ async function main() {
291
342
  // a reset) later in the same boot. The actual logic lives in `./wren-home.js` so it's directly
292
343
  // unit-testable without going through a mocked `TurnDeps`.
293
344
  const originalWrenHome = process.env["WREN_HOME"];
345
+ const operatorWrenHome = originalWrenHome?.trim() || path.join(nativeHome, ".wren");
346
+ const nativeSourceWrenHome = () => store.getSetupMode() === "create"
347
+ ? path.join(workspaceRoot, ".wren")
348
+ : operatorWrenHome;
294
349
  const setWrenHomeForSetupMode = createSetWrenHomeForSetupMode(workspaceRoot, originalWrenHome);
295
350
  // Mutable auth-choice binding — the auth-choice mirror of boundProject above. Starts at the
296
351
  // boot-resolved `authChoice` and can be rebound later by PUT /api/config/runtime
@@ -453,7 +508,12 @@ async function main() {
453
508
  try {
454
509
  const require = createRequire(import.meta.url);
455
510
  ensureDarwinNodePtySpawnHelper(require.resolve("node-pty"));
456
- return await createProbedPtyFactory(await import("node-pty"));
511
+ assertNativeExecutableIdentity(nodeExecutable);
512
+ return await createProbedPtyFactory(await import("node-pty"), {
513
+ cwd: nativeMaterializationState.root,
514
+ env: nativeHostEnvironment,
515
+ probeExecutable: nodeExecutable.executable,
516
+ });
457
517
  }
458
518
  catch {
459
519
  throw new InteractiveLaunchError("interactive terminal host cannot spawn local processes on this machine");
@@ -470,14 +530,24 @@ async function main() {
470
530
  async function prepareInteractiveTerminal(target, binding) {
471
531
  if (enrichIrPath === undefined)
472
532
  throw new InteractiveLaunchError("interactive enrichment artifacts are not configured");
533
+ if (producerExecutable === undefined)
534
+ throw new InteractiveLaunchError("interactive enrichment materialization failed");
473
535
  const artifactRoot = legacyInteractiveWorkspace(nativeMaterializationState, binding.path, target);
536
+ const dispatchEnvironment = buildNativeChildEnvironment({
537
+ toolDirectories: nativeToolDirectories,
538
+ home: nativeHome,
539
+ projectPath: binding.path,
540
+ });
474
541
  return prepareInteractiveHandoff({
475
542
  target,
476
543
  binding,
477
544
  artifactRoot,
478
545
  materializationState: nativeMaterializationState,
479
- materialize: () => dispatchInteractiveArtifacts({ warbleBin: resolvedWarbleBin ?? "warble", irPath: enrichIrPath, target, cwd: artifactRoot, boundProject: binding.path, materializationState: nativeMaterializationState }),
480
- }, { getCurrentBinding: currentInteractiveBinding });
546
+ materialize: () => dispatchInteractiveArtifacts({ producer: producerExecutable, irPath: enrichIrPath, target, cwd: artifactRoot, env: dispatchEnvironment, boundProject: binding.path, materializationState: nativeMaterializationState }),
547
+ }, {
548
+ getCurrentBinding: currentInteractiveBinding,
549
+ resolveExecutable: (executable) => executable === "claude" ? vendorExecutables.claude : vendorExecutables.codex,
550
+ });
481
551
  }
482
552
  const nativeMcpUrl = `http://127.0.0.1:${port}/api/native-sessions/mcp`;
483
553
  const nativeArtifacts = new NativeArtifactService({
@@ -487,6 +557,27 @@ async function main() {
487
557
  mcpUrl: process.env["WREN_HARNESS_NATIVE_MCP_URL"] ?? nativeMcpUrl,
488
558
  getBinding: currentInteractiveBinding,
489
559
  });
560
+ const nativeTerminalHostAvailable = async () => {
561
+ try {
562
+ await loadPty();
563
+ return true;
564
+ }
565
+ catch {
566
+ return false;
567
+ }
568
+ };
569
+ // This is the sole production composition of the Phase-1 policy. Browser
570
+ // requests never reach this selection or its executable/policy inputs.
571
+ const codexConfiguration = readCodexBootConfiguration(process.env);
572
+ const codexComposition = createCodexRuntimeComposition({ configuration: codexConfiguration, packageRoot,
573
+ producerBinary: producerExecutable?.executable ?? "", profileSource: harnessProfileSources.analysis, store,
574
+ getBinding: currentInteractiveBinding, sourceWrenHome: nativeSourceWrenHome, artifacts: nativeArtifacts });
575
+ const nativeRuntimeHost = new RuntimeHost({
576
+ selected: codexConfiguration.selected,
577
+ deployment: process.env["NODE_ENV"] === "production" ? "production" : "development",
578
+ localAvailable: () => codexConfiguration.allowLocal ? nativeTerminalHostAvailable() : false,
579
+ vendorProbes: { "codex-app-server": codexComposition.probe },
580
+ });
490
581
  const nativeSessions = new NativeSessionService({
491
582
  store,
492
583
  terminalManager,
@@ -503,32 +594,56 @@ async function main() {
503
594
  // Setup has no binding by definition, so it compiles the profile's authored
504
595
  // context with no user project composed in; its golden resolves nothing
505
596
  // either, which is why Setup never showed this symptom.
506
- resolveDispatchIr: async (purpose, binding) => {
507
- const profileSource = harnessProfileSources[purpose];
597
+ resolveDispatchIr: async (purpose, binding, selectedProfileSource) => {
598
+ // A selected conversation profile compiles from the registry's copy; everything else from the
599
+ // boot-time source for its purpose.
600
+ const profileSource = selectedProfileSource ?? harnessProfileSources[purpose];
508
601
  const compiled = purpose === "setup" || binding === undefined
509
602
  ? await compileRawProfile({ profileSource, mode: "native", ...warbleBinOption })
510
603
  : await compileProfile({ profileSource, userProject: binding.path, mode: "native", ...warbleBinOption });
511
604
  return compiled.irPath;
512
605
  },
606
+ resolveConversationProfile: async (id) => {
607
+ const { row, sourceDir } = await profileRegistry.resolveConversationProfileSource(id);
608
+ return { id: row.id, sourceDir, entry: row.entryKind === "agent" && row.entryVerb !== null ? { kind: "agent", verb: row.entryVerb } : { kind: "scope" } };
609
+ },
610
+ listConversationProfiles: async () => (await profileRegistry.list())
611
+ .filter((row) => row.role === "conversation")
612
+ .map((row) => ({
613
+ id: row.id,
614
+ selectable: row.admissionStatus === "admitted",
615
+ ...(row.admissionReason !== null ? { reason: row.admissionReason } : {}),
616
+ ...(row.entryKind !== null ? { entryKind: row.entryKind } : {}),
617
+ ...(row.entryVerb !== null ? { entryVerb: row.entryVerb } : {}),
618
+ })),
513
619
  // The service's option is a required string; when resolution failed the bare
514
620
  // name is what the preflight will report as unresolvable, with its reason.
515
- warbleBin: resolvedWarbleBin ?? "warble",
516
- ...(process.env["WREN_HARNESS_WREN_SHIM"] !== undefined ? { wrenShim: process.env["WREN_HARNESS_WREN_SHIM"] } : {}),
517
- terminalHostAvailable: async () => {
518
- try {
519
- await loadPty();
520
- return true;
521
- }
522
- catch {
523
- return false;
524
- }
525
- },
621
+ warbleBin: producerExecutable?.executable ?? "warble",
622
+ terminalHostAvailable: nativeTerminalHostAvailable,
623
+ runtimeHost: nativeRuntimeHost,
624
+ directCodex: codexComposition.native,
625
+ resolveManagedCodexWrenRuntime: () => resolveManagedWrenRuntime({ packageRoot }),
626
+ nativeHome,
627
+ sourceWrenHome: nativeSourceWrenHome,
628
+ ...(baseRouteOptions.codexHome ? { codexHome: baseRouteOptions.codexHome } : {}),
629
+ vendorExecutables,
630
+ nodeExecutable,
631
+ childToolDirectories: nativeToolDirectories,
632
+ ...(bootPath !== undefined ? { pathValue: bootPath } : {}),
526
633
  artifactService: nativeArtifacts,
527
634
  });
635
+ // Explicit opt-in remains behind the packaged exact-version certification gate.
636
+ const structuredRuntime = new StructuredRuntime({
637
+ selected: { codex: codexConfiguration.selected, claude: "local" }, allowLocal: codexConfiguration.allowLocal, deployment,
638
+ adapters: { codex: createCodexStructuredAdapter(codexComposition.structured) },
639
+ localRoute: route, localSetupFor: (choice) => setupRunnerFor?.(choice) ?? setupRunner,
640
+ });
528
641
  deps = {
529
642
  store,
643
+ structuredRuntime,
530
644
  nativeSessions,
531
645
  nativeArtifacts,
646
+ profileRegistry,
532
647
  route,
533
648
  baseRouteOptions,
534
649
  describeBundle: (options) => {
@@ -540,8 +655,8 @@ async function main() {
540
655
  : {}),
541
656
  });
542
657
  },
543
- describeHarnessBundle: (purpose, options) => {
544
- const profileSource = harnessProfileSources[purpose];
658
+ describeHarnessBundle: (purpose, options, selectedProfileSource) => {
659
+ const profileSource = selectedProfileSource ?? harnessProfileSources[purpose];
545
660
  const common = {
546
661
  ...options,
547
662
  profileSource,
@@ -570,7 +685,24 @@ async function main() {
570
685
  const { spec } = await prepareInteractiveTerminal(target, binding);
571
686
  const manager = await terminalManager();
572
687
  validateLegacyInteractiveWorkspace(nativeMaterializationState, binding.path, spec.cwd);
573
- return manager.start(spec, undefined, { ...process.env, WREN_PROJECT_HOME: binding.path });
688
+ if (producerExecutable === undefined || spec.hostExecutable === undefined)
689
+ throw new InteractiveLaunchError("interactive enrichment materialization failed");
690
+ const runtimeSpec = buildNativeRuntimeSpec({
691
+ backend: "local",
692
+ vendor: target === "claude-code:interactive" ? "claude" : "codex",
693
+ executables: [
694
+ nodeExecutable,
695
+ producerExecutable,
696
+ attestNativeExecutable("vendor", spec.hostExecutable),
697
+ ],
698
+ toolDirectories: nativeToolDirectories,
699
+ workspace: spec.cwd,
700
+ home: nativeHome,
701
+ binding,
702
+ ...(target === "codex:interactive" && baseRouteOptions.codexHome ? { codexHome: baseRouteOptions.codexHome } : {}),
703
+ });
704
+ assertNativeRuntimeSpec(runtimeSpec);
705
+ return manager.start(spec, undefined, runtimeSpec.childEnvironment);
574
706
  },
575
707
  prepareInteractiveTerminal: async ({ target, binding }) => {
576
708
  return (await prepareInteractiveTerminal(target, binding)).handoff;
@@ -592,6 +724,7 @@ async function main() {
592
724
  return false;
593
725
  }
594
726
  },
727
+ resolveExecutable: (executable) => executable === "claude" ? vendorExecutables.claude : vendorExecutables.codex,
595
728
  });
596
729
  },
597
730
  workspaceRoot,
@@ -605,6 +738,25 @@ async function main() {
605
738
  const server = serve({ fetch: app.fetch, websocket: { server: websocket }, hostname: "127.0.0.1", port }, (info) => {
606
739
  process.stdout.write(`wren-harness BFF listening on http://127.0.0.1:${info.port} (db: ${dbPath})\n`);
607
740
  });
741
+ const shutdown = () => {
742
+ const deadline = setTimeout(() => process.exit(1), 12_000);
743
+ void shutdownNativeResources({
744
+ shutdownSessions: async () => {
745
+ const results = await Promise.allSettled([nativeSessions.shutdown(), structuredRuntime.shutdown(), codexComposition.shutdown()]);
746
+ if (results.some((result) => result.status === "rejected"))
747
+ throw new Error("Session cleanup failed");
748
+ },
749
+ closeTerminals: () => interactiveTerminal?.closeAll(),
750
+ closeServer: () => { server.close(); },
751
+ }).then((code) => {
752
+ if (code)
753
+ process.stderr.write("error: native session cleanup did not complete\n");
754
+ clearTimeout(deadline);
755
+ process.exit(code);
756
+ });
757
+ };
758
+ process.once("SIGTERM", shutdown);
759
+ process.once("SIGINT", shutdown);
608
760
  // Without this, a port that is already taken surfaces as an unhandled 'error' event and a raw
609
761
  // Node stack trace -- the first thing a new user is likely to hit, and the least readable.
610
762
  server.on("error", (error) => {
@@ -0,0 +1,18 @@
1
+ import type { HostQueryObservation } from "../harness/route/codex-host-query.js";
2
+ import { type RenderEnvelope } from "../harness/render/envelope.js";
3
+ /**
4
+ * Grounds a codex:local answer against the queries this turn's host query service executed,
5
+ * with the component path's predicate (`citedGroundedQuery`: a tool-proven definition that
6
+ * reads at least one table, cited by `query_id`, else by whitespace-insensitive SQL).
7
+ *
8
+ * Verified only when the answer cites at least one query and every citation grounds. The
9
+ * envelope is then rebuilt from the observations alone: one `table` and one `definition` block
10
+ * (carrying the observed `query_id`) per cited query, plus the model's `summary` text as written.
11
+ * A cited result the host saw truncated still grounds the rows it shows; the host then appends
12
+ * a note to `summary`, the only place the render contract carries such text. Anything else stays the model's answer with
13
+ * `verified: false`: no citation, a citation to a query this turn never ran (including one from
14
+ * an earlier turn), a query that read no table, or chart / KPI blocks the host cannot rebuild.
15
+ */
16
+ export declare function groundCodexLocalEnvelope(envelope: RenderEnvelope, finalText: string, observations: readonly HostQueryObservation[]): RenderEnvelope;
17
+ /** Host-written, never model-written: a cited table that is only the first rows of its result. */
18
+ export declare function truncationNote(shown: number): string;
@@ -0,0 +1,92 @@
1
+ import { citedGroundedQuery } from "../harness/components/normalize.js";
2
+ import { extractJsonCandidateFromText } from "../harness/render/envelope.js";
3
+ import { namedAnswerQueryId, namedAnswerSql, sqlKey } from "../harness/render/answering-query.js";
4
+ /** Blocks whose values the host cannot rebuild from one observed table; their presence leaves an answer unverified. */
5
+ const UNREBUILDABLE_DATA_BLOCKS = new Set(["chart", "kpi_card"]);
6
+ function record(value) {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
8
+ }
9
+ /**
10
+ * The queries a codex:local answer names. A `{blocks}` answer cites through its `definition`
11
+ * blocks; a flat or query-result answer through its own `definition` / top-level fields, read
12
+ * from the raw text because envelope normalization drops a cited `query_id`. A host id wins over
13
+ * SQL, like the component path.
14
+ */
15
+ function citations(envelope, finalText) {
16
+ const raw = extractJsonCandidateFromText(finalText);
17
+ if (!Array.isArray(record(raw)?.blocks)) {
18
+ const queryId = namedAnswerQueryId(raw);
19
+ if (queryId !== undefined)
20
+ return [{ queryId }];
21
+ const sql = namedAnswerSql(raw);
22
+ return sql !== undefined ? [{ sql }] : [];
23
+ }
24
+ const found = [];
25
+ const seen = new Set();
26
+ for (const block of Array.isArray(envelope.blocks) ? envelope.blocks : []) {
27
+ const fields = record(block);
28
+ if (fields?.type !== "definition")
29
+ continue;
30
+ const queryId = typeof fields.query_id === "string" && fields.query_id.length > 0 ? fields.query_id : undefined;
31
+ const sql = typeof fields.sql === "string" && fields.sql.trim() ? fields.sql : undefined;
32
+ const citation = queryId !== undefined ? { queryId } : sql !== undefined ? { sql } : undefined;
33
+ if (!citation)
34
+ continue;
35
+ const key = queryId !== undefined ? `id:${queryId}` : `sql:${sqlKey(sql)}`;
36
+ if (seen.has(key))
37
+ continue;
38
+ seen.add(key);
39
+ found.push(citation);
40
+ }
41
+ return found;
42
+ }
43
+ /**
44
+ * Grounds a codex:local answer against the queries this turn's host query service executed,
45
+ * with the component path's predicate (`citedGroundedQuery`: a tool-proven definition that
46
+ * reads at least one table, cited by `query_id`, else by whitespace-insensitive SQL).
47
+ *
48
+ * Verified only when the answer cites at least one query and every citation grounds. The
49
+ * envelope is then rebuilt from the observations alone: one `table` and one `definition` block
50
+ * (carrying the observed `query_id`) per cited query, plus the model's `summary` text as written.
51
+ * A cited result the host saw truncated still grounds the rows it shows; the host then appends
52
+ * a note to `summary`, the only place the render contract carries such text. Anything else stays the model's answer with
53
+ * `verified: false`: no citation, a citation to a query this turn never ran (including one from
54
+ * an earlier turn), a query that read no table, or chart / KPI blocks the host cannot rebuild.
55
+ */
56
+ export function groundCodexLocalEnvelope(envelope, finalText, observations) {
57
+ const unverified = { ...envelope, verified: false };
58
+ if (observations.length === 0)
59
+ return unverified;
60
+ const blocks = Array.isArray(envelope.blocks) ? envelope.blocks : [];
61
+ if (blocks.some((block) => UNREBUILDABLE_DATA_BLOCKS.has(String(record(block)?.type))))
62
+ return unverified;
63
+ const cited = citations(envelope, finalText);
64
+ if (cited.length === 0)
65
+ return unverified;
66
+ const rebuilt = [];
67
+ const notes = [];
68
+ for (const citation of cited) {
69
+ const query = citedGroundedQuery(observations, citation);
70
+ if (!query)
71
+ return unverified;
72
+ const observation = observations.find((call) => call.output?.query_id === query.queryId);
73
+ if (observation?.output?.truncated === true)
74
+ notes.push(truncationNote(query.table.rows.length));
75
+ // The definition keeps the observed query's host id, so grounding the rebuilt envelope
76
+ // again (the artifact path does) resolves to the very same observation.
77
+ rebuilt.push({ type: "table", columns: query.table.columns, rows: query.table.rows }, { type: "definition", ...query.definition, ...(query.queryId !== undefined ? { query_id: query.queryId } : {}) });
78
+ }
79
+ const summary = withNotes(typeof envelope.summary === "string" ? envelope.summary : undefined, notes);
80
+ return { blocks: rebuilt, ...(summary !== undefined ? { summary } : {}), verified: true };
81
+ }
82
+ /** Host-written, never model-written: a cited table that is only the first rows of its result. */
83
+ export function truncationNote(shown) {
84
+ return `Host note: this query returned more than ${shown} rows; the table shows only the first ${shown}.`;
85
+ }
86
+ /** Appends each host note once, so grounding an already-grounded envelope again changes nothing. */
87
+ function withNotes(summary, notes) {
88
+ const missing = [...new Set(notes)].filter((note) => !summary?.includes(note));
89
+ if (missing.length === 0)
90
+ return summary;
91
+ return [summary, ...missing].filter((part) => part !== undefined && part.length > 0).join("\n\n");
92
+ }
@@ -0,0 +1,25 @@
1
+ import type { CodexAppServerBackend, CodexLaunchPermit } from "./runtime-host/codex-app-server.js";
2
+ import type { CodexSessionTools } from "./runtime-host/codex-direct-tools.js";
3
+ import type { EnrichmentBinding } from "./enrichment.js";
4
+ import type { NativeSessionRow } from "./db.js";
5
+ /** Server-only provisioner. A ready probe alone never authorizes materialization. */
6
+ export interface DirectCodexProvisioner {
7
+ /** Official remote CLI renderer; the host still owns the governed thread. */
8
+ readonly terminal?: boolean;
9
+ readonly terminalCertified?: (entry: string) => boolean;
10
+ readonly backend: Pick<CodexAppServerBackend, "prepareLaunch" | "open">;
11
+ /** Own and clean partial allocations on failure. Never receives browser paths or credentials. */
12
+ prepare(input: {
13
+ readonly session: NativeSessionRow;
14
+ readonly binding: EnrichmentBinding;
15
+ readonly permit: CodexLaunchPermit;
16
+ readonly signal: AbortSignal;
17
+ readonly assertActive: () => void;
18
+ }): Promise<{
19
+ readonly input: Omit<Parameters<CodexAppServerBackend["open"]>[1], "onEvent" | "tools"> & {
20
+ readonly tools: CodexSessionTools;
21
+ };
22
+ /** Runs after the conversation and scoped tools have closed. */
23
+ dispose(): Promise<void>;
24
+ }>;
25
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import type { Store } from "./db.js";
2
+ import type { EnrichmentBinding } from "./enrichment.js";
3
+ import type { NativeArtifactService } from "./native-artifacts.js";
4
+ import type { RuntimeBackendProbeResult } from "./runtime-host/types.js";
5
+ import type { DirectCodexProvisioner } from "./codex-native-session.js";
6
+ import type { CodexStructuredProvisioner } from "./codex-structured-ask.js";
7
+ export interface CodexBootConfiguration {
8
+ readonly selected: "local" | "codex-app-server";
9
+ readonly allowLocal: boolean;
10
+ readonly executable?: string;
11
+ readonly source?: string;
12
+ readonly loginHome?: string;
13
+ readonly accountEmail?: string;
14
+ }
15
+ /** Read once at composition. No browser values, PATH discovery or credential copying. */
16
+ export declare function readCodexBootConfiguration(env: NodeJS.ProcessEnv): CodexBootConfiguration;
17
+ interface Options {
18
+ readonly configuration: CodexBootConfiguration;
19
+ readonly packageRoot: string;
20
+ readonly producerBinary: string;
21
+ readonly profileSource: string;
22
+ readonly store: Store;
23
+ readonly getBinding: () => EnrichmentBinding | undefined;
24
+ readonly sourceWrenHome: () => string;
25
+ readonly artifacts: NativeArtifactService;
26
+ }
27
+ /** Real composition for both entry surfaces; certification is enforced by the packaged backend. */
28
+ export declare function createCodexRuntimeComposition(options: Options): {
29
+ probe: () => Promise<RuntimeBackendProbeResult<"codex-app-server">>;
30
+ native: DirectCodexProvisioner;
31
+ structured: CodexStructuredProvisioner;
32
+ shutdown: () => Promise<void>;
33
+ };
34
+ export {};