@wrenai/genbi 0.0.1

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 (286) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +141 -0
  3. package/bin/genbi.mjs +31 -0
  4. package/dist/assets/echarts-LTFC-ysd.js +40 -0
  5. package/dist/assets/index-Bo--QMUN.js +441 -0
  6. package/dist/assets/index-Xs4q7pNo.css +1 -0
  7. package/dist/index.html +14 -0
  8. package/dist/wrenai_logo.png +0 -0
  9. package/dist/wrenai_logo_white.png +0 -0
  10. package/dist-server/harness/auth/detect.d.ts +18 -0
  11. package/dist-server/harness/auth/detect.js +37 -0
  12. package/dist-server/harness/auth/index.d.ts +4 -0
  13. package/dist-server/harness/auth/index.js +2 -0
  14. package/dist-server/harness/auth/probe.d.ts +18 -0
  15. package/dist-server/harness/auth/probe.js +52 -0
  16. package/dist-server/harness/auth/types.d.ts +42 -0
  17. package/dist-server/harness/auth/types.js +9 -0
  18. package/dist-server/harness/bundle/loader.d.ts +32 -0
  19. package/dist-server/harness/bundle/loader.js +43 -0
  20. package/dist-server/harness/bundle/schema.d.ts +199 -0
  21. package/dist-server/harness/bundle/schema.js +127 -0
  22. package/dist-server/harness/bundle/version.d.ts +10 -0
  23. package/dist-server/harness/bundle/version.js +43 -0
  24. package/dist-server/harness/capability/gate.d.ts +6 -0
  25. package/dist-server/harness/capability/gate.js +29 -0
  26. package/dist-server/harness/capability/registry.d.ts +6 -0
  27. package/dist-server/harness/capability/registry.js +17 -0
  28. package/dist-server/harness/cli-args.d.ts +123 -0
  29. package/dist-server/harness/cli-args.js +247 -0
  30. package/dist-server/harness/cli.d.ts +2 -0
  31. package/dist-server/harness/cli.js +151 -0
  32. package/dist-server/harness/compile/cache.d.ts +23 -0
  33. package/dist-server/harness/compile/cache.js +109 -0
  34. package/dist-server/harness/compile/compose-profile.d.ts +34 -0
  35. package/dist-server/harness/compile/compose-profile.js +95 -0
  36. package/dist-server/harness/compile/errors.d.ts +22 -0
  37. package/dist-server/harness/compile/errors.js +41 -0
  38. package/dist-server/harness/compile/fingerprint.d.ts +22 -0
  39. package/dist-server/harness/compile/fingerprint.js +84 -0
  40. package/dist-server/harness/compile/index.d.ts +8 -0
  41. package/dist-server/harness/compile/index.js +7 -0
  42. package/dist-server/harness/compile/pipeline.d.ts +66 -0
  43. package/dist-server/harness/compile/pipeline.js +176 -0
  44. package/dist-server/harness/compile/resolve-binary.d.ts +55 -0
  45. package/dist-server/harness/compile/resolve-binary.js +133 -0
  46. package/dist-server/harness/compile/types.d.ts +107 -0
  47. package/dist-server/harness/compile/types.js +1 -0
  48. package/dist-server/harness/compile/warble-identity.d.ts +12 -0
  49. package/dist-server/harness/compile/warble-identity.js +33 -0
  50. package/dist-server/harness/compliance/gate.d.ts +35 -0
  51. package/dist-server/harness/compliance/gate.js +48 -0
  52. package/dist-server/harness/compliance/index.d.ts +2 -0
  53. package/dist-server/harness/compliance/index.js +1 -0
  54. package/dist-server/harness/compliance/types.d.ts +16 -0
  55. package/dist-server/harness/compliance/types.js +1 -0
  56. package/dist-server/harness/events/emitter.d.ts +34 -0
  57. package/dist-server/harness/events/emitter.js +25 -0
  58. package/dist-server/harness/events/index.d.ts +4 -0
  59. package/dist-server/harness/events/index.js +2 -0
  60. package/dist-server/harness/events/sse.d.ts +20 -0
  61. package/dist-server/harness/events/sse.js +48 -0
  62. package/dist-server/harness/events/types.d.ts +131 -0
  63. package/dist-server/harness/events/types.js +1 -0
  64. package/dist-server/harness/exec/errors.d.ts +16 -0
  65. package/dist-server/harness/exec/errors.js +29 -0
  66. package/dist-server/harness/exec/index.d.ts +5 -0
  67. package/dist-server/harness/exec/index.js +2 -0
  68. package/dist-server/harness/exec/local.d.ts +32 -0
  69. package/dist-server/harness/exec/local.js +134 -0
  70. package/dist-server/harness/exec/policy.d.ts +12 -0
  71. package/dist-server/harness/exec/policy.js +1 -0
  72. package/dist-server/harness/exec/types.d.ts +63 -0
  73. package/dist-server/harness/exec/types.js +1 -0
  74. package/dist-server/harness/guardrails/index.d.ts +2 -0
  75. package/dist-server/harness/guardrails/index.js +1 -0
  76. package/dist-server/harness/guardrails/policy.d.ts +45 -0
  77. package/dist-server/harness/guardrails/policy.js +57 -0
  78. package/dist-server/harness/index.d.ts +37 -0
  79. package/dist-server/harness/index.js +19 -0
  80. package/dist-server/harness/loop/errors.d.ts +53 -0
  81. package/dist-server/harness/loop/errors.js +72 -0
  82. package/dist-server/harness/loop/executor.d.ts +123 -0
  83. package/dist-server/harness/loop/executor.js +380 -0
  84. package/dist-server/harness/loop/guard.d.ts +16 -0
  85. package/dist-server/harness/loop/guard.js +1 -0
  86. package/dist-server/harness/loop/index.d.ts +5 -0
  87. package/dist-server/harness/loop/index.js +3 -0
  88. package/dist-server/harness/native-producer/index.d.ts +109 -0
  89. package/dist-server/harness/native-producer/index.js +302 -0
  90. package/dist-server/harness/npm-package-resolve.d.ts +28 -0
  91. package/dist-server/harness/npm-package-resolve.js +47 -0
  92. package/dist-server/harness/providers/adapters/anthropic.d.ts +7 -0
  93. package/dist-server/harness/providers/adapters/anthropic.js +6 -0
  94. package/dist-server/harness/providers/adapters/mock.d.ts +4 -0
  95. package/dist-server/harness/providers/adapters/mock.js +5 -0
  96. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +10 -0
  97. package/dist-server/harness/providers/adapters/openai-compatible.js +10 -0
  98. package/dist-server/harness/providers/binding.d.ts +23 -0
  99. package/dist-server/harness/providers/binding.js +13 -0
  100. package/dist-server/harness/providers/errors.d.ts +6 -0
  101. package/dist-server/harness/providers/errors.js +12 -0
  102. package/dist-server/harness/providers/index.d.ts +18 -0
  103. package/dist-server/harness/providers/index.js +22 -0
  104. package/dist-server/harness/providers/registry.d.ts +16 -0
  105. package/dist-server/harness/providers/registry.js +24 -0
  106. package/dist-server/harness/render/envelope.d.ts +145 -0
  107. package/dist-server/harness/render/envelope.js +428 -0
  108. package/dist-server/harness/render/errors.d.ts +33 -0
  109. package/dist-server/harness/render/errors.js +46 -0
  110. package/dist-server/harness/render/index.d.ts +5 -0
  111. package/dist-server/harness/render/index.js +3 -0
  112. package/dist-server/harness/render/validate.d.ts +13 -0
  113. package/dist-server/harness/render/validate.js +88 -0
  114. package/dist-server/harness/route/adapter-spec.d.ts +40 -0
  115. package/dist-server/harness/route/adapter-spec.js +61 -0
  116. package/dist-server/harness/route/agent-sdk-cli.d.ts +30 -0
  117. package/dist-server/harness/route/agent-sdk-cli.js +86 -0
  118. package/dist-server/harness/route/agent-sdk-manifest.d.ts +31 -0
  119. package/dist-server/harness/route/agent-sdk-manifest.js +56 -0
  120. package/dist-server/harness/route/artifact-content.d.ts +53 -0
  121. package/dist-server/harness/route/artifact-content.js +83 -0
  122. package/dist-server/harness/route/chat-event-mapper.d.ts +95 -0
  123. package/dist-server/harness/route/chat-event-mapper.js +164 -0
  124. package/dist-server/harness/route/codex-ask-events.d.ts +25 -0
  125. package/dist-server/harness/route/codex-ask-events.js +395 -0
  126. package/dist-server/harness/route/codex-ask.d.ts +15 -0
  127. package/dist-server/harness/route/codex-ask.js +242 -0
  128. package/dist-server/harness/route/codex-local-cli.d.ts +20 -0
  129. package/dist-server/harness/route/codex-local-cli.js +78 -0
  130. package/dist-server/harness/route/codex-local-manifest.d.ts +22 -0
  131. package/dist-server/harness/route/codex-local-manifest.js +180 -0
  132. package/dist-server/harness/route/describe.d.ts +54 -0
  133. package/dist-server/harness/route/describe.js +84 -0
  134. package/dist-server/harness/route/dispatched.d.ts +138 -0
  135. package/dist-server/harness/route/dispatched.js +339 -0
  136. package/dist-server/harness/route/in-process.d.ts +43 -0
  137. package/dist-server/harness/route/in-process.js +106 -0
  138. package/dist-server/harness/route/index.d.ts +19 -0
  139. package/dist-server/harness/route/index.js +12 -0
  140. package/dist-server/harness/route/profile-source.d.ts +37 -0
  141. package/dist-server/harness/route/profile-source.js +76 -0
  142. package/dist-server/harness/route/route.d.ts +33 -0
  143. package/dist-server/harness/route/route.js +114 -0
  144. package/dist-server/harness/route/tier-binding.d.ts +36 -0
  145. package/dist-server/harness/route/tier-binding.js +63 -0
  146. package/dist-server/harness/route/types.d.ts +286 -0
  147. package/dist-server/harness/route/types.js +1 -0
  148. package/dist-server/harness/session/errors.d.ts +4 -0
  149. package/dist-server/harness/session/errors.js +7 -0
  150. package/dist-server/harness/session/gate.d.ts +11 -0
  151. package/dist-server/harness/session/gate.js +13 -0
  152. package/dist-server/harness/session/index.d.ts +5 -0
  153. package/dist-server/harness/session/index.js +3 -0
  154. package/dist-server/harness/session/run.d.ts +60 -0
  155. package/dist-server/harness/session/run.js +333 -0
  156. package/dist-server/harness/session/types.d.ts +29 -0
  157. package/dist-server/harness/session/types.js +1 -0
  158. package/dist-server/harness/setup/codex-events.d.ts +12 -0
  159. package/dist-server/harness/setup/codex-events.js +108 -0
  160. package/dist-server/harness/setup/runner.d.ts +497 -0
  161. package/dist-server/harness/setup/runner.js +1314 -0
  162. package/dist-server/harness/tools/errors.d.ts +107 -0
  163. package/dist-server/harness/tools/errors.js +141 -0
  164. package/dist-server/harness/tools/index.d.ts +13 -0
  165. package/dist-server/harness/tools/index.js +8 -0
  166. package/dist-server/harness/tools/mcp.d.ts +26 -0
  167. package/dist-server/harness/tools/mcp.js +15 -0
  168. package/dist-server/harness/tools/native.d.ts +179 -0
  169. package/dist-server/harness/tools/native.js +322 -0
  170. package/dist-server/harness/tools/parse.d.ts +14 -0
  171. package/dist-server/harness/tools/parse.js +18 -0
  172. package/dist-server/harness/tools/resolve-wren-binary.d.ts +12 -0
  173. package/dist-server/harness/tools/resolve-wren-binary.js +21 -0
  174. package/dist-server/harness/tools/resolve.d.ts +51 -0
  175. package/dist-server/harness/tools/resolve.js +79 -0
  176. package/dist-server/harness/tools/setup-native.d.ts +84 -0
  177. package/dist-server/harness/tools/setup-native.js +353 -0
  178. package/dist-server/harness/tools/wiring.d.ts +10 -0
  179. package/dist-server/harness/tools/wiring.js +16 -0
  180. package/dist-server/providers/setup.provider.yaml +31 -0
  181. package/dist-server/providers/wren.provider.yaml +33 -0
  182. package/dist-server/server/adopt.d.ts +87 -0
  183. package/dist-server/server/adopt.js +263 -0
  184. package/dist-server/server/app.d.ts +6 -0
  185. package/dist-server/server/app.js +2521 -0
  186. package/dist-server/server/auth-choice.d.ts +16 -0
  187. package/dist-server/server/auth-choice.js +36 -0
  188. package/dist-server/server/bin.d.ts +2 -0
  189. package/dist-server/server/bin.js +601 -0
  190. package/dist-server/server/bootstrap-project-binding.d.ts +31 -0
  191. package/dist-server/server/bootstrap-project-binding.js +71 -0
  192. package/dist-server/server/clarify.d.ts +10 -0
  193. package/dist-server/server/clarify.js +17 -0
  194. package/dist-server/server/codex-setup-mcp.d.ts +2 -0
  195. package/dist-server/server/codex-setup-mcp.js +147 -0
  196. package/dist-server/server/compose.d.ts +89 -0
  197. package/dist-server/server/compose.js +271 -0
  198. package/dist-server/server/conn-config.d.ts +30 -0
  199. package/dist-server/server/conn-config.js +253 -0
  200. package/dist-server/server/context-files.d.ts +12 -0
  201. package/dist-server/server/context-files.js +117 -0
  202. package/dist-server/server/context-map.d.ts +30 -0
  203. package/dist-server/server/context-map.js +95 -0
  204. package/dist-server/server/context-source.d.ts +68 -0
  205. package/dist-server/server/context-source.js +85 -0
  206. package/dist-server/server/db.d.ts +862 -0
  207. package/dist-server/server/db.js +1839 -0
  208. package/dist-server/server/enrichment-runner.d.ts +184 -0
  209. package/dist-server/server/enrichment-runner.js +213 -0
  210. package/dist-server/server/enrichment-verify.d.ts +73 -0
  211. package/dist-server/server/enrichment-verify.js +290 -0
  212. package/dist-server/server/enrichment.d.ts +232 -0
  213. package/dist-server/server/enrichment.js +199 -0
  214. package/dist-server/server/env-detect.d.ts +8 -0
  215. package/dist-server/server/env-detect.js +17 -0
  216. package/dist-server/server/env-field-hints.d.ts +46 -0
  217. package/dist-server/server/env-field-hints.js +48 -0
  218. package/dist-server/server/fold.d.ts +133 -0
  219. package/dist-server/server/fold.js +407 -0
  220. package/dist-server/server/harness-profile-sources.d.ts +3 -0
  221. package/dist-server/server/harness-profile-sources.js +24 -0
  222. package/dist-server/server/harness.d.ts +19 -0
  223. package/dist-server/server/harness.js +439 -0
  224. package/dist-server/server/impact.d.ts +18 -0
  225. package/dist-server/server/impact.js +85 -0
  226. package/dist-server/server/interactive-terminal.d.ts +175 -0
  227. package/dist-server/server/interactive-terminal.js +336 -0
  228. package/dist-server/server/native-artifacts.d.ts +1004 -0
  229. package/dist-server/server/native-artifacts.js +564 -0
  230. package/dist-server/server/native-dispatch-registry.d.ts +42 -0
  231. package/dist-server/server/native-dispatch-registry.js +45 -0
  232. package/dist-server/server/native-producer-fence.d.ts +14 -0
  233. package/dist-server/server/native-producer-fence.js +48 -0
  234. package/dist-server/server/native-session-resume.d.ts +6 -0
  235. package/dist-server/server/native-session-resume.js +103 -0
  236. package/dist-server/server/native-session-workspace.d.ts +40 -0
  237. package/dist-server/server/native-session-workspace.js +175 -0
  238. package/dist-server/server/native-sessions.d.ts +374 -0
  239. package/dist-server/server/native-sessions.js +1517 -0
  240. package/dist-server/server/native-wren-runtime.d.ts +20 -0
  241. package/dist-server/server/native-wren-runtime.js +259 -0
  242. package/dist-server/server/node-pty-host.d.ts +31 -0
  243. package/dist-server/server/node-pty-host.js +100 -0
  244. package/dist-server/server/route-intent.d.ts +40 -0
  245. package/dist-server/server/route-intent.js +23 -0
  246. package/dist-server/server/runtime-binding.d.ts +52 -0
  247. package/dist-server/server/runtime-binding.js +258 -0
  248. package/dist-server/server/source-catalog.d.ts +84 -0
  249. package/dist-server/server/source-catalog.js +221 -0
  250. package/dist-server/server/source-driver.d.ts +35 -0
  251. package/dist-server/server/source-driver.js +124 -0
  252. package/dist-server/server/spa.d.ts +2 -0
  253. package/dist-server/server/spa.js +51 -0
  254. package/dist-server/server/subscription-model-catalog.d.ts +27 -0
  255. package/dist-server/server/subscription-model-catalog.js +203 -0
  256. package/dist-server/server/turn.d.ts +249 -0
  257. package/dist-server/server/turn.js +1074 -0
  258. package/dist-server/server/wire-types.d.ts +799 -0
  259. package/dist-server/server/wire-types.js +1 -0
  260. package/dist-server/server/wren-home.d.ts +19 -0
  261. package/dist-server/server/wren-home.js +33 -0
  262. package/dist-server/server/wren-profiles.d.ts +43 -0
  263. package/dist-server/server/wren-profiles.js +123 -0
  264. package/package.json +93 -0
  265. package/profiles/genbi-default/context/binding.yml +9 -0
  266. package/profiles/genbi-default/ir.golden.json +506 -0
  267. package/profiles/genbi-default/profile.yml +16 -0
  268. package/profiles/genbi-enrich-context/components/apply_enrichment/component.yml +42 -0
  269. package/profiles/genbi-enrich-context/components/draft_enrichment/component.yml +30 -0
  270. package/profiles/genbi-enrich-context/components/draft_enrichment/steps/draft.md +34 -0
  271. package/profiles/genbi-enrich-context/components/inspect_context/component.yml +32 -0
  272. package/profiles/genbi-enrich-context/components/inspect_context/steps/inspect.md +13 -0
  273. package/profiles/genbi-enrich-context/context/binding.yml +4 -0
  274. package/profiles/genbi-enrich-context/ir.golden.json +302 -0
  275. package/profiles/genbi-enrich-context/profile.yml +15 -0
  276. package/profiles/genbi-monitor/context/binding.yml +7 -0
  277. package/profiles/genbi-monitor/ir.golden.json +188 -0
  278. package/profiles/genbi-monitor/profile.yml +27 -0
  279. package/profiles/genbi-setup/components/build_context/component.yml +44 -0
  280. package/profiles/genbi-setup/components/build_context/steps/build.md +14 -0
  281. package/profiles/genbi-setup/components/connect_source/component.yml +51 -0
  282. package/profiles/genbi-setup/components/connect_source/steps/connect.md +23 -0
  283. package/profiles/genbi-setup/context/binding.yml +8 -0
  284. package/profiles/genbi-setup/ir.golden.json +140 -0
  285. package/profiles/genbi-setup/profile.yml +18 -0
  286. package/profiles/genbi-setup/raw/schema.json +5 -0
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Real (non-mock) `EnrichmentRunner` implementation for the draft path.
3
+ *
4
+ * Dispatches warble's `genbi-enrich-context` profile — `inspect_context`
5
+ * then `draft_enrichment` — via dispatched (the Claude Agent SDK dispatcher),
6
+ * one subprocess per component. `warble-agent-sdk chat` is a single
7
+ * line-per-turn stdin protocol (see `harness/route/dispatched.ts`'s
8
+ * `spawnChat`/`buildAgentSdkChatArgs`), so a two-component flow is two
9
+ * separate dispatches: this module folds turn 1's finalText into turn 2's
10
+ * composed question itself — there is no SDK-level session chaining that
11
+ * could do this for us within one `draft()` call.
12
+ *
13
+ * Translation, not policy. `server/enrichment.ts`'s `canonicalizeProposal`
14
+ * is the only place a draft becomes a trusted `EnrichmentProposal`: it
15
+ * ignores any `id`/`hash`/`projectRevision`/`risk` the model supplies and
16
+ * independently recomputes them. This module's `translateDraftTerminal`
17
+ * adds a second, independent layer of the same discipline by construction:
18
+ * it copies over only five named fields (`sink`, `changeKind`, `summary`,
19
+ * `draft`, `confidence`) from the model's parsed JSON, so a forged
20
+ * `hash`/`id`/`risk`/`projectRevision` the model includes never even
21
+ * reaches `canonicalizeProposal` in the first place.
22
+ *
23
+ * Vocabulary seam: warble's `draft_enrichment` component (steps/draft.md)
24
+ * only ever describes the operation envelope in prose — `relative_sink`,
25
+ * `recommended_yaml`, confidence/evidence — and is silent on a literal
26
+ * `changeKind`/`summary` field, because those are GenBI-host vocabulary
27
+ * (`EnrichmentChangeKind`), not something warble's own contract shares with
28
+ * the model. `composeDraftPrompt` below is where that gap is closed: it
29
+ * layers an additional host-owned instruction on top of the dispatched
30
+ * component's own step prompt, exactly the way `server/compose.ts`'s
31
+ * `composeSetupPrompt` layers its own `SETUP_STATUS: ok|needs_input|error`
32
+ * terminal convention on top of `connect_source`, "since warble's
33
+ * `connect_source` component carries no terminal-status convention of its
34
+ * own." Neither wrapper prompt edits or contradicts the dispatched
35
+ * component's own instructions.
36
+ */
37
+ import type { AgentEvent, AuthChoice } from "../harness/index.js";
38
+ import { type EnrichmentMode, type EnrichmentRunner } from "./enrichment.js";
39
+ export declare const INSPECT_CONTEXT_AGENT_ID = "inspect_context";
40
+ export declare const DRAFT_ENRICHMENT_AGENT_ID = "draft_enrichment";
41
+ /** One dispatched turn: an `agentId` (warble component) + composed `question`, against a bound project. */
42
+ export interface EnrichmentDispatchInput {
43
+ readonly agentId: string;
44
+ readonly question: string;
45
+ readonly userProject: string;
46
+ }
47
+ export interface EnrichmentDispatchResult {
48
+ readonly finalText: string;
49
+ }
50
+ /** Test-only seam: replaces the two real dispatched subprocess dispatches. Never set in production. */
51
+ export type EnrichmentDispatch = (input: EnrichmentDispatchInput) => Promise<EnrichmentDispatchResult>;
52
+ export interface DispatchedEnrichmentDraftRunnerOptions {
53
+ /**
54
+ * Live auth-choice read, not a value frozen at construction. `EnrichmentRunner.draft()`
55
+ * (`server/enrichment.ts`) takes no `authChoice` parameter of its own — unlike
56
+ * `SetupStepRunner.run()`, which does — so this is this module's own seam for staying current
57
+ * with a later `PUT /api/config/runtime` rebind (`server/turn.ts`'s `TurnDeps.getAuthChoice`)
58
+ * instead of dispatching every future draft call against whatever auth choice happened to be
59
+ * active when `server/bin.ts` constructed this runner at boot.
60
+ */
61
+ readonly getAuthChoice: () => AuthChoice;
62
+ /** Defaults to `resolveDefaultEnrichIrPath()` (the committed `profiles/genbi-enrich-context/ir.golden.json`). */
63
+ readonly irPath?: string;
64
+ readonly warbleBin?: string;
65
+ readonly agentSdkBin?: string;
66
+ readonly outDir?: string;
67
+ /**
68
+ * Mirrors `DispatchedSetupRunnerOptions.getModelsConfig` — a live read, not a value frozen at
69
+ * construction, so a later `PUT /api/config/runtime` rebind is picked up by the next draft
70
+ * call instead of requiring a process restart.
71
+ */
72
+ /**
73
+ * Directory of the genbi-enrich-context profile. When set, the draft is
74
+ * Dispatched from an IR compiled against the bound project — which is the
75
+ * only way the agent's injected context describes the project it is about to
76
+ * propose changes to.
77
+ *
78
+ * Without it the runner falls back to `irPath`, a prebuilt IR. That is how
79
+ * this ran until now, and the prebuilt IR available to it was the profile's
80
+ * eval golden: the profile compiled against warble's own example project,
81
+ * with jaffle-shop's models baked into `context_binding.resolved`. A live
82
+ * draft duly analysed gaps in someone else's schema.
83
+ */
84
+ readonly profileSource?: string;
85
+ readonly getModelsConfig?: () => string | undefined;
86
+ readonly chatTimeoutMs?: number;
87
+ readonly maxTurns?: number;
88
+ readonly onEvent?: (event: AgentEvent) => void;
89
+ /** Test-only seam. See `EnrichmentDispatch`. Never set in production wiring (`server/bin.ts`). */
90
+ readonly dispatch?: EnrichmentDispatch;
91
+ }
92
+ /**
93
+ * Which profile input `runDispatchedDefault` is given, and whether it is allowed to
94
+ * skip compilation.
95
+ *
96
+ * Passing `irPath` short-circuits `compileProfile`, which is how a prebuilt
97
+ * golden — the profile compiled against warble's own example project — used to
98
+ * reach the agent. With a profile source we want the opposite: compile, so the
99
+ * Dispatched IR's context is the bound project's.
100
+ *
101
+ * Exported because that branch is the whole point of the fix and is otherwise
102
+ * only reachable through a real dispatch.
103
+ */
104
+ export declare function draftProfileInput(profileSource: string | undefined, irPath: string): {
105
+ profileSource: string;
106
+ irPath?: string;
107
+ };
108
+ /**
109
+ * `inspect_context` has no formal params and no dynamic per-run content to
110
+ * restate (unlike Setup's `connect`/`context` steps, which restate a
111
+ * project name / source type the warble component has no other way to
112
+ * learn). The only thing this run-specific wrapper needs to convey is the
113
+ * mode, because `steps/inspect.md` itself makes probe permission mode
114
+ * dependent ("In grill mode, live database probes are allowed only after
115
+ * the host records the user's one-time consent; in autopilot they are
116
+ * forbidden"). This host does not yet implement a consent-recording
117
+ * mechanism, so it must say so honestly rather than silently imply consent
118
+ * was granted — asserting an unrecorded consent is exactly the kind of
119
+ * disprovable claim this codebase has been burned by before.
120
+ */
121
+ export declare function composeInspectPrompt(mode: EnrichmentMode): string;
122
+ /**
123
+ * Layers two host-owned requirements on top of `draft_enrichment`'s own
124
+ * `steps/draft.md` instructions, which never contradict: (1) the pinned
125
+ * project revision this run is locked to, restated so the model treats it
126
+ * as input rather than inferring one of its own (draft.md already demands
127
+ * this, this just supplies the concrete value); (2) a `changeKind` and a
128
+ * `summary` field on every operation — vocabulary the host's own
129
+ * `EnrichmentChangeKind` type needs to classify and route the sink, that
130
+ * warble's component prose never names. Everything else (field names for
131
+ * the sink/body themselves, the single-operation-plus-decision shape for
132
+ * grill, the cube schema, the append-only/no-overwrite rules) is exactly
133
+ * what draft.md already tells the model — restating it here would be
134
+ * redundant, not additive.
135
+ */
136
+ export declare function composeDraftPrompt(mode: EnrichmentMode, projectRevision: string, gapInventory: string): string;
137
+ /**
138
+ * Parses the model's bare-JSON final message. draft.md's instruction is
139
+ * unambiguous ("Your FINAL message must be one JSON object only. Do not
140
+ * include prose or Markdown fences"), and the compiled golden the runtime
141
+ * actually dispatches matches it byte for byte -- so a fenced terminal is
142
+ * model variance, not prompt drift, and the durable fix is host-side
143
+ * tolerance, not a prompt rewrite (see
144
+ * `test/draft-enrichment-fenced-terminal.integration.test.ts`, pinned
145
+ * against a real recorded run whose terminal was prose followed by a
146
+ * ```json fenced block).
147
+ *
148
+ * The fence match is intentionally NOT anchored to the whole trimmed
149
+ * string, so a fence preceded (or followed) by prose is tolerated the same
150
+ * as a bare fence or no fence at all. This duplicates the fence-matching
151
+ * approach `harness/render/envelope.ts`'s `extractJsonObjectText` already
152
+ * uses for the unrelated answer-rendering path, rather than importing that
153
+ * function directly: it is private to that module, and its extra
154
+ * brace-scanning fallback (for free-form, unfenced prose) is machinery this
155
+ * caller doesn't need -- a bare JSON terminal here already round-trips
156
+ * through `JSON.parse` below with no fence at all.
157
+ */
158
+ export declare function parseDraftTerminalJson(finalText: string): unknown;
159
+ /**
160
+ * Normalizes warble's model-facing terminal shape (`{ enrichment_proposal:
161
+ * {...} }`, a single operation directly under `enrichment_proposal` for
162
+ * grill per draft.md's spec, or an `operations` array for a multi-operation
163
+ * flow) into the shape `canonicalizeProposal` (server/enrichment.ts)
164
+ * accepts: `{ operations: [{ sink, changeKind, summary, draft, confidence
165
+ * }] }`. Copies over ONLY those five fields, by name, from whichever of the
166
+ * model's own field names (`relative_sink`/`recommended_yaml`, this
167
+ * module's added `changeKind`/`summary`) or the host's own names
168
+ * (`sink`/`draft`) the model actually used — so a forged `hash`, `id`,
169
+ * `projectRevision`, or `risk` the model includes anywhere in its JSON is
170
+ * silently dropped here and never reaches `canonicalizeProposal` at all.
171
+ */
172
+ export declare function translateDraftTerminal(finalText: string): unknown;
173
+ /**
174
+ * Builds the real, production `EnrichmentRunner.draft()`: two dispatched
175
+ * dispatches (`inspect_context` then `draft_enrichment`) against the bound
176
+ * project, folding turn 1's output into turn 2's composed question, then
177
+ * translating the terminal into the shape the host's own
178
+ * `canonicalizeProposal` independently validates and hashes. Returns
179
+ * `unknown`, exactly like the interface requires — this function never
180
+ * calls `canonicalizeProposal` itself; that stays the caller's (the `/api/
181
+ * context/enrichment/*` route's) job, same as every mock runner in
182
+ * `test/bff-enrichment.test.ts`.
183
+ */
184
+ export declare function createDispatchedEnrichmentDraftRunner(options: DispatchedEnrichmentDraftRunnerOptions): EnrichmentRunner;
@@ -0,0 +1,213 @@
1
+ import { resolveDefaultEnrichIrPath, runDispatchedDefault } from "../harness/index.js";
2
+ import { EnrichmentContractError } from "./enrichment.js";
3
+ /**
4
+ * The one live refusal condition `readiness()` and `defaultDispatch` share --
5
+ * this dispatch path only ever talks to Claude via dispatched, so anything else
6
+ * is a refusal, not a routing choice. Kept as a single function so the two
7
+ * call sites (the readiness check below, and `defaultDispatch`'s own guard)
8
+ * can never drift apart from each other; the reason code is also exactly
9
+ * what `readiness()` hands to the host route, which forwards it verbatim.
10
+ */
11
+ const REQUIRES_CLAUDE_SUBSCRIPTION = "requires_claude_subscription";
12
+ /**
13
+ * A type guard (not just a boolean) so `defaultDispatch`'s own guard clause
14
+ * keeps narrowing `authChoice` to what `runDispatchedDefault` requires -- the
15
+ * single source of truth `draftAuthReadiness` and `defaultDispatch` both
16
+ * call, so the two can never drift apart.
17
+ */
18
+ function isClaudeSubscriptionAuth(authChoice) {
19
+ return authChoice.mode === "subscription" && authChoice.provider === "claude";
20
+ }
21
+ function draftAuthReadiness(authChoice) {
22
+ return isClaudeSubscriptionAuth(authChoice) ? { available: true } : { available: false, reason: REQUIRES_CLAUDE_SUBSCRIPTION };
23
+ }
24
+ export const INSPECT_CONTEXT_AGENT_ID = "inspect_context";
25
+ export const DRAFT_ENRICHMENT_AGENT_ID = "draft_enrichment";
26
+ /**
27
+ * Which profile input `runDispatchedDefault` is given, and whether it is allowed to
28
+ * skip compilation.
29
+ *
30
+ * Passing `irPath` short-circuits `compileProfile`, which is how a prebuilt
31
+ * golden — the profile compiled against warble's own example project — used to
32
+ * reach the agent. With a profile source we want the opposite: compile, so the
33
+ * Dispatched IR's context is the bound project's.
34
+ *
35
+ * Exported because that branch is the whole point of the fix and is otherwise
36
+ * only reachable through a real dispatch.
37
+ */
38
+ export function draftProfileInput(profileSource, irPath) {
39
+ return profileSource === undefined ? { profileSource: irPath, irPath } : { profileSource };
40
+ }
41
+ function defaultDispatch(options) {
42
+ return async ({ agentId, question, userProject }) => {
43
+ const irPath = options.irPath ?? resolveDefaultEnrichIrPath();
44
+ if (irPath === undefined) {
45
+ throw new EnrichmentContractError("no genbi-enrich-context IR found (searched ancestors for a sibling warble checkout); " +
46
+ "pass an explicit irPath or check out warble as a sibling repo");
47
+ }
48
+ const authChoice = options.getAuthChoice();
49
+ if (!isClaudeSubscriptionAuth(authChoice)) {
50
+ throw new EnrichmentContractError(`enrichment draft dispatch requires Claude subscription auth, got mode "${authChoice.mode}"` +
51
+ (authChoice.mode === "subscription" ? ` provider "${authChoice.provider}"` : ""));
52
+ }
53
+ const modelsConfig = options.getModelsConfig?.();
54
+ const result = await runDispatchedDefault({
55
+ authChoice,
56
+ ...draftProfileInput(options.profileSource, irPath),
57
+ userProject,
58
+ question,
59
+ agentId,
60
+ ...(options.warbleBin !== undefined ? { warbleBin: options.warbleBin } : {}),
61
+ ...(options.agentSdkBin !== undefined ? { agentSdkBin: options.agentSdkBin } : {}),
62
+ ...(options.outDir !== undefined ? { outDir: options.outDir } : {}),
63
+ ...(modelsConfig !== undefined ? { modelsConfig } : {}),
64
+ ...(options.chatTimeoutMs !== undefined ? { chatTimeoutMs: options.chatTimeoutMs } : {}),
65
+ ...(options.maxTurns !== undefined ? { maxTurns: options.maxTurns } : {}),
66
+ ...(options.onEvent !== undefined ? { onEvent: options.onEvent } : {}),
67
+ });
68
+ return { finalText: result.finalText };
69
+ };
70
+ }
71
+ /**
72
+ * `inspect_context` has no formal params and no dynamic per-run content to
73
+ * restate (unlike Setup's `connect`/`context` steps, which restate a
74
+ * project name / source type the warble component has no other way to
75
+ * learn). The only thing this run-specific wrapper needs to convey is the
76
+ * mode, because `steps/inspect.md` itself makes probe permission mode
77
+ * dependent ("In grill mode, live database probes are allowed only after
78
+ * the host records the user's one-time consent; in autopilot they are
79
+ * forbidden"). This host does not yet implement a consent-recording
80
+ * mechanism, so it must say so honestly rather than silently imply consent
81
+ * was granted — asserting an unrecorded consent is exactly the kind of
82
+ * disprovable claim this codebase has been burned by before.
83
+ */
84
+ export function composeInspectPrompt(mode) {
85
+ const probeRule = mode === "grill"
86
+ ? "No one-time database-probe consent has been recorded for this run, so do not perform any live database probe — rely only on already-materialized MDL, cube, knowledge, and NL-to-SQL content."
87
+ : "Autopilot mode: live database probes are forbidden — rely only on already-materialized MDL, cube, knowledge, and NL-to-SQL content.";
88
+ return `Begin the enrichment inventory now. Run mode is "${mode}". ${probeRule} Produce the enrichment_gaps inventory exactly as instructed.`;
89
+ }
90
+ /**
91
+ * Layers two host-owned requirements on top of `draft_enrichment`'s own
92
+ * `steps/draft.md` instructions, which never contradict: (1) the pinned
93
+ * project revision this run is locked to, restated so the model treats it
94
+ * as input rather than inferring one of its own (draft.md already demands
95
+ * this, this just supplies the concrete value); (2) a `changeKind` and a
96
+ * `summary` field on every operation — vocabulary the host's own
97
+ * `EnrichmentChangeKind` type needs to classify and route the sink, that
98
+ * warble's component prose never names. Everything else (field names for
99
+ * the sink/body themselves, the single-operation-plus-decision shape for
100
+ * grill, the cube schema, the append-only/no-overwrite rules) is exactly
101
+ * what draft.md already tells the model — restating it here would be
102
+ * redundant, not additive.
103
+ */
104
+ export function composeDraftPrompt(mode, projectRevision, gapInventory) {
105
+ const changeKinds = "knowledge_append, new_cube, new_view, new_relationship, mdl_metric, calculated_column, conflict, ambiguous";
106
+ return (`Draft the enrichment proposal now for project revision "${projectRevision}", run mode "${mode}". ` +
107
+ `In addition to the fields your own instructions already describe, every operation object must also carry ` +
108
+ `a "changeKind" field set to exactly one of: ${changeKinds}; and a "summary" field: a short, one-line, ` +
109
+ `human-readable description of the change with no raw excerpts or credentials. Do not invent or claim a ` +
110
+ `hash, digest, id, or risk label for any operation — the host computes and independently verifies all of ` +
111
+ `those and will discard any you include. Gap inventory from the inspection step:\n${gapInventory}`);
112
+ }
113
+ function record(value) {
114
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
115
+ }
116
+ /**
117
+ * Parses the model's bare-JSON final message. draft.md's instruction is
118
+ * unambiguous ("Your FINAL message must be one JSON object only. Do not
119
+ * include prose or Markdown fences"), and the compiled golden the runtime
120
+ * actually dispatches matches it byte for byte -- so a fenced terminal is
121
+ * model variance, not prompt drift, and the durable fix is host-side
122
+ * tolerance, not a prompt rewrite (see
123
+ * `test/draft-enrichment-fenced-terminal.integration.test.ts`, pinned
124
+ * against a real recorded run whose terminal was prose followed by a
125
+ * ```json fenced block).
126
+ *
127
+ * The fence match is intentionally NOT anchored to the whole trimmed
128
+ * string, so a fence preceded (or followed) by prose is tolerated the same
129
+ * as a bare fence or no fence at all. This duplicates the fence-matching
130
+ * approach `harness/render/envelope.ts`'s `extractJsonObjectText` already
131
+ * uses for the unrelated answer-rendering path, rather than importing that
132
+ * function directly: it is private to that module, and its extra
133
+ * brace-scanning fallback (for free-form, unfenced prose) is machinery this
134
+ * caller doesn't need -- a bare JSON terminal here already round-trips
135
+ * through `JSON.parse` below with no fence at all.
136
+ */
137
+ export function parseDraftTerminalJson(finalText) {
138
+ let text = finalText.trim();
139
+ const fenced = /```(?:json)?\s*([\s\S]*?)\s*```/.exec(text);
140
+ if (fenced?.[1] !== undefined)
141
+ text = fenced[1].trim();
142
+ try {
143
+ return JSON.parse(text);
144
+ }
145
+ catch {
146
+ throw new EnrichmentContractError("draft turn did not return a parseable JSON terminal");
147
+ }
148
+ }
149
+ /**
150
+ * Normalizes warble's model-facing terminal shape (`{ enrichment_proposal:
151
+ * {...} }`, a single operation directly under `enrichment_proposal` for
152
+ * grill per draft.md's spec, or an `operations` array for a multi-operation
153
+ * flow) into the shape `canonicalizeProposal` (server/enrichment.ts)
154
+ * accepts: `{ operations: [{ sink, changeKind, summary, draft, confidence
155
+ * }] }`. Copies over ONLY those five fields, by name, from whichever of the
156
+ * model's own field names (`relative_sink`/`recommended_yaml`, this
157
+ * module's added `changeKind`/`summary`) or the host's own names
158
+ * (`sink`/`draft`) the model actually used — so a forged `hash`, `id`,
159
+ * `projectRevision`, or `risk` the model includes anywhere in its JSON is
160
+ * silently dropped here and never reaches `canonicalizeProposal` at all.
161
+ */
162
+ export function translateDraftTerminal(finalText) {
163
+ const parsed = parseDraftTerminalJson(finalText);
164
+ const root = record(parsed);
165
+ const proposal = root ? record(root["enrichment_proposal"]) : undefined;
166
+ if (!proposal)
167
+ throw new EnrichmentContractError("draft turn's terminal did not contain an enrichment_proposal object");
168
+ const rawOperations = Array.isArray(proposal["operations"]) ? proposal["operations"] : [proposal];
169
+ const operations = rawOperations.map((rawOperation) => {
170
+ const op = record(rawOperation) ?? {};
171
+ return {
172
+ sink: op["sink"] ?? op["relative_sink"],
173
+ changeKind: op["changeKind"] ?? op["change_kind"],
174
+ summary: op["summary"],
175
+ draft: op["draft"] ?? op["recommended_yaml"],
176
+ confidence: op["confidence"],
177
+ };
178
+ });
179
+ return { operations };
180
+ }
181
+ /**
182
+ * Builds the real, production `EnrichmentRunner.draft()`: two dispatched
183
+ * dispatches (`inspect_context` then `draft_enrichment`) against the bound
184
+ * project, folding turn 1's output into turn 2's composed question, then
185
+ * translating the terminal into the shape the host's own
186
+ * `canonicalizeProposal` independently validates and hashes. Returns
187
+ * `unknown`, exactly like the interface requires — this function never
188
+ * calls `canonicalizeProposal` itself; that stays the caller's (the `/api/
189
+ * context/enrichment/*` route's) job, same as every mock runner in
190
+ * `test/bff-enrichment.test.ts`.
191
+ */
192
+ export function createDispatchedEnrichmentDraftRunner(options) {
193
+ const dispatch = options.dispatch ?? defaultDispatch(options);
194
+ return {
195
+ async draft(input) {
196
+ const inspectResult = await dispatch({
197
+ agentId: INSPECT_CONTEXT_AGENT_ID,
198
+ question: composeInspectPrompt(input.mode),
199
+ userProject: input.projectPath,
200
+ });
201
+ const draftResult = await dispatch({
202
+ agentId: DRAFT_ENRICHMENT_AGENT_ID,
203
+ question: composeDraftPrompt(input.mode, input.projectRevision, inspectResult.finalText),
204
+ userProject: input.projectPath,
205
+ });
206
+ return translateDraftTerminal(draftResult.finalText);
207
+ },
208
+ // Pure read of the live auth choice -- no dispatch, no cost, no side effect.
209
+ readiness() {
210
+ return draftAuthReadiness(options.getAuthChoice());
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,73 @@
1
+ import type { EnrichmentBinding, EnrichmentProposal } from "./enrichment.js";
2
+ /**
3
+ * The ladder, cheapest first. Every step before `dry_run` is local: no
4
+ * database, no credentials, no round trip. Ordering is the point — a proposal
5
+ * refuted by grammar must never reach the warehouse.
6
+ *
7
+ * `dry_run` is not optional the way that phrasing might suggest. A `new_cube`
8
+ * always reaches it, because nothing local resolves a cube's expressions; every
9
+ * other change kind is refuted or verified before it.
10
+ */
11
+ export type VerificationStep = "sink" | "grammar" | "validate" | "build" | "dry_plan" | "dry_run";
12
+ export declare const VERIFICATION_LADDER: readonly VerificationStep[];
13
+ export interface VerificationRefutation {
14
+ readonly operationId: string;
15
+ readonly step: VerificationStep;
16
+ /** Readable by a person and consumable by a later repair step. Never raw source material. */
17
+ readonly reason: string;
18
+ }
19
+ export type VerificationVerdict = {
20
+ readonly status: "verified";
21
+ readonly proposalHash: string;
22
+ readonly projectRevision: string;
23
+ readonly stepsRun: readonly VerificationStep[];
24
+ } | {
25
+ readonly status: "refuted";
26
+ readonly proposalHash: string;
27
+ readonly projectRevision: string;
28
+ readonly stepsRun: readonly VerificationStep[];
29
+ readonly refutation: VerificationRefutation;
30
+ }
31
+ /**
32
+ * The ladder could not be run at all. Deliberately distinct from `verified`:
33
+ * "we could not check" and "we checked and it holds" must never collapse into
34
+ * the same value, or an unrunnable checker silently becomes an approval.
35
+ */
36
+ | {
37
+ readonly status: "unavailable";
38
+ readonly proposalHash: string;
39
+ readonly projectRevision: string;
40
+ readonly reason: string;
41
+ };
42
+ export interface VerifyProposalOptions {
43
+ /** Test seam. Defaults to running the real `wren` in the shadow directory. */
44
+ readonly run?: (args: readonly string[], cwd: string) => Promise<{
45
+ stdout: string;
46
+ stderr: string;
47
+ error: NodeJS.ErrnoException | null;
48
+ }>;
49
+ }
50
+ /**
51
+ * There is deliberately no opt-out for the data-source step.
52
+ *
53
+ * An earlier version carried an `includeWarehouseDryRun` flag documented as
54
+ * "off by default". Once cubes had to be checked through `dry-run` — nothing
55
+ * local resolves their expressions — the flag stopped being read while its
56
+ * documentation went on promising a round trip could be avoided. An advertised
57
+ * safety lever that does nothing is worse than none, so it is gone.
58
+ *
59
+ * What is true, in one place: verifying a `new_cube` always reaches the data
60
+ * source, via `dry-run`, which parses and validates without returning rows.
61
+ * Every other change kind is verified entirely locally. A caller that cannot
62
+ * reach its source cannot verify a cube, and the honest report for that is a
63
+ * refutation or `unavailable` from the attempt itself — not a flag that skips
64
+ * the check and calls the result verified.
65
+ */
66
+ /**
67
+ * Runs the ladder against a shadow copy of the bound project. Stops at the
68
+ * first refutation, so a proposal refuted locally never reaches the warehouse.
69
+ *
70
+ * The verdict carries the proposal hash and project revision it was earned
71
+ * against: a verdict must not survive onto edited content or a moved project.
72
+ */
73
+ export declare function verifyProposal(proposal: EnrichmentProposal, binding: EnrichmentBinding, options?: VerifyProposalOptions): Promise<VerificationVerdict>;