@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,333 @@
1
+ import { assertCapabilities } from "../capability/gate.js";
2
+ import { createAgentEventEmitter } from "../events/index.js";
3
+ import { createLocalExecutionEnv } from "../exec/index.js";
4
+ import { deriveEnforcement } from "../guardrails/index.js";
5
+ import { executeAgent, summarizeToolOutput } from "../loop/index.js";
6
+ import { getAllowedBlockTypes, renderEnvelope } from "../render/index.js";
7
+ import { BUILD_DASHBOARD_TOOL_NAME, WREN_QUERY_TOOL_NAME, withResolvedTools } from "../tools/index.js";
8
+ import { UnknownAgentError } from "./errors.js";
9
+ import { findLockedGatedCheck } from "./gate.js";
10
+ /**
11
+ * The full `answer_query` walking-skeleton entry point: capability gate
12
+ * (whole-bundle) -> resolve `agentId`'s tools -> run its dataflow
13
+ * steps with repair-fold -> synthesize the two-stage render
14
+ * envelope -> enforce any locked `gated_check` guardrail -> an
15
+ * explicit `AnswerResult | RefusalResult`.
16
+ *
17
+ * MCP clients opened while resolving `agentId`'s tools are always torn down
18
+ * (`withResolvedTools`'s `finally`), whether the run succeeds, the loop
19
+ * throws (e.g. `RepairExhaustedError`), or the render stage throws (e.g. a
20
+ * malformed model response failing `output_schema` validation) — in every
21
+ * failure case the error propagates out of `runAgent` rather than being
22
+ * swallowed into a refusal; only a *rendered but unverified* envelope
23
+ * becomes a `RefusalResult`.
24
+ */
25
+ export async function runAgent(bundle, agentId, userInput, ctx) {
26
+ assertCapabilities(bundle, ctx.capabilityRegistry);
27
+ const agent = bundle.agents.find((candidate) => candidate.id === agentId);
28
+ if (!agent) {
29
+ throw new UnknownAgentError(agentId);
30
+ }
31
+ const emitter = createAgentEventEmitter(ctx.onEvent);
32
+ emitter.emit({ kind: "run.start", mode: "A", agentId });
33
+ try {
34
+ const enforcementPolicy = {
35
+ ...deriveEnforcement(agent),
36
+ ...(ctx.allowedHosts !== undefined ? { allowedHosts: ctx.allowedHosts } : {}),
37
+ };
38
+ const executionEnv = ctx.executionEnv ?? createLocalExecutionEnv();
39
+ const result = await withResolvedTools(agent, {
40
+ ...(ctx.mcpServers !== undefined ? { mcpServers: ctx.mcpServers } : {}),
41
+ ...(ctx.nativeTools !== undefined ? { nativeTools: ctx.nativeTools } : {}),
42
+ executionEnv,
43
+ enforcementPolicy,
44
+ }, async (tools) => {
45
+ // Track whether the run had at least one *successful*
46
+ // data-access tool call, independent of anything the render LLM
47
+ // claims about itself. `requiresDataAccess` is capability-aware — an
48
+ // agent that never declares a `sql_execution:*` capability (a
49
+ // pure-render/no-data component) never sets this, so it can never be
50
+ // forced to a refusal by this check.
51
+ let sawSuccessfulDataAccessCall = false;
52
+ const requiresDataAccess = agent.capabilities.some((entry) => entry.capability.startsWith("sql_execution:"));
53
+ // Deterministic table capture: the most recent *successful* `query`
54
+ // tool result, kept as a flat table payload (columns + positional
55
+ // rows + the SQL that produced it). This is the agent's real data —
56
+ // `renderEnvelope` builds the table/definition blocks straight from
57
+ // it instead of asking the render model to re-emit structure it tends
58
+ // to summarize away (leaving a bare `{type:"table"}`). The last
59
+ // success wins because, in the answer_query flow, it is the query
60
+ // whose rows are the answer.
61
+ let toolTable;
62
+ // Deterministic dashboard-envelope capture: the most recent
63
+ // *successful* `build_dashboard` tool result, kept as-is (it's
64
+ // already a complete, ordered `{blocks}` envelope — see
65
+ // `assembleDashboardBlocks`). Mirrors `toolTable`'s capture above;
66
+ // the last success wins for the same reason. `renderEnvelope` prefers
67
+ // this over `toolTable` (a dashboard's `{blocks}` result is strictly
68
+ // more complete than a single incidental `query` call's flat table).
69
+ let dashboardSeed;
70
+ // The most recent data-access tool error (`query` or
71
+ // `build_dashboard`), kept as a bounded summary so a non-gated
72
+ // data-block agent (e.g. `generate_dashboard`) that never grounded
73
+ // its answer can surface WHY, instead of rendering an empty,
74
+ // ungrounded "verified" envelope. Last error wins.
75
+ let lastDataAccessError;
76
+ // The FLOOR: accumulated straight from the same
77
+ // `onToolCallOutcome` firings the deterministic gate already consumes, zero new
78
+ // emission infrastructure. Terminal-only, flat, ordered by fire order.
79
+ const traceSteps = [];
80
+ let ordinal = 0;
81
+ const artifacts = await executeAgent(agent, {
82
+ binding: ctx.binding,
83
+ registry: ctx.registry,
84
+ tools,
85
+ userInput,
86
+ ...(ctx.evaluateGuard !== undefined ? { evaluateGuard: ctx.evaluateGuard } : {}),
87
+ onToolCallOutcome: (outcome) => {
88
+ // Carry the tool's input and a compact detail (success
89
+ // summary, or the error message) so the UI's work-log can expand
90
+ // a step — especially useful for the SQL-repair retries, where
91
+ // an errored `query` step should show the SQL it tried and why
92
+ // it failed.
93
+ // Both success + error detail go through summarizeToolOutput so the
94
+ // persisted trace_json detail is bounded (200 chars) either way — an
95
+ // error string is returned as-is (no quoting) then truncated, matching
96
+ // the success path's cap rather than persisting an unbounded message.
97
+ const detail = outcome.outcome === "success"
98
+ ? summarizeToolOutput(outcome.output)
99
+ : summarizeToolOutput(outcome.error);
100
+ traceSteps.push({
101
+ id: outcome.callId ?? `${outcome.tool}-${ordinal}`,
102
+ tool: outcome.tool,
103
+ outcome: outcome.outcome,
104
+ ordinal,
105
+ ...(outcome.input !== undefined ? { input: outcome.input } : {}),
106
+ ...(detail !== undefined ? { detail } : {}),
107
+ });
108
+ ordinal += 1;
109
+ if (outcome.tool === WREN_QUERY_TOOL_NAME && outcome.outcome === "success") {
110
+ sawSuccessfulDataAccessCall = true;
111
+ const seed = toToolTableSeed(outcome.output, outcome.input);
112
+ if (seed !== undefined)
113
+ toolTable = seed;
114
+ }
115
+ if (outcome.tool === BUILD_DASHBOARD_TOOL_NAME && outcome.outcome === "success") {
116
+ const seed = toDashboardSeed(outcome.output);
117
+ if (seed !== undefined)
118
+ dashboardSeed = seed;
119
+ }
120
+ if (outcome.outcome === "error" &&
121
+ (outcome.tool === WREN_QUERY_TOOL_NAME || outcome.tool === BUILD_DASHBOARD_TOOL_NAME)) {
122
+ lastDataAccessError = detail;
123
+ }
124
+ },
125
+ onEvent: emitter.emit,
126
+ });
127
+ const envelope = await renderEnvelope(agent, artifacts, {
128
+ binding: ctx.binding,
129
+ registry: ctx.registry,
130
+ userInput,
131
+ ...(ctx.renderTier !== undefined ? { tier: ctx.renderTier } : {}),
132
+ ...(toolTable !== undefined ? { toolTable } : {}),
133
+ ...(dashboardSeed !== undefined ? { dashboardSeed } : {}),
134
+ });
135
+ const trace = { steps: traceSteps };
136
+ const gatedCheck = findLockedGatedCheck(agent);
137
+ // Earn `verified` from a real
138
+ // successful data-access instead of relying solely on the render LLM's
139
+ // self-attestation. A successful, non-erroring `query` (or, for a
140
+ // dashboard, `build_dashboard`) execution IS the evidence the
141
+ // deterministic gate requires; the render LLM (used for narrative-only
142
+ // agents like `explain_change`) does not reliably self-attest
143
+ // `verified: true`, so without this, a genuine successful query still
144
+ // produced a spurious refusal. Agents with no successful data-access
145
+ // call are unaffected — they still depend on (and can still fail) the
146
+ // render LLM's self-attestation below.
147
+ //
148
+ // CAVEAT (accepted trade-off, single-query agents only, see
149
+ // `isDashboardShaped` below): this is scoped to "the run had ANY
150
+ // successful `query` call", so it overrides a render LLM's
151
+ // `verified: false` regardless of WHY the model set it — including a
152
+ // legitimate self-doubt that the answer doesn't match the data, not
153
+ // only fabrication. The product meaning of `verified` here is
154
+ // therefore "grounded in a real, successful data-access", not "the
155
+ // model certifies its own interpretation".
156
+ //
157
+ // A DASHBOARD-SHAPED agent (`kpi_card`/`chart` permitted by
158
+ // `output_schema` — i.e. `generate_dashboard`'s multi-panel,
159
+ // `build_dashboard`-mediated assembly) is different in kind from a
160
+ // single-query agent: it fans out to several independent panel
161
+ // queries, so "any successful query somewhere in the run" says
162
+ // nothing about whether the OTHER panels — or the dashboard assembly
163
+ // itself — actually succeeded. A run where every panel query failed
164
+ // but one incidental prior query succeeded must not be marked
165
+ // verified with an empty/fabricated dashboard. Grounding for a
166
+ // dashboard-shaped answer therefore requires the seed that actually
167
+ // IS the answer: `dashboardSeed` (paired with a real successful
168
+ // query, so a `build_dashboard` call carrying only fabricated panel
169
+ // data doesn't count).
170
+ //
171
+ // Every other shape (table-only `answer_query`, narrative
172
+ // `explain_change`, unconstrained `explore_model`) has exactly one
173
+ // data-producing step, not several independent ones a dashboard fans
174
+ // out to — so for those, "any real successful data-access call" is
175
+ // sufficient evidence and keeps the original grounding rule.
176
+ //
177
+ // NOTE (deviation from the literal spec): the spec's literal
178
+ // `seedBackedVerified` formula additionally credited "table-only
179
+ // schema AND a captured `toolTable` seed". `toolTable` is populated
180
+ // from `query`'s raw tool-result `output`, but `query` is resolved as
181
+ // an MCP tool, whose tool-result `output` is the full MCP
182
+ // `CallToolResult` (`{content, structuredContent, isError}`) — not the
183
+ // flat `{columns, rows}` shape `toToolTableSeed` expects — so
184
+ // `toolTable` is always `undefined` in practice for any real
185
+ // MCP-backed `query` call, making that clause dead code today. Fixing
186
+ // the unwrap was tried, but `toolTable` is *also* fed straight into
187
+ // `renderEnvelope`'s own seed precedence (ranked above the
188
+ // direct-artifact scan), so making it populate changed `answer_query`'s
189
+ // rendered *content* (the raw, un-curated query dump winning over
190
+ // `generate_sql`'s actual curated answer) — a regression outside this
191
+ // fix's scope. The `isDashboardShaped` split below achieves the
192
+ // same fix intent (stop trusting an incidental query for a
193
+ // multi-panel dashboard) without that side effect.
194
+ const outputSchema = agent.output_schema;
195
+ const DATA_BLOCK_TYPES = new Set(["table", "kpi_card", "chart"]);
196
+ const DASHBOARD_ONLY_BLOCK_TYPES = new Set(["kpi_card", "chart"]);
197
+ const allowedBlockTypes = getAllowedBlockTypes(outputSchema);
198
+ const producesDataBlocks = allowedBlockTypes !== undefined && [...allowedBlockTypes].some((t) => DATA_BLOCK_TYPES.has(t));
199
+ const isDashboardShaped = allowedBlockTypes !== undefined && [...allowedBlockTypes].some((t) => DASHBOARD_ONLY_BLOCK_TYPES.has(t));
200
+ const seedBackedVerified = isDashboardShaped
201
+ ? dashboardSeed !== undefined && sawSuccessfulDataAccessCall
202
+ : requiresDataAccess && sawSuccessfulDataAccessCall;
203
+ const earnedVerified = seedBackedVerified;
204
+ // Only ever ADD verified:true when earned — never force verified:false
205
+ // onto an unearned envelope. The deterministic gates below (both the
206
+ // pre-existing `gatedCheck` checks and the dashboard-grounding branch) decide
207
+ // solely from `seedBackedVerified`/`sawSuccessfulDataAccessCall`, not
208
+ // from `effectiveEnvelope.verified` — so forcing it false here would
209
+ // only rob a refusal's returned envelope of the model's own (possibly
210
+ // fabricated) self-attestation, which the test explicitly wants
211
+ // preserved as evidence the gate doesn't trust it.
212
+ const effectiveEnvelope = earnedVerified ? { ...envelope, verified: true } : envelope;
213
+ if (gatedCheck && effectiveEnvelope.verified !== true) {
214
+ return {
215
+ kind: "refusal",
216
+ reason: `agent "${agentId}" has a locked gated_check guardrail, but the rendered envelope's ` +
217
+ `"verified" field was ${JSON.stringify(effectiveEnvelope.verified)} (must be exactly true)`,
218
+ envelope: effectiveEnvelope,
219
+ trace,
220
+ };
221
+ }
222
+ // Deterministic gate invariant: a locked `gated_check`
223
+ // guardrail must never be satisfied by the render LLM's self-attested
224
+ // `verified: true` alone when the agent declares a data-access
225
+ // capability (`sql_execution:*`, i.e. it needs the `query` tool to
226
+ // answer honestly). A fabricating or confused model can assert
227
+ // `verified: true` on a run where the `query` tool was never called,
228
+ // or was called and only ever errored (including a
229
+ // missing `wren` binary — every call throws, so zero successes are
230
+ // recorded and this branch correctly forces a refusal instead of
231
+ // trusting a possibly-fabricated `verified: true`). This check is
232
+ // purely additive to the one above: honest runs that have a real
233
+ // successful `query` call keep behaving exactly as before.
234
+ if (gatedCheck && requiresDataAccess && !sawSuccessfulDataAccessCall) {
235
+ return {
236
+ kind: "refusal",
237
+ reason: `agent "${agentId}" has a locked gated_check guardrail and declares a data-access ` +
238
+ `capability ("sql_execution:*"), but the run had zero successful "${WREN_QUERY_TOOL_NAME}" ` +
239
+ `tool calls — a self-attested "verified: true" cannot stand without at least one real, ` +
240
+ `non-erroring data-access call (deterministic gate)`,
241
+ envelope: effectiveEnvelope,
242
+ trace,
243
+ };
244
+ }
245
+ // A NON-gated data-block agent (e.g. generate_dashboard)
246
+ // that requires data access but produced no real answer-data seed
247
+ // never actually grounded its answer (build_dashboard was never
248
+ // assembled, or every panel query failed). Rather than present an
249
+ // empty, ungrounded envelope as an "answer", surface WHY — the last
250
+ // data-access error — as a refusal. Gated agents (answer_query,
251
+ // explain_change) are unaffected: their two refusal blocks above
252
+ // already handle the unverified case.
253
+ if (!gatedCheck && producesDataBlocks && requiresDataAccess && !seedBackedVerified) {
254
+ return {
255
+ kind: "refusal",
256
+ reason: lastDataAccessError
257
+ ? `Couldn't build a grounded result — the data query failed: ${lastDataAccessError}`
258
+ : `Couldn't build a grounded result — the queried data was not assembled into an answer`,
259
+ envelope: effectiveEnvelope,
260
+ trace,
261
+ };
262
+ }
263
+ return { kind: "answer", envelope: effectiveEnvelope, trace };
264
+ });
265
+ if (result.kind === "answer") {
266
+ emitter.emit({ kind: "answer", envelope: result.envelope });
267
+ emitter.emit({ kind: "run.finish", status: "answer" });
268
+ }
269
+ else {
270
+ emitter.emit({ kind: "refusal", reason: result.reason, envelope: result.envelope });
271
+ emitter.emit({ kind: "run.finish", status: "refusal" });
272
+ }
273
+ return result;
274
+ }
275
+ catch (error) {
276
+ emitter.emit({ kind: "error", message: describeRunError(error) });
277
+ emitter.emit({ kind: "run.finish", status: "error" });
278
+ throw error;
279
+ }
280
+ }
281
+ function describeRunError(error) {
282
+ if (error instanceof Error)
283
+ return error.message;
284
+ return typeof error === "string" ? error : JSON.stringify(error);
285
+ }
286
+ /**
287
+ * Reshapes a successful `query` tool call into a {@link ToolTableSeed}: the
288
+ * tool returns `{columns, rows}` with rows as column-keyed objects, but the
289
+ * render table block wants positional rows aligned to `columns`. Returns
290
+ * `undefined` for any output that isn't that shape (so a non-table tool, or a
291
+ * malformed result, simply falls back to the normal render path). The SQL, if
292
+ * the tool call's input carried it, becomes the definition block's `sql`.
293
+ */
294
+ function toToolTableSeed(output, input) {
295
+ if (typeof output !== "object" || output === null)
296
+ return undefined;
297
+ const { columns, rows } = output;
298
+ if (!Array.isArray(columns) || !Array.isArray(rows))
299
+ return undefined;
300
+ if (!columns.every((c) => typeof c === "string"))
301
+ return undefined;
302
+ const positionalRows = rows.map((row) => row !== null && typeof row === "object"
303
+ ? columns.map((col) => row[col])
304
+ : [row]);
305
+ const sql = typeof input === "object" && input !== null && typeof input.sql === "string"
306
+ ? input.sql
307
+ : undefined;
308
+ return {
309
+ columns: columns,
310
+ rows: positionalRows,
311
+ ...(sql !== undefined ? { sql } : {}),
312
+ };
313
+ }
314
+ /**
315
+ * Reshapes a successful `build_dashboard` tool call into a
316
+ * {@link DashboardSeed}: the tool already returns `{blocks, summary?,
317
+ * verified}` (see `assembleDashboardBlocks`), so this just narrows/validates
318
+ * that shape defensively. Returns `undefined` for any output that isn't at
319
+ * least `{blocks: array}` (so a malformed result simply falls back to the
320
+ * normal render path instead of seeding garbage).
321
+ */
322
+ function toDashboardSeed(output) {
323
+ if (typeof output !== "object" || output === null)
324
+ return undefined;
325
+ const { blocks, summary, verified } = output;
326
+ if (!Array.isArray(blocks))
327
+ return undefined;
328
+ return {
329
+ blocks,
330
+ ...(typeof summary === "string" ? { summary } : {}),
331
+ ...(typeof verified === "boolean" ? { verified } : {}),
332
+ };
333
+ }
@@ -0,0 +1,29 @@
1
+ import type { StepTrace } from "../events/index.js";
2
+ import type { RenderEnvelope } from "../render/index.js";
3
+ /** A normal answer: the envelope satisfied every `gated_check` guardrail (or the agent has none). */
4
+ export interface AnswerResult {
5
+ readonly kind: "answer";
6
+ readonly envelope: RenderEnvelope;
7
+ /**
8
+ * The FLOOR. Always present for a in-process run (`runAgent`
9
+ * always accumulates one, even with no `onEvent` sink attached). Dispatched
10
+ * carries no trace at all — it isn't a `RunAgentResult`, so this field
11
+ * doesn't apply there (see `route/dispatched.ts`'s doc comment for the gap).
12
+ */
13
+ readonly trace?: StepTrace;
14
+ }
15
+ /**
16
+ * A refusal: the agent has a `locked` `gated_check` guardrail and the
17
+ * rendered envelope's `verified` field was not `true`. The envelope is
18
+ * still attached (it's often useful for diagnostics), but callers must
19
+ * branch on `kind` before treating it as a deliverable answer.
20
+ */
21
+ export interface RefusalResult {
22
+ readonly kind: "refusal";
23
+ readonly reason: string;
24
+ readonly envelope: RenderEnvelope;
25
+ /** See {@link AnswerResult.trace}. */
26
+ readonly trace?: StepTrace;
27
+ }
28
+ /** `runAgent`'s result: model refusal vs. answer as an explicit, exhaustive union. */
29
+ export type RunAgentResult = AnswerResult | RefusalResult;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { AgentEventInput } from "../events/index.js";
2
+ export declare class CodexSetupEventMapper {
3
+ private readonly tracePath?;
4
+ private readonly tools;
5
+ private activeStep;
6
+ private finalText;
7
+ private traceCursor;
8
+ constructor(tracePath?: string | undefined);
9
+ nextLine(line: string): AgentEventInput | undefined;
10
+ result(): string;
11
+ private nextTraceRecord;
12
+ }
@@ -0,0 +1,108 @@
1
+ import { readFileSync } from "node:fs";
2
+ export class CodexSetupEventMapper {
3
+ tracePath;
4
+ tools = new Map();
5
+ activeStep = "";
6
+ finalText;
7
+ traceCursor = 0;
8
+ constructor(tracePath) {
9
+ this.tracePath = tracePath;
10
+ }
11
+ nextLine(line) {
12
+ let event;
13
+ try {
14
+ event = JSON.parse(line);
15
+ }
16
+ catch {
17
+ throw new Error("warble-codex-local emitted non-JSON output");
18
+ }
19
+ switch (event.t) {
20
+ case "step_start":
21
+ this.activeStep = event.id;
22
+ return { kind: "step.start", stepId: event.id, name: event.name, tier: "strong", depth: 0 };
23
+ case "tool_call":
24
+ if (!this.activeStep)
25
+ throw new Error("Codex tool call arrived before step start");
26
+ this.tools.set(event.id, { stepId: this.activeStep, name: event.name });
27
+ return {
28
+ kind: "tool.call",
29
+ stepId: this.activeStep,
30
+ callId: event.id,
31
+ tool: event.name,
32
+ depth: 0,
33
+ status: "running",
34
+ };
35
+ case "tool_result": {
36
+ const tool = this.tools.get(event.id);
37
+ if (!tool)
38
+ throw new Error("Codex tool result arrived without a matching call");
39
+ this.tools.delete(event.id);
40
+ const trace = this.nextTraceRecord();
41
+ return {
42
+ kind: "tool.result",
43
+ stepId: tool.stepId,
44
+ callId: event.id,
45
+ tool: tool.name,
46
+ status: event.ok ? "success" : "error",
47
+ ...(trace ? { input: trace.input } : {}),
48
+ ...(event.ok && trace ? { summary: trace.detail } : {}),
49
+ ...(event.error ? { error: event.error } : {}),
50
+ };
51
+ }
52
+ case "answer":
53
+ this.finalText = event.text;
54
+ return { kind: "answer", text: event.text };
55
+ case "step_finish":
56
+ return {
57
+ kind: "step.finish",
58
+ stepId: event.id,
59
+ name: event.id,
60
+ status: event.ok ? "ok" : "error",
61
+ ...(event.detail ? { detail: event.detail } : {}),
62
+ };
63
+ default:
64
+ throw new Error("warble-codex-local emitted an unknown event");
65
+ }
66
+ }
67
+ result() {
68
+ if (this.tools.size > 0)
69
+ throw new Error("warble-codex-local ended with an unfinished tool call");
70
+ if (this.finalText === undefined || this.finalText.trim().length === 0) {
71
+ throw new Error("warble-codex-local ended without a final answer");
72
+ }
73
+ return this.finalText;
74
+ }
75
+ nextTraceRecord() {
76
+ if (!this.tracePath)
77
+ return undefined;
78
+ const lines = readFileSync(this.tracePath, "utf8").split("\n").filter((line) => line.trim().length > 0);
79
+ const line = lines[this.traceCursor];
80
+ if (line === undefined)
81
+ throw new Error("Codex setup MCP completed without a matching bridge trace record");
82
+ this.traceCursor += 1;
83
+ let value;
84
+ try {
85
+ value = JSON.parse(line);
86
+ }
87
+ catch {
88
+ throw new Error("Codex setup MCP wrote an invalid bridge trace record");
89
+ }
90
+ if (!isTraceRecord(value))
91
+ throw new Error("Codex setup MCP wrote a malformed bridge trace record");
92
+ return value;
93
+ }
94
+ }
95
+ function isTraceRecord(value) {
96
+ if (typeof value !== "object" || value === null)
97
+ return false;
98
+ const record = value;
99
+ if (typeof record["detail"] !== "string")
100
+ return false;
101
+ const input = record["input"];
102
+ if (typeof input !== "object" || input === null)
103
+ return false;
104
+ const fields = input;
105
+ if (fields["action"] !== "exec" && fields["action"] !== "write")
106
+ return false;
107
+ return ["command", "cwd", "path"].every((key) => fields[key] === undefined || typeof fields[key] === "string");
108
+ }