@wrenai/genbi 0.0.4 → 0.0.6

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 (234) hide show
  1. package/README.md +65 -3
  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 +43 -45
  93. package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
  94. package/dist-server/harness/route/codex-local-manifest.js +96 -15
  95. package/dist-server/harness/route/describe.js +45 -6
  96. package/dist-server/harness/route/dispatched.d.ts +20 -0
  97. package/dist-server/harness/route/dispatched.js +122 -24
  98. package/dist-server/harness/route/host-query.d.ts +93 -0
  99. package/dist-server/harness/route/host-query.js +257 -0
  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 +8 -2
  103. package/dist-server/harness/route/types.d.ts +47 -1
  104. package/dist-server/harness/route/wren-host-proxy.d.ts +33 -0
  105. package/dist-server/harness/route/wren-host-proxy.js +228 -0
  106. package/dist-server/harness/session/run.js +30 -10
  107. package/dist-server/harness/setup/runner.d.ts +17 -3
  108. package/dist-server/harness/setup/runner.js +272 -89
  109. package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
  110. package/dist-server/harness/setup/yaml-scalar.js +62 -0
  111. package/dist-server/harness/tools/setup-native.d.ts +7 -0
  112. package/dist-server/harness/tools/setup-native.js +7 -4
  113. package/dist-server/server/adopt.js +1 -17
  114. package/dist-server/server/app.d.ts +1 -0
  115. package/dist-server/server/app.js +175 -17
  116. package/dist-server/server/auth-choice.d.ts +13 -1
  117. package/dist-server/server/auth-choice.js +66 -1
  118. package/dist-server/server/bin.js +185 -25
  119. package/dist-server/server/codex-native-session.d.ts +25 -0
  120. package/dist-server/server/codex-native-session.js +1 -0
  121. package/dist-server/server/codex-runtime-composition.d.ts +34 -0
  122. package/dist-server/server/codex-runtime-composition.js +250 -0
  123. package/dist-server/server/codex-structured-ask.d.ts +23 -0
  124. package/dist-server/server/codex-structured-ask.js +102 -0
  125. package/dist-server/server/conn-config.js +4 -16
  126. package/dist-server/server/conversation-socket.d.ts +16 -0
  127. package/dist-server/server/conversation-socket.js +65 -0
  128. package/dist-server/server/db.d.ts +46 -0
  129. package/dist-server/server/db.js +88 -9
  130. package/dist-server/server/env-detect.d.ts +6 -0
  131. package/dist-server/server/env-detect.js +7 -0
  132. package/dist-server/server/fold.d.ts +23 -4
  133. package/dist-server/server/fold.js +42 -5
  134. package/dist-server/server/harness.d.ts +2 -2
  135. package/dist-server/server/harness.js +33 -7
  136. package/dist-server/server/host-evidence-grounding.d.ts +21 -0
  137. package/dist-server/server/host-evidence-grounding.js +93 -0
  138. package/dist-server/server/interactive-terminal.d.ts +10 -6
  139. package/dist-server/server/interactive-terminal.js +33 -8
  140. package/dist-server/server/managed-wren-runtime.d.ts +116 -0
  141. package/dist-server/server/managed-wren-runtime.js +670 -0
  142. package/dist-server/server/native-artifacts.d.ts +112 -7
  143. package/dist-server/server/native-artifacts.js +194 -14
  144. package/dist-server/server/native-component-context.d.ts +19 -0
  145. package/dist-server/server/native-component-context.js +76 -0
  146. package/dist-server/server/native-component-preparation.d.ts +20 -0
  147. package/dist-server/server/native-component-preparation.js +129 -0
  148. package/dist-server/server/native-component-runtime.d.ts +36 -0
  149. package/dist-server/server/native-component-runtime.js +92 -0
  150. package/dist-server/server/native-components.d.ts +84 -0
  151. package/dist-server/server/native-components.js +160 -0
  152. package/dist-server/server/native-dispatch-process.d.ts +8 -0
  153. package/dist-server/server/native-dispatch-process.js +65 -0
  154. package/dist-server/server/native-runtime-spec.d.ts +89 -0
  155. package/dist-server/server/native-runtime-spec.js +169 -0
  156. package/dist-server/server/native-sessions.d.ts +172 -19
  157. package/dist-server/server/native-sessions.js +839 -244
  158. package/dist-server/server/native-shutdown.d.ts +6 -0
  159. package/dist-server/server/native-shutdown.js +28 -0
  160. package/dist-server/server/native-wren-home.d.ts +18 -0
  161. package/dist-server/server/native-wren-home.js +262 -0
  162. package/dist-server/server/node-pty-host.d.ts +2 -1
  163. package/dist-server/server/node-pty-host.js +3 -2
  164. package/dist-server/server/profile-registry.d.ts +139 -0
  165. package/dist-server/server/profile-registry.js +620 -0
  166. package/dist-server/server/runtime-binding.d.ts +4 -1
  167. package/dist-server/server/runtime-binding.js +50 -2
  168. package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
  169. package/dist-server/server/runtime-host/claude-component-step.js +186 -0
  170. package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
  171. package/dist-server/server/runtime-host/codex-app-server.js +239 -0
  172. package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
  173. package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
  174. package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
  175. package/dist-server/server/runtime-host/codex-component-step.js +308 -0
  176. package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
  177. package/dist-server/server/runtime-host/codex-conversation.js +295 -0
  178. package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
  179. package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
  180. package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
  181. package/dist-server/server/runtime-host/codex-events.js +66 -0
  182. package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
  183. package/dist-server/server/runtime-host/codex-policy.js +151 -0
  184. package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
  185. package/dist-server/server/runtime-host/codex-process.js +96 -0
  186. package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
  187. package/dist-server/server/runtime-host/codex-rpc.js +182 -0
  188. package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
  189. package/dist-server/server/runtime-host/codex-session.js +492 -0
  190. package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
  191. package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
  192. package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
  193. package/dist-server/server/runtime-host/codex-terminal.js +120 -0
  194. package/dist-server/server/runtime-host/index.d.ts +5 -0
  195. package/dist-server/server/runtime-host/index.js +3 -0
  196. package/dist-server/server/runtime-host/local.d.ts +29 -0
  197. package/dist-server/server/runtime-host/local.js +90 -0
  198. package/dist-server/server/runtime-host/policy.d.ts +8 -0
  199. package/dist-server/server/runtime-host/policy.js +78 -0
  200. package/dist-server/server/runtime-host/types.d.ts +52 -0
  201. package/dist-server/server/runtime-host/types.js +62 -0
  202. package/dist-server/server/structured-query-evidence.d.ts +7 -0
  203. package/dist-server/server/structured-query-evidence.js +47 -0
  204. package/dist-server/server/structured-runtime.d.ts +52 -0
  205. package/dist-server/server/structured-runtime.js +130 -0
  206. package/dist-server/server/structured-scope.d.ts +4 -0
  207. package/dist-server/server/structured-scope.js +12 -0
  208. package/dist-server/server/turn.d.ts +6 -1
  209. package/dist-server/server/turn.js +86 -26
  210. package/dist-server/server/wire-types.d.ts +67 -3
  211. package/dist-server/server/wren-profiles.js +1 -1
  212. package/managed-wren/README.md +60 -0
  213. package/managed-wren/dependency-wheels.json +282 -0
  214. package/managed-wren/manifest.json +373 -0
  215. package/managed-wren/release-inputs.json +45 -0
  216. package/managed-wren/runtime-tree.cjs +115 -0
  217. package/managed-wren/source-inputs.json +40 -0
  218. package/package.json +15 -6
  219. package/profiles/genbi-default/context/binding.yml +17 -7
  220. package/profiles/genbi-default/context/context.json +146 -0
  221. package/profiles/genbi-default/ir.golden.json +39 -43
  222. package/profiles/genbi-default/profile.yml +31 -0
  223. package/profiles/genbi-enrich-context/context/binding.yml +14 -3
  224. package/profiles/genbi-enrich-context/context/context.json +146 -0
  225. package/profiles/genbi-enrich-context/ir.golden.json +4 -5
  226. package/profiles/genbi-monitor/context/binding.yml +13 -5
  227. package/profiles/genbi-monitor/context/context.json +146 -0
  228. package/profiles/genbi-monitor/ir.golden.json +13 -7
  229. package/profiles/genbi-report/context/binding.yml +19 -0
  230. package/profiles/genbi-report/context/context.json +223 -0
  231. package/profiles/genbi-report/ir.golden.json +328 -0
  232. package/profiles/genbi-report/profile.yml +38 -0
  233. package/profiles/genbi-setup/ir.golden.json +3 -1
  234. package/dist/assets/index-Xs4q7pNo.css +0 -1
@@ -1,16 +1,21 @@
1
1
  export { UnknownAdapterError, UnknownTierError } from "./errors.js";
2
2
  export { createProviderRegistry } from "./registry.js";
3
- export { resolveStepModel, resolveTierModel } from "./binding.js";
3
+ export { isZoneAwareBinding, parseTierBindingKey, resolveStepModel, resolveTierModel, resolveTierSpec, tierBindingKey, } from "./binding.js";
4
+ export { ANSWER_SHAPES, ZONES, answerShapeSchema, disclosurePolicySchema, parseDisclosurePolicy, parseTierBindingDocument, readTierBindingFile, TierBindingParseError, tierBindingDocumentSchema, zoneRolesSchema, zoneSchema, } from "./zone.js";
4
5
  export { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
5
- export { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
6
+ export { createOpenAICompatibleAdapter, mergeRequestBody, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
6
7
  export { ANTHROPIC_ADAPTER_ID, createAnthropicAdapter } from "./adapters/anthropic.js";
8
+ export { createOpenAIAdapter, OPENAI_ADAPTER_ID } from "./adapters/openai.js";
9
+ export { createPiAiAdapter, listPiAiCatalog, PI_AI_ADAPTER_ID } from "./adapters/pi-ai.js";
7
10
  import { createAnthropicAdapter, ANTHROPIC_ADAPTER_ID } from "./adapters/anthropic.js";
8
11
  import { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
12
+ import { createOpenAIAdapter, OPENAI_ADAPTER_ID } from "./adapters/openai.js";
9
13
  import { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
14
+ import { createPiAiAdapter, PI_AI_ADAPTER_ID } from "./adapters/pi-ai.js";
10
15
  import { createProviderRegistry } from "./registry.js";
11
16
  /**
12
17
  * A provider registry pre-populated with the built-in adapters
13
- * (`mock`, `openai-compatible`, `anthropic`). Callers can still
18
+ * (`mock`, `openai-compatible`, `anthropic`, `openai`, `pi-ai`). Callers can still
14
19
  * `register()` additional adapter ids onto the returned registry.
15
20
  */
16
21
  export function createDefaultProviderRegistry() {
@@ -18,5 +23,7 @@ export function createDefaultProviderRegistry() {
18
23
  registry.register(MOCK_ADAPTER_ID, createMockAdapter);
19
24
  registry.register(OPENAI_COMPATIBLE_ADAPTER_ID, createOpenAICompatibleAdapter);
20
25
  registry.register(ANTHROPIC_ADAPTER_ID, createAnthropicAdapter);
26
+ registry.register(OPENAI_ADAPTER_ID, createOpenAIAdapter);
27
+ registry.register(PI_AI_ADAPTER_ID, createPiAiAdapter);
21
28
  return registry;
22
29
  }
@@ -0,0 +1,96 @@
1
+ import { z } from "zod";
2
+ import type { TierBinding } from "./binding.js";
3
+ /**
4
+ * Where a tier's model runs. `private` is a deployment the operator trusts
5
+ * with project data (rows, SQL, the semantic context); `public` is anything
6
+ * else, typically a cloud API. A deployment fact declared on the binding —
7
+ * never derived from an endpoint host, a model id or an adapter kind.
8
+ */
9
+ export declare const ZONES: readonly ["private", "public"];
10
+ export type Zone = (typeof ZONES)[number];
11
+ export declare const zoneSchema: z.ZodEnum<{
12
+ private: "private";
13
+ public: "public";
14
+ }>;
15
+ /** The answer shapes a planner may declare for a slot and the policy may allow. */
16
+ export declare const ANSWER_SHAPES: readonly ["scalar", "series", "table", "narrative"];
17
+ export type AnswerShape = (typeof ANSWER_SHAPES)[number];
18
+ export declare const answerShapeSchema: z.ZodEnum<{
19
+ narrative: "narrative";
20
+ scalar: "scalar";
21
+ series: "series";
22
+ table: "table";
23
+ }>;
24
+ /**
25
+ * The egress disclosure policy: what a verified callee answer may look like
26
+ * before it is disclosed to a public-zone caller. `max_rows` is required and
27
+ * must be a positive integer — a policy that forgets it would otherwise
28
+ * silently allow any table through, which is the wrong default for a gate.
29
+ */
30
+ export declare const disclosurePolicySchema: z.ZodObject<{
31
+ allowed_shapes: z.ZodDefault<z.ZodArray<z.ZodEnum<{
32
+ narrative: "narrative";
33
+ scalar: "scalar";
34
+ series: "series";
35
+ table: "table";
36
+ }>>>;
37
+ max_rows: z.ZodNumber;
38
+ min_group_size: z.ZodDefault<z.ZodNumber>;
39
+ sensitive_column_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
40
+ pii_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
41
+ judge_timeout_ms: z.ZodDefault<z.ZodNumber>;
42
+ }, z.core.$strict>;
43
+ export type DisclosurePolicy = z.infer<typeof disclosurePolicySchema>;
44
+ /**
45
+ * Non-step tiers the zone gate must check by role. Each value is a binding
46
+ * key (a bare tier or `<mount>/<tier>`) that must resolve in `tiers`.
47
+ */
48
+ export declare const zoneRolesSchema: z.ZodObject<{
49
+ judge: z.ZodOptional<z.ZodString>;
50
+ render: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strict>;
52
+ export type ZoneRoles = z.infer<typeof zoneRolesSchema>;
53
+ /**
54
+ * The on-disk tier binding document (`--tier-binding <file>`), the file
55
+ * counterpart of the `--tier-adapter` flags. Keys of `tiers` are bare tier
56
+ * names or `<mount>/<tier>`; `disclosure_policy` and `roles` are the hybrid
57
+ * split's additions. Credentials belong in the adapter's environment, not in
58
+ * this file, but nothing here forbids `config.apiKey` for a local dev setup.
59
+ */
60
+ export declare const tierBindingDocumentSchema: z.ZodObject<{
61
+ tiers: z.ZodRecord<z.ZodString, z.ZodObject<{
62
+ adapter: z.ZodString;
63
+ config: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
64
+ zone: z.ZodOptional<z.ZodEnum<{
65
+ private: "private";
66
+ public: "public";
67
+ }>>;
68
+ }, z.core.$strict>>;
69
+ disclosure_policy: z.ZodOptional<z.ZodObject<{
70
+ allowed_shapes: z.ZodDefault<z.ZodArray<z.ZodEnum<{
71
+ narrative: "narrative";
72
+ scalar: "scalar";
73
+ series: "series";
74
+ table: "table";
75
+ }>>>;
76
+ max_rows: z.ZodNumber;
77
+ min_group_size: z.ZodDefault<z.ZodNumber>;
78
+ sensitive_column_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
79
+ pii_patterns: z.ZodDefault<z.ZodArray<z.ZodString>>;
80
+ judge_timeout_ms: z.ZodDefault<z.ZodNumber>;
81
+ }, z.core.$strict>>;
82
+ roles: z.ZodOptional<z.ZodObject<{
83
+ judge: z.ZodOptional<z.ZodString>;
84
+ render: z.ZodOptional<z.ZodString>;
85
+ }, z.core.$strict>>;
86
+ }, z.core.$strict>;
87
+ export type TierBindingDocument = z.infer<typeof tierBindingDocumentSchema>;
88
+ export declare class TierBindingParseError extends Error {
89
+ constructor(message: string);
90
+ }
91
+ /** Parses a tier binding document into the runtime `TierBinding`, loud-failing on any schema violation. */
92
+ export declare function parseTierBindingDocument(value: unknown): TierBinding;
93
+ /** Parses a disclosure policy on its own (the shape the egress verification step consumes). */
94
+ export declare function parseDisclosurePolicy(value: unknown): DisclosurePolicy;
95
+ /** Reads and parses a JSON tier binding file. */
96
+ export declare function readTierBindingFile(filePath: string): Promise<TierBinding>;
@@ -0,0 +1,112 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { z } from "zod";
3
+ /**
4
+ * Where a tier's model runs. `private` is a deployment the operator trusts
5
+ * with project data (rows, SQL, the semantic context); `public` is anything
6
+ * else, typically a cloud API. A deployment fact declared on the binding —
7
+ * never derived from an endpoint host, a model id or an adapter kind.
8
+ */
9
+ export const ZONES = ["private", "public"];
10
+ export const zoneSchema = z.enum(ZONES);
11
+ /** The answer shapes a planner may declare for a slot and the policy may allow. */
12
+ export const ANSWER_SHAPES = ["scalar", "series", "table", "narrative"];
13
+ export const answerShapeSchema = z.enum(ANSWER_SHAPES);
14
+ /**
15
+ * The egress disclosure policy: what a verified callee answer may look like
16
+ * before it is disclosed to a public-zone caller. `max_rows` is required and
17
+ * must be a positive integer — a policy that forgets it would otherwise
18
+ * silently allow any table through, which is the wrong default for a gate.
19
+ */
20
+ export const disclosurePolicySchema = z.object({
21
+ allowed_shapes: z.array(answerShapeSchema).min(1).default([...ANSWER_SHAPES]),
22
+ max_rows: z.number().int().positive(),
23
+ min_group_size: z.number().int().nonnegative().default(1),
24
+ sensitive_column_patterns: z.array(z.string().min(1)).default([]),
25
+ /** Regexes matched against string cells; the built-in PII set applies as well. */
26
+ pii_patterns: z.array(z.string().min(1)).default([]),
27
+ /** Judge wall-clock budget; a judge that exceeds it refuses the slot. */
28
+ judge_timeout_ms: z.number().int().positive().default(30_000),
29
+ }).strict();
30
+ /**
31
+ * Non-step tiers the zone gate must check by role. Each value is a binding
32
+ * key (a bare tier or `<mount>/<tier>`) that must resolve in `tiers`.
33
+ */
34
+ export const zoneRolesSchema = z.object({
35
+ judge: z.string().min(1).optional(),
36
+ render: z.string().min(1).optional(),
37
+ }).strict();
38
+ const adapterSpecSchema = z.object({
39
+ adapter: z.string().min(1),
40
+ config: z.record(z.string(), z.unknown()).default({}),
41
+ zone: zoneSchema.optional(),
42
+ }).strict();
43
+ /**
44
+ * The on-disk tier binding document (`--tier-binding <file>`), the file
45
+ * counterpart of the `--tier-adapter` flags. Keys of `tiers` are bare tier
46
+ * names or `<mount>/<tier>`; `disclosure_policy` and `roles` are the hybrid
47
+ * split's additions. Credentials belong in the adapter's environment, not in
48
+ * this file, but nothing here forbids `config.apiKey` for a local dev setup.
49
+ */
50
+ export const tierBindingDocumentSchema = z.object({
51
+ tiers: z.record(z.string().min(1), adapterSpecSchema),
52
+ disclosure_policy: disclosurePolicySchema.optional(),
53
+ roles: zoneRolesSchema.optional(),
54
+ }).strict();
55
+ export class TierBindingParseError extends Error {
56
+ constructor(message) {
57
+ super(message);
58
+ this.name = "TierBindingParseError";
59
+ }
60
+ }
61
+ function describeIssues(error) {
62
+ return error.issues.map((issue) => `${issue.path.map(String).join(".") || "<root>"}: ${issue.message}`).join("; ");
63
+ }
64
+ /** Parses a tier binding document into the runtime `TierBinding`, loud-failing on any schema violation. */
65
+ export function parseTierBindingDocument(value) {
66
+ const parsed = tierBindingDocumentSchema.safeParse(value);
67
+ if (!parsed.success)
68
+ throw new TierBindingParseError(`tier binding is invalid — ${describeIssues(parsed.error)}`);
69
+ const tiers = {};
70
+ for (const [key, spec] of Object.entries(parsed.data.tiers)) {
71
+ if (key.startsWith("/") || key.endsWith("/") || key.split("/").length > 2) {
72
+ throw new TierBindingParseError(`tier binding key "${key}" must be "<tier>" or "<mount>/<tier>"`);
73
+ }
74
+ tiers[key] = { adapter: spec.adapter, config: spec.config, ...(spec.zone !== undefined ? { zone: spec.zone } : {}) };
75
+ }
76
+ const binding = {
77
+ tiers,
78
+ ...(parsed.data.disclosure_policy !== undefined ? { disclosurePolicy: parsed.data.disclosure_policy } : {}),
79
+ ...(parsed.data.roles !== undefined ? { roles: parsed.data.roles } : {}),
80
+ };
81
+ for (const [role, key] of Object.entries(binding.roles ?? {})) {
82
+ if (key !== undefined && binding.tiers[key] === undefined) {
83
+ throw new TierBindingParseError(`roles.${role} names tier "${key}", which is not in tiers`);
84
+ }
85
+ }
86
+ return binding;
87
+ }
88
+ /** Parses a disclosure policy on its own (the shape the egress verification step consumes). */
89
+ export function parseDisclosurePolicy(value) {
90
+ const parsed = disclosurePolicySchema.safeParse(value);
91
+ if (!parsed.success)
92
+ throw new TierBindingParseError(`disclosure_policy is invalid — ${describeIssues(parsed.error)}`);
93
+ return parsed.data;
94
+ }
95
+ /** Reads and parses a JSON tier binding file. */
96
+ export async function readTierBindingFile(filePath) {
97
+ let text;
98
+ try {
99
+ text = await readFile(filePath, "utf8");
100
+ }
101
+ catch (error) {
102
+ throw new TierBindingParseError(`cannot read tier binding file "${filePath}": ${error instanceof Error ? error.message : String(error)}`);
103
+ }
104
+ let value;
105
+ try {
106
+ value = JSON.parse(text);
107
+ }
108
+ catch (error) {
109
+ throw new TierBindingParseError(`tier binding file "${filePath}" is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
110
+ }
111
+ return parseTierBindingDocument(value);
112
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Which executed query answers the question. A step may run several successful
3
+ * queries (an exploratory count, then the real one, then a stray check), so
4
+ * "the last successful table" is not a safe choice: a chatty model produces a
5
+ * grounded but wrong answer. The model names the query it answered with in the
6
+ * terminal value's `definition`: by the `query_id` the host attached to the
7
+ * tool result, or by the exact SQL it ran; the host then selects that observed
8
+ * table, and never the model's own copy of the rows.
9
+ */
10
+ /** Whitespace- and terminator-insensitive identity for one SQL text. */
11
+ export declare function sqlKey(sql: string): string;
12
+ /**
13
+ * The host-assigned query id (`q<N>`) a terminal value cites as its answer, if any. An id is
14
+ * preferred over SQL: the model names the executed query instead of re-typing it.
15
+ */
16
+ export declare function namedAnswerQueryId(value: unknown): string | undefined;
17
+ /** The SQL a terminal value names as its answer, if it names exactly one. */
18
+ export declare function namedAnswerSql(value: unknown): string | undefined;
19
+ /**
20
+ * Picks the answering entry among candidates that each carry the SQL they ran.
21
+ * Named and observed: the last observation of that SQL. Named but never run:
22
+ * `undefined`, so the caller refuses rather than substitute another table.
23
+ * Unnamed: the last candidate, the historical rule, kept only for values that
24
+ * do not name a query at all.
25
+ */
26
+ export declare function selectAnsweringCandidate<T extends {
27
+ readonly sql?: string | undefined;
28
+ readonly queryId?: string | undefined;
29
+ }>(candidates: readonly T[], terminalValue: unknown): T | undefined;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Which executed query answers the question. A step may run several successful
3
+ * queries (an exploratory count, then the real one, then a stray check), so
4
+ * "the last successful table" is not a safe choice: a chatty model produces a
5
+ * grounded but wrong answer. The model names the query it answered with in the
6
+ * terminal value's `definition`: by the `query_id` the host attached to the
7
+ * tool result, or by the exact SQL it ran; the host then selects that observed
8
+ * table, and never the model's own copy of the rows.
9
+ */
10
+ /** Whitespace- and terminator-insensitive identity for one SQL text. */
11
+ export function sqlKey(sql) {
12
+ return sql.replace(/\s+/g, " ").replace(/;\s*$/, "").trim();
13
+ }
14
+ /**
15
+ * The host-assigned query id (`q<N>`) a terminal value cites as its answer, if any. An id is
16
+ * preferred over SQL: the model names the executed query instead of re-typing it.
17
+ */
18
+ export function namedAnswerQueryId(value) {
19
+ if (typeof value !== "object" || value === null || Array.isArray(value))
20
+ return undefined;
21
+ const record = value;
22
+ const definition = record.definition;
23
+ if (typeof definition === "object" && definition !== null && !Array.isArray(definition)) {
24
+ const id = definition.query_id;
25
+ if (typeof id === "string" && id.length > 0)
26
+ return id;
27
+ }
28
+ return typeof record.query_id === "string" && record.query_id.length > 0 ? record.query_id : undefined;
29
+ }
30
+ /** The SQL a terminal value names as its answer, if it names exactly one. */
31
+ export function namedAnswerSql(value) {
32
+ if (typeof value !== "object" || value === null || Array.isArray(value))
33
+ return undefined;
34
+ const record = value;
35
+ const definition = record.definition;
36
+ if (typeof definition === "object" && definition !== null && !Array.isArray(definition)) {
37
+ const sql = definition.sql;
38
+ if (typeof sql === "string" && sql.trim())
39
+ return sql;
40
+ }
41
+ return typeof record.sql === "string" && record.sql.trim() ? record.sql : undefined;
42
+ }
43
+ /**
44
+ * Picks the answering entry among candidates that each carry the SQL they ran.
45
+ * Named and observed: the last observation of that SQL. Named but never run:
46
+ * `undefined`, so the caller refuses rather than substitute another table.
47
+ * Unnamed: the last candidate, the historical rule, kept only for values that
48
+ * do not name a query at all.
49
+ */
50
+ export function selectAnsweringCandidate(candidates, terminalValue) {
51
+ // Cited by id among candidates that carry host ids: that candidate or none, never a fallback
52
+ // to the SQL text or the last table. Candidates with no ids at all (the session loop's seeds)
53
+ // cannot resolve an id, so the citation falls through to the SQL and last-table rules.
54
+ const id = namedAnswerQueryId(terminalValue);
55
+ if (id !== undefined && candidates.some((candidate) => candidate.queryId !== undefined))
56
+ return candidates.find((candidate) => candidate.queryId === id);
57
+ const named = namedAnswerSql(terminalValue);
58
+ if (named === undefined)
59
+ return candidates.at(-1);
60
+ const key = sqlKey(named);
61
+ return [...candidates].reverse().find((candidate) => candidate.sql !== undefined && sqlKey(candidate.sql) === key);
62
+ }
@@ -143,3 +143,9 @@ export declare function isTableOnlySchema(outputSchema: Record<string, unknown>)
143
143
  * `form: "text"` instead of fabricating an envelope.
144
144
  */
145
145
  export declare function extractEnvelopeFromText(text: string): RenderEnvelope | undefined;
146
+ /**
147
+ * The raw JSON object a model's final text carries (fence- and prose-tolerant, see
148
+ * `extractJsonObjectText`), before any envelope normalization, else `undefined`. Fields the
149
+ * normalization drops, such as a cited `query_id`, are still present here.
150
+ */
151
+ export declare function extractJsonCandidateFromText(text: string): unknown;
@@ -351,14 +351,21 @@ function extractJsonObjectText(text) {
351
351
  * `form: "text"` instead of fabricating an envelope.
352
352
  */
353
353
  export function extractEnvelopeFromText(text) {
354
- let candidate;
354
+ const candidate = extractJsonCandidateFromText(text);
355
+ return candidate === undefined ? undefined : normalizeExtractedCandidate(candidate);
356
+ }
357
+ /**
358
+ * The raw JSON object a model's final text carries (fence- and prose-tolerant, see
359
+ * `extractJsonObjectText`), before any envelope normalization, else `undefined`. Fields the
360
+ * normalization drops, such as a cited `query_id`, are still present here.
361
+ */
362
+ export function extractJsonCandidateFromText(text) {
355
363
  try {
356
- candidate = JSON.parse(extractJsonObjectText(text));
364
+ return JSON.parse(extractJsonObjectText(text));
357
365
  }
358
366
  catch {
359
367
  return undefined;
360
368
  }
361
- return normalizeExtractedCandidate(candidate);
362
369
  }
363
370
  /** Bounded: at most one MCP `CallToolResult` unwrap, so a pathological/self-referential payload can't recurse forever. */
364
371
  const MAX_UNWRAP_DEPTH = 2;
@@ -25,15 +25,14 @@ export interface DeriveAdapterSpecOptions {
25
25
  * exactly what that adapter targets. `endpoint` becomes `baseURL`; `model`
26
26
  * comes from `options.model` (falling back to `DEFAULT_LOCAL_MODEL`) since
27
27
  * `LocalAuthChoice` itself carries no model field.
28
- * - `gateway` — also mapped onto `openai-compatible` (an operator-managed
29
- * gateway is, from this adapter's point of view, just another OpenAI-compatible
30
- * endpoint); `authChoice.config` must already carry `baseURL`/`model`
31
- * (and optionally `apiKey`) matching `OpenAICompatibleAdapterConfig` — 70a
32
- * leaves this config shape unspecified, so this is this ticket's own
33
- * assumption, not a contract 70a promises. Unlike `local` (which defaults
34
- * endpoint/model), `gateway` has no sensible default, so a missing/empty
35
- * `baseURL`/`model` is a loud `wren-harness`-level error here rather than a
36
- * bare "Invalid URL" deep inside the adapter.
28
+ * - `gateway` — mapped onto the `pi-ai` adapter, pi-ai's unified client for
29
+ * an operator-chosen provider (a built-in pi-ai provider id such as
30
+ * `openrouter` or `amazon-bedrock`, or a custom OpenAI-compatible endpoint
31
+ * when `baseUrl` is set). `authChoice.config` must carry `provider` and
32
+ * `model`, and may carry `apiKey`, `baseUrl` and `headers`
33
+ * (`PiAiAdapterConfig`). `gateway` has no sensible default, so a
34
+ * missing/empty `provider`/`model` is a loud `wren-harness`-level error here
35
+ * rather than a failure deep inside the adapter on the first call.
37
36
  */
38
37
  export declare function deriveAdapterSpec(authChoice: Extract<AuthChoice, {
39
38
  mode: "api-key" | "local" | "gateway";
@@ -1,4 +1,4 @@
1
- import { OPENAI_COMPATIBLE_ADAPTER_ID } from "../providers/index.js";
1
+ import { OPENAI_COMPATIBLE_ADAPTER_ID, PI_AI_ADAPTER_ID } from "../providers/index.js";
2
2
  /** Default local endpoint used when a `LocalAuthChoice` omits `endpoint` (Ollama's default OpenAI-compatible port). */
3
3
  export const DEFAULT_LOCAL_ENDPOINT = "http://localhost:11434/v1";
4
4
  /**
@@ -20,15 +20,14 @@ export const DEFAULT_LOCAL_MODEL = "llama3.1";
20
20
  * exactly what that adapter targets. `endpoint` becomes `baseURL`; `model`
21
21
  * comes from `options.model` (falling back to `DEFAULT_LOCAL_MODEL`) since
22
22
  * `LocalAuthChoice` itself carries no model field.
23
- * - `gateway` — also mapped onto `openai-compatible` (an operator-managed
24
- * gateway is, from this adapter's point of view, just another OpenAI-compatible
25
- * endpoint); `authChoice.config` must already carry `baseURL`/`model`
26
- * (and optionally `apiKey`) matching `OpenAICompatibleAdapterConfig` — 70a
27
- * leaves this config shape unspecified, so this is this ticket's own
28
- * assumption, not a contract 70a promises. Unlike `local` (which defaults
29
- * endpoint/model), `gateway` has no sensible default, so a missing/empty
30
- * `baseURL`/`model` is a loud `wren-harness`-level error here rather than a
31
- * bare "Invalid URL" deep inside the adapter.
23
+ * - `gateway` — mapped onto the `pi-ai` adapter, pi-ai's unified client for
24
+ * an operator-chosen provider (a built-in pi-ai provider id such as
25
+ * `openrouter` or `amazon-bedrock`, or a custom OpenAI-compatible endpoint
26
+ * when `baseUrl` is set). `authChoice.config` must carry `provider` and
27
+ * `model`, and may carry `apiKey`, `baseUrl` and `headers`
28
+ * (`PiAiAdapterConfig`). `gateway` has no sensible default, so a
29
+ * missing/empty `provider`/`model` is a loud `wren-harness`-level error here
30
+ * rather than a failure deep inside the adapter on the first call.
32
31
  */
33
32
  export function deriveAdapterSpec(authChoice, options = {}) {
34
33
  switch (authChoice.mode) {
@@ -45,14 +44,14 @@ export function deriveAdapterSpec(authChoice, options = {}) {
45
44
  case "gateway": {
46
45
  const config = authChoice.config ?? {};
47
46
  const missing = [];
48
- if (!isNonEmptyString(config["baseURL"]))
49
- missing.push("baseURL");
47
+ if (!isNonEmptyString(config["provider"]))
48
+ missing.push("provider");
50
49
  if (!isNonEmptyString(config["model"]))
51
50
  missing.push("model");
52
51
  if (missing.length > 0) {
53
- throw new Error(`gateway mode requires ${missing.join(" and ")} in config (pass --endpoint and --model)`);
52
+ throw new Error(`gateway mode requires provider and model in config, missing ${missing.join(" and ")} (pass --provider and --model)`);
54
53
  }
55
- return { adapter: OPENAI_COMPATIBLE_ADAPTER_ID, config };
54
+ return { adapter: PI_AI_ADAPTER_ID, config };
56
55
  }
57
56
  }
58
57
  }
@@ -1,6 +1,6 @@
1
1
  const COMPONENT_STEPS = {
2
2
  answer_query: [
3
- { name: "resolve_intent", tier: "cheap", tool: "get_context" },
3
+ { name: "resolve_intent", tier: "cheap", tool: null },
4
4
  { name: "generate_sql", tier: "strong", tool: "run_sql" },
5
5
  { name: "repair_sql", tier: "strong", tool: "run_sql" },
6
6
  ],
@@ -115,6 +115,7 @@ export class CodexAskEventMapper {
115
115
  reference.agentRole !== expectedRole(step.name) ||
116
116
  reference.server !== "wren" ||
117
117
  !reference.itemId ||
118
+ step.tool === null ||
118
119
  reference.tool !== step.tool) {
119
120
  throw new Error("warble-codex-local Ask artifact attribution did not match the allowed Wren MCP surface");
120
121
  }
@@ -8,6 +8,10 @@ export declare function buildCodexAskArgs(cli: ResolvedCli, options: {
8
8
  readonly codexHome: string;
9
9
  readonly models: CodexManifestModels;
10
10
  readonly mcpServer: ResolvedCli;
11
+ /** The genbi stdio proxy Codex starts as its `wren` MCP server (see `wren-host-proxy.ts`). */
12
+ readonly proxy: ResolvedCli;
13
+ /** This turn's host query credential file; the proxy reads it, its path carries no secret. */
14
+ readonly credentialFile: string;
11
15
  readonly codexBin?: string;
12
16
  readonly timeoutMs: number;
13
17
  readonly component?: CodexAskComponent;
@@ -1,13 +1,15 @@
1
1
  import { spawn } from "node:child_process";
2
- import { accessSync, constants as fsConstants } from "node:fs";
3
2
  import { createInterface } from "node:readline";
4
3
  import path from "node:path";
4
+ import { hashDirectory } from "../compile/fingerprint.js";
5
+ import { openWrenComponentAccess } from "../components/wren-access.js";
5
6
  import { enforceCompliance } from "../compliance/index.js";
6
7
  import { compileProfile } from "../compile/pipeline.js";
7
8
  import { createAgentEventEmitter } from "../events/index.js";
8
9
  import { CodexAskEventMapper } from "./codex-ask-events.js";
10
+ import { defaultWrenHostProxy, openHostQueryService, resolveExecutableOnPath, singleWrenExecutable, wrenHostProxyArgs, } from "./host-query.js";
9
11
  import { resolveCodexLocalCli } from "./codex-local-cli.js";
10
- import { CODEX_ASK_COMPONENTS, } from "./codex-local-manifest.js";
12
+ import { CODEX_ASK_COMPONENTS, codexAnalysisToolArgs, } from "./codex-local-manifest.js";
11
13
  const CODEX_BILLING_ENV_KEYS = new Set([
12
14
  "OPENAI_API_KEY",
13
15
  "CODEX_API_KEY",
@@ -21,6 +23,7 @@ const DEFAULT_CODEX_ASK_TIMEOUT_MS = 10 * 60 * 1000;
21
23
  export function buildCodexAskArgs(cli, options) {
22
24
  if (!options.question.trim())
23
25
  throw new Error("Codex Ask question must not be empty");
26
+ const serverArgs = wrenHostProxyArgs(options);
24
27
  return [
25
28
  ...cli.prefixArgs,
26
29
  "dispatch",
@@ -41,20 +44,9 @@ export function buildCodexAskArgs(cli, options) {
41
44
  "--server",
42
45
  "wren",
43
46
  "--server-command",
44
- options.mcpServer.command,
45
- ...options.mcpServer.prefixArgs.flatMap((arg) => arg.startsWith("-") ? [`--server-arg=${arg}`] : ["--server-arg", arg]),
46
- "--server-arg",
47
- "serve",
48
- "--server-arg",
49
- "mcp",
50
- "--server-arg=--project",
51
- "--server-arg",
52
- options.userProject,
53
- "--server-arg=--quiet",
54
- "--inspect-tool",
55
- "get_context",
56
- "--query-tool",
57
- "run_sql",
47
+ options.proxy.command,
48
+ ...serverArgs.flatMap((arg) => arg.startsWith("-") ? [`--server-arg=${arg}`] : ["--server-arg", arg]),
49
+ ...codexAnalysisToolArgs(options.component ?? "answer_query"),
58
50
  "--timeout",
59
51
  String(options.timeoutMs),
60
52
  ...(options.codexBin ? ["--codex-bin", options.codexBin] : []),
@@ -97,21 +89,42 @@ export async function runCodexAskDefault(options) {
97
89
  if (options.signal?.aborted)
98
90
  throw new Error("warble-codex-local Ask was cancelled during preparation");
99
91
  const timeoutMs = options.timeoutMs ?? DEFAULT_CODEX_ASK_TIMEOUT_MS;
100
- const args = buildCodexAskArgs(cli, {
101
- irPath,
102
- question: options.question,
103
- userProject: options.userProject,
104
- codexHome,
105
- models,
106
- mcpServer,
107
- timeoutMs,
108
- component,
109
- ...(options.codexBin !== undefined ? { codexBin: options.codexBin } : {}),
92
+ const userProject = options.userProject;
93
+ // This turn's host query service: the only place codex:local's run_sql executes and the
94
+ // only evidence its answer can be grounded on. Closed (and its observations discarded)
95
+ // when the turn ends, whatever the outcome.
96
+ const hostQueries = await openHostQueryService({
97
+ openAccess: async (signal) => openWrenComponentAccess({
98
+ executable: singleWrenExecutable(mcpServer),
99
+ project: userProject,
100
+ fingerprint: await hashDirectory(userProject),
101
+ signal,
102
+ }),
103
+ ...(options.signal !== undefined ? { signal: options.signal } : {}),
110
104
  });
111
- const mapper = new CodexAskEventMapper(models, component);
112
- const finalText = await spawnCodexAsk(cli.command, args, mapper, emitter.emit, options.processTimeoutMs ?? timeoutMs + 5_000, options.signal);
113
- emitter.emit({ kind: "run.finish", status: "answer" });
114
- return { finalText };
105
+ try {
106
+ const args = buildCodexAskArgs(cli, {
107
+ irPath,
108
+ question: options.question,
109
+ userProject,
110
+ codexHome,
111
+ models,
112
+ mcpServer,
113
+ proxy: defaultWrenHostProxy(),
114
+ credentialFile: hostQueries.credentialFile,
115
+ timeoutMs,
116
+ component,
117
+ ...(options.codexBin !== undefined ? { codexBin: options.codexBin } : {}),
118
+ });
119
+ const mapper = new CodexAskEventMapper(models, component);
120
+ const finalText = await spawnCodexAsk(cli.command, args, mapper, emitter.emit, options.processTimeoutMs ?? timeoutMs + 5_000, options.signal);
121
+ const hostObservations = hostQueries.observations();
122
+ emitter.emit({ kind: "run.finish", status: "answer" });
123
+ return { finalText, hostObservations };
124
+ }
125
+ finally {
126
+ await hostQueries.close();
127
+ }
115
128
  }
116
129
  catch (error) {
117
130
  const message = error instanceof Error ? error.message : String(error);
@@ -127,21 +140,6 @@ function resolveModels(value) {
127
140
  }
128
141
  return models;
129
142
  }
130
- function resolveExecutableOnPath(name) {
131
- for (const directory of (process.env.PATH ?? "").split(path.delimiter)) {
132
- if (!directory)
133
- continue;
134
- const candidate = path.join(directory, name);
135
- try {
136
- accessSync(candidate, fsConstants.X_OK);
137
- return candidate;
138
- }
139
- catch {
140
- // Keep searching.
141
- }
142
- }
143
- throw new Error(`could not find the "${name}" executable on PATH`);
144
- }
145
143
  function sanitizedCodexEnvironment() {
146
144
  return Object.fromEntries(Object.entries(process.env).filter(([key, value]) => value !== undefined && !CODEX_BILLING_ENV_KEYS.has(key.toUpperCase())));
147
145
  }
@@ -9,10 +9,18 @@ export declare const CODEX_ASK_COMPONENTS: readonly ["answer_query", "generate_d
9
9
  export type CodexAskComponent = (typeof CODEX_ASK_COMPONENTS)[number];
10
10
  export declare const CODEX_ENRICH_COMPONENTS: readonly ["inspect_context", "draft_enrichment"];
11
11
  export type CodexManifestPurpose = "analysis" | "setup" | "context_enrichment";
12
+ export declare function codexAnalysisToolArgs(component: CodexAskComponent): string[];
12
13
  export declare function buildCodexAskManifestArgs(cli: ResolvedCli, irPath: string, models: CodexManifestModels, component?: CodexAskComponent): readonly string[];
13
14
  export declare function buildCodexBootstrapManifestArgs(cli: ResolvedCli, irPath: string): readonly string[];
14
15
  export declare function buildCodexEnrichmentManifestArgs(cli: ResolvedCli, irPath: string, models: CodexManifestModels, component: (typeof CODEX_ENRICH_COMPONENTS)[number]): readonly string[];
15
- export declare function describeCodexAskManifest(cli: ResolvedCli, irPath: string, models: CodexManifestModels): Promise<Bundle>;
16
+ export declare function describeCodexAskManifest(cli: ResolvedCli, irPath: string, models: CodexManifestModels, context?: CodexManifestContext): Promise<Bundle>;
17
+ export interface CodexManifestContext {
18
+ readonly warbleBin: string;
19
+ readonly contexts: Readonly<Record<string, {
20
+ readonly binding: unknown;
21
+ readonly snapshot: unknown;
22
+ }>>;
23
+ }
16
24
  /**
17
25
  * Setup is a raw/bootstrap profile, not an Ask profile. Its dispatcher has a
18
26
  * dedicated whole-profile `manifest` command that neither needs nor accepts a