@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,145 @@
1
+ import type { Agent } from "../bundle/schema.js";
2
+ import type { DataflowArtifacts } from "../loop/index.js";
3
+ import type { ProviderRegistry, TierBinding } from "../providers/index.js";
4
+ /**
5
+ * The structured answer synthesized from an agent's finished dataflow
6
+ * artifacts, shaped by `agent.output_schema`. The golden `answer_query`
7
+ * schema requires `blocks` and allows `summary`/`verified` to be present —
8
+ * this type only pins down the fields the harness itself inspects
9
+ * (`verified`, for `gated_check` enforcement); everything else the schema
10
+ * defines flows through as-is.
11
+ */
12
+ export interface RenderEnvelope {
13
+ readonly blocks: unknown;
14
+ readonly summary?: string | null;
15
+ readonly verified?: boolean | null;
16
+ readonly [key: string]: unknown;
17
+ }
18
+ /**
19
+ * A flat table payload captured from a data-access tool's real, successful
20
+ * result (the `query` tool's `{columns, rows}` reshaped to positional rows,
21
+ * plus the SQL that produced it). When present, `renderEnvelope` builds the
22
+ * envelope's table/definition blocks directly from this — deterministic, and
23
+ * carrying the agent's actual rows — instead of routing through the render
24
+ * model, which is lossy for structured data.
25
+ */
26
+ export interface ToolTableSeed {
27
+ readonly columns: readonly string[];
28
+ readonly rows: readonly unknown[][];
29
+ readonly sql?: string;
30
+ readonly summary?: string;
31
+ }
32
+ /**
33
+ * A full `{blocks}` payload captured from a `build_dashboard` tool's real,
34
+ * successful result (see `harness/tools/native.ts`'s `assembleDashboardBlocks`).
35
+ * Unlike {@link ToolTableSeed} (a flat table reshaped into table/definition
36
+ * blocks), this is already a complete, ordered dashboard envelope — the
37
+ * deterministic assembly tool did all the reshaping. When present and it
38
+ * validates against `output_schema`, `renderEnvelope` returns it directly,
39
+ * ahead of every other path (see `renderEnvelope`'s seed-precedence order).
40
+ */
41
+ export interface DashboardSeed {
42
+ readonly blocks: readonly unknown[];
43
+ readonly summary?: string;
44
+ readonly verified?: boolean;
45
+ }
46
+ export interface RenderEnvelopeContext {
47
+ readonly binding: TierBinding;
48
+ readonly registry: ProviderRegistry;
49
+ /** The user's original question, threaded into the synthesis prompt. */
50
+ readonly userInput: string;
51
+ /**
52
+ * Deterministic render seed: a data-access tool's real result. When set
53
+ * and it validates against `output_schema`, the envelope is built straight
54
+ * from it and the render model is skipped entirely. See {@link ToolTableSeed}.
55
+ */
56
+ readonly toolTable?: ToolTableSeed;
57
+ /**
58
+ * Deterministic render seed: a `build_dashboard` tool's real, already-
59
+ * assembled `{blocks}` result (see {@link DashboardSeed}). Takes priority
60
+ * over every other seed/path — see `renderEnvelope`'s seed-precedence order.
61
+ */
62
+ readonly dashboardSeed?: DashboardSeed;
63
+ /**
64
+ * Tier to run the envelope-synthesis `generateText` call on. Defaults
65
+ * to the tier of the agent's *last* `independent` step (in `steps[]`
66
+ * array order) — the reasoning behind this default: the last independent
67
+ * step is the one that did the heaviest lifting toward the final answer
68
+ * (e.g. `generate_sql` in the golden `answer_query` shape), so synthesis
69
+ * warrants at least that much model strength. Callers with a dedicated
70
+ * "render" tier in their bundle can override this explicitly.
71
+ */
72
+ readonly tier?: string;
73
+ }
74
+ /**
75
+ * The two-stage structured-output envelope. Deliberately kept OUT of the
76
+ * `ToolLoopAgent` construction in `executeAgent` — attaching `Output.object`
77
+ * to a tool-bearing loop constrains every turn's output to the final
78
+ * schema, including turns that are just making tool calls. Instead, this
79
+ * runs as its own separate call, once, after the loop's dataflow artifacts
80
+ * have all been produced.
81
+ *
82
+ * This deliberately uses `generateText`, not `generateObject` — the schema
83
+ * is enforced in prompt text plus `collectJsonSchemaErrors` below, not via
84
+ * an SDK-level `responseFormat: json_schema`/structured-output request.
85
+ * `generateObject`'s `json_schema` response format isn't supported by every
86
+ * OpenAI-compatible endpoint (e.g. Ollama's `/v1` endpoint 400s on it), so a
87
+ * provider-agnostic envelope stage can't rely on it; `generateText` has no
88
+ * such requirement and works identically against mock, cloud, and local
89
+ * providers. Enforcement doesn't get any weaker: `jsonSchema()`'s own
90
+ * `validate` callback was already the only thing giving `output_schema`
91
+ * real teeth (it performs no runtime validation on its own without one), so
92
+ * running `collectJsonSchemaErrors` directly against the parsed JSON here
93
+ * is the same enforcement, just invoked by hand instead of through the SDK.
94
+ */
95
+ export declare function renderEnvelope(agent: Agent, artifacts: DataflowArtifacts, ctx: RenderEnvelopeContext): Promise<RenderEnvelope>;
96
+ /**
97
+ * Extracts the set of block `type` consts an `output_schema` permits, by
98
+ * walking `properties.blocks.items` — either a single item schema (e.g.
99
+ * `explain_change`'s narrative-only shape) or an `anyOf` of several (e.g.
100
+ * `answer_query`'s table/definition shape, or `generate_dashboard`'s
101
+ * kpi_card/table/chart/definition shape). Each variant contributes its
102
+ * `properties.type.const`, if it declares one.
103
+ *
104
+ * Returns `undefined` when the schema declares no `type` consts at all (e.g.
105
+ * `explore_model`'s bare `{type: "object"}` item schema, which places no
106
+ * constraint on block shape) — an unconstrained schema is deliberately never
107
+ * treated as "table-only" by {@link isTableOnlySchema}, since there's no
108
+ * actual evidence it only permits table/definition blocks.
109
+ */
110
+ export declare function getAllowedBlockTypes(outputSchema: Record<string, unknown>): Set<string> | undefined;
111
+ /**
112
+ * True when `output_schema`'s `blocks.items` permits ONLY `table`/
113
+ * `definition` block types — the `answer_query` shape, and the exact
114
+ * precondition for applying `ToolTableSeed` (see `renderEnvelope`). An
115
+ * unconstrained schema (`getAllowedBlockTypes` returns `undefined`) is never
116
+ * table-only.
117
+ */
118
+ export declare function isTableOnlySchema(outputSchema: Record<string, unknown>): boolean;
119
+ /**
120
+ * Best-effort extraction of a {@link RenderEnvelope} from dispatched's raw
121
+ * `finalText` — a CLI stdout string (`DispatchedResult.finalText`), not a
122
+ * structured artifact map the way in-process's `executeAgent` output is. Dispatched
123
+ * has no dataflow-artifact scan or tool-outcome capture of its own (see
124
+ * `runDispatchedDefault` — it only reads the dispatcher's terminal stdout line),
125
+ * so this is the only signal available to build a `form: "rich"` answer from.
126
+ *
127
+ * The agent is expected to emit its final structured answer as a JSON
128
+ * object, plain or ```json-fenced (the same contract the render model
129
+ * itself follows — see `extractJsonObjectText`), in one of the shapes
130
+ * {@link normalizeToEnvelopeShape} already reconciles:
131
+ * - already `{blocks: [...]}` (e.g. `generate_dashboard`) -> used as-is.
132
+ * - flat `{columns, rows, verified?, summary?, definition?}` (`answer_query`'s
133
+ * actual tool contract) -> reshaped into a `table` (+ `definition`) block.
134
+ * - an MCP `CallToolResult` (`{content: [{type:"text", text}], ...}` or
135
+ * `{structuredContent}`) -> unwrapped one level and the same rules
136
+ * re-applied to what's inside. This is a known trap (see the
137
+ * harness-tooltable-seed-dead follow-up): the real `query` MCP tool's raw
138
+ * result is this wrapper shape, not the flat one above, so an extractor
139
+ * that only checked for `{columns, rows}` would silently miss it.
140
+ *
141
+ * Returns `undefined` when nothing recognizable is found (not JSON at all,
142
+ * or JSON that matches none of the above), so the caller can fall back to
143
+ * `form: "text"` instead of fabricating an envelope.
144
+ */
145
+ export declare function extractEnvelopeFromText(text: string): RenderEnvelope | undefined;
@@ -0,0 +1,428 @@
1
+ import { generateText } from "ai";
2
+ import { resolveTierModel } from "../providers/index.js";
3
+ import { EnvelopeParseError, EnvelopeSchemaError, NoRenderTierError } from "./errors.js";
4
+ import { collectJsonSchemaErrors } from "./validate.js";
5
+ /** Bounded: at most one reformat attempt if the model's first response doesn't parse. */
6
+ const MAX_PARSE_ATTEMPTS = 2;
7
+ /**
8
+ * The two-stage structured-output envelope. Deliberately kept OUT of the
9
+ * `ToolLoopAgent` construction in `executeAgent` — attaching `Output.object`
10
+ * to a tool-bearing loop constrains every turn's output to the final
11
+ * schema, including turns that are just making tool calls. Instead, this
12
+ * runs as its own separate call, once, after the loop's dataflow artifacts
13
+ * have all been produced.
14
+ *
15
+ * This deliberately uses `generateText`, not `generateObject` — the schema
16
+ * is enforced in prompt text plus `collectJsonSchemaErrors` below, not via
17
+ * an SDK-level `responseFormat: json_schema`/structured-output request.
18
+ * `generateObject`'s `json_schema` response format isn't supported by every
19
+ * OpenAI-compatible endpoint (e.g. Ollama's `/v1` endpoint 400s on it), so a
20
+ * provider-agnostic envelope stage can't rely on it; `generateText` has no
21
+ * such requirement and works identically against mock, cloud, and local
22
+ * providers. Enforcement doesn't get any weaker: `jsonSchema()`'s own
23
+ * `validate` callback was already the only thing giving `output_schema`
24
+ * real teeth (it performs no runtime validation on its own without one), so
25
+ * running `collectJsonSchemaErrors` directly against the parsed JSON here
26
+ * is the same enforcement, just invoked by hand instead of through the SDK.
27
+ */
28
+ export async function renderEnvelope(agent, artifacts, ctx) {
29
+ const outputSchema = agent.output_schema;
30
+ // Deterministic render seed (STRONGEST path, checked first): a
31
+ // `build_dashboard` tool's real, already-assembled `{blocks}` result
32
+ // (`ctx.dashboardSeed`). This is a complete dashboard envelope built
33
+ // deterministically by the native tool (see `assembleDashboardBlocks`), so
34
+ // there is nothing left to reconcile — it wins over the table-only
35
+ // `toolTable` seed below (whose gate below would skip a dashboard schema
36
+ // anyway) and over the artifact scan / render LLM. Only taken when it
37
+ // validates against `output_schema`; otherwise fall through (e.g.
38
+ // `build_dashboard` was never called for this run).
39
+ if (ctx.dashboardSeed !== undefined) {
40
+ const seeded = {
41
+ blocks: ctx.dashboardSeed.blocks,
42
+ ...(ctx.dashboardSeed.summary !== undefined ? { summary: ctx.dashboardSeed.summary } : {}),
43
+ ...(ctx.dashboardSeed.verified !== undefined ? { verified: ctx.dashboardSeed.verified } : {}),
44
+ };
45
+ if (collectJsonSchemaErrors(outputSchema, seeded).length === 0) {
46
+ return seeded;
47
+ }
48
+ }
49
+ // Deterministic render seed: if the caller captured a data-access tool's
50
+ // real result (`ctx.toolTable` — e.g. the `query` tool's rows), build the
51
+ // envelope straight from it. This preserves the agent's actual data
52
+ // regardless of whether the step model chose to re-emit that structure as
53
+ // text (many models summarize it to prose, which the artifact scan below
54
+ // then can't recover). `verified: true` is warranted here specifically
55
+ // because the payload IS a successful, non-erroring execution result — the
56
+ // exact evidence `runAgent`'s deterministic `gated_check` requires.
57
+ //
58
+ // Gated on `isTableOnlySchema`: this seed only ever produces `table`/
59
+ // `definition` blocks, so it must only be applied when `output_schema`
60
+ // permits *just* those block types (the `answer_query` shape). Without
61
+ // this gate, a table-only seed captured from an incidental `query` call
62
+ // (e.g. inside `generate_dashboard`'s multi-panel flow) would fire FIRST
63
+ // and collapse a whole dashboard down to a single table — the schema
64
+ // validation below would eventually reject it (a dashboard schema also
65
+ // permits `kpi_card`/`chart`, so a table-only payload can actually still
66
+ // validate against it, since every block in the array only needs to match
67
+ // ONE `anyOf` branch), silently losing every other panel. Gating on the
68
+ // schema's allowed types catches this before validation, not after.
69
+ // Only taken when it also validates against `output_schema`; otherwise
70
+ // fall through.
71
+ if (ctx.toolTable !== undefined && isTableOnlySchema(outputSchema)) {
72
+ const seeded = normalizeToEnvelopeShape({
73
+ columns: ctx.toolTable.columns,
74
+ rows: ctx.toolTable.rows,
75
+ verified: true,
76
+ ...(ctx.toolTable.summary !== undefined ? { summary: ctx.toolTable.summary } : {}),
77
+ ...(ctx.toolTable.sql !== undefined
78
+ ? { definition: { sql: ctx.toolTable.sql, source_tables: [], filters: [] } }
79
+ : {}),
80
+ });
81
+ if (collectJsonSchemaErrors(outputSchema, seeded).length === 0) {
82
+ return seeded;
83
+ }
84
+ }
85
+ // Deterministic fast-path: if a terminal artifact is ALREADY a renderable
86
+ // payload (answer_query emits the flat {columns,rows,verified,definition};
87
+ // other agents may already emit a {blocks} envelope), build the envelope
88
+ // straight from it — no render LLM. This preserves the agent's real data
89
+ // (the table rows) instead of asking a render model to re-synthesize it,
90
+ // which is lossy (it tends to summarize the table away, leaving a bare
91
+ // `{type:"table"}`) and an extra call. Only taken when the result validates;
92
+ // otherwise fall through to the render-LLM path below.
93
+ const direct = tryDirectEnvelope(artifacts);
94
+ if (direct !== undefined && collectJsonSchemaErrors(outputSchema, direct).length === 0) {
95
+ return direct;
96
+ }
97
+ const tier = ctx.tier ?? defaultRenderTier(agent);
98
+ const model = resolveTierModel(ctx.binding, tier, ctx.registry);
99
+ const prompt = buildEnvelopePrompt(agent, artifacts, ctx.userInput, outputSchema);
100
+ let lastParseError;
101
+ for (let attempt = 1; attempt <= MAX_PARSE_ATTEMPTS; attempt += 1) {
102
+ const { text } = await generateText({ model, prompt: attempt === 1 ? prompt : reformatPrompt(prompt) });
103
+ let value;
104
+ try {
105
+ value = JSON.parse(extractJsonObjectText(text));
106
+ }
107
+ catch (error) {
108
+ lastParseError = error;
109
+ continue;
110
+ }
111
+ const normalized = normalizeToEnvelopeShape(value);
112
+ const errors = collectJsonSchemaErrors(outputSchema, normalized);
113
+ if (errors.length > 0) {
114
+ throw new EnvelopeSchemaError(agent.id, errors);
115
+ }
116
+ return normalized;
117
+ }
118
+ throw new EnvelopeParseError(agent.id, lastParseError);
119
+ }
120
+ /**
121
+ * `answer_query`'s ACTUAL tool contract (see `answer_tool.ts`/warble's own
122
+ * eval, which this must never change) is a FLAT table shape —
123
+ * `{columns, rows, verified, definition}` — not the `{blocks}`
124
+ * RenderEnvelope this module otherwise expects. Left unreconciled, that flat
125
+ * shape fails `output_schema` validation below and the run degrades to an
126
+ * unverified raw-JSON text blob (see `runAgent`'s `gated_check`, which only
127
+ * trusts `envelope.verified === true`).
128
+ *
129
+ * This is a compatibility/normalization shim, applied to every parsed
130
+ * candidate before schema validation:
131
+ * - Already has a `blocks` array (e.g. `generate_dashboard`, or any future
132
+ * agent that already speaks the block contract) → returned unchanged.
133
+ * - Otherwise, if it structurally matches the flat table shape (`columns`
134
+ * and `rows` are both arrays — a shape a real `{blocks: [...]}` envelope
135
+ * never has at its top level, so this can't misfire against one) →
136
+ * synthesize `blocks = [{type: "table", columns, rows}, ...(definition
137
+ * block, if present)]` and carry `verified`/`summary` onto the result,
138
+ * so the flat payload renders as a verified table instead of failing
139
+ * validation.
140
+ * - Anything else (unrecognized shape) → returned unchanged, so it still
141
+ * fails `output_schema` validation below with a useful error rather than
142
+ * being silently coerced into something misleading.
143
+ */
144
+ /**
145
+ * Scans the loop's dataflow artifacts for one that is ALREADY a renderable
146
+ * payload and builds the envelope from it deterministically (via
147
+ * `normalizeToEnvelopeShape`), skipping the render LLM. Artifacts are checked
148
+ * in reverse insertion order so the agent's terminal output (the last step's
149
+ * result) wins over any intermediate one. String artifacts are parsed the same
150
+ * way a model response would be. Returns `undefined` when nothing is directly
151
+ * renderable, so the caller falls back to the render-LLM path.
152
+ */
153
+ function tryDirectEnvelope(artifacts) {
154
+ for (const raw of [...artifacts.values()].reverse()) {
155
+ const candidate = coerceToObject(raw);
156
+ if (candidate === undefined)
157
+ continue;
158
+ if (hasBlocksArray(candidate) || isFlatTablePayload(candidate)) {
159
+ return normalizeToEnvelopeShape(candidate);
160
+ }
161
+ }
162
+ return undefined;
163
+ }
164
+ /** An artifact value as a parsed object: objects pass through; strings are
165
+ * parsed like a model response (fence-stripped); anything else is skipped. */
166
+ function coerceToObject(raw) {
167
+ if (typeof raw === "object" && raw !== null)
168
+ return raw;
169
+ if (typeof raw === "string") {
170
+ try {
171
+ return JSON.parse(extractJsonObjectText(raw));
172
+ }
173
+ catch {
174
+ return undefined;
175
+ }
176
+ }
177
+ return undefined;
178
+ }
179
+ function normalizeToEnvelopeShape(value) {
180
+ if (hasBlocksArray(value))
181
+ return value;
182
+ const candidate = isCodexQueryResultPayload(value)
183
+ ? codexQueryResultToFlatTable(value)
184
+ : value;
185
+ if (!isFlatTablePayload(candidate))
186
+ return value;
187
+ const { columns, rows, verified, summary, definition } = candidate;
188
+ const blocks = [{ type: "table", columns, rows }];
189
+ if (definition && typeof definition === "object") {
190
+ const { sql, source_tables, filters } = definition;
191
+ blocks.push({ type: "definition", sql, source_tables, filters });
192
+ }
193
+ return {
194
+ blocks,
195
+ ...(summary !== undefined ? { summary } : {}),
196
+ ...(verified !== undefined ? { verified } : {}),
197
+ };
198
+ }
199
+ const TABLE_ONLY_BLOCK_TYPES = new Set(["table", "definition"]);
200
+ /**
201
+ * Extracts the set of block `type` consts an `output_schema` permits, by
202
+ * walking `properties.blocks.items` — either a single item schema (e.g.
203
+ * `explain_change`'s narrative-only shape) or an `anyOf` of several (e.g.
204
+ * `answer_query`'s table/definition shape, or `generate_dashboard`'s
205
+ * kpi_card/table/chart/definition shape). Each variant contributes its
206
+ * `properties.type.const`, if it declares one.
207
+ *
208
+ * Returns `undefined` when the schema declares no `type` consts at all (e.g.
209
+ * `explore_model`'s bare `{type: "object"}` item schema, which places no
210
+ * constraint on block shape) — an unconstrained schema is deliberately never
211
+ * treated as "table-only" by {@link isTableOnlySchema}, since there's no
212
+ * actual evidence it only permits table/definition blocks.
213
+ */
214
+ export function getAllowedBlockTypes(outputSchema) {
215
+ const items = extractBlocksItemsSchema(outputSchema);
216
+ if (items === undefined)
217
+ return undefined;
218
+ const variants = Array.isArray(items.anyOf) ? items.anyOf : [items];
219
+ const types = new Set();
220
+ for (const variant of variants) {
221
+ if (!isPlainRecord(variant))
222
+ continue;
223
+ const properties = variant.properties;
224
+ if (!isPlainRecord(properties))
225
+ continue;
226
+ const typeSchema = properties.type;
227
+ if (isPlainRecord(typeSchema) && typeof typeSchema.const === "string") {
228
+ types.add(typeSchema.const);
229
+ }
230
+ }
231
+ return types.size > 0 ? types : undefined;
232
+ }
233
+ /**
234
+ * True when `output_schema`'s `blocks.items` permits ONLY `table`/
235
+ * `definition` block types — the `answer_query` shape, and the exact
236
+ * precondition for applying `ToolTableSeed` (see `renderEnvelope`). An
237
+ * unconstrained schema (`getAllowedBlockTypes` returns `undefined`) is never
238
+ * table-only.
239
+ */
240
+ export function isTableOnlySchema(outputSchema) {
241
+ const allowed = getAllowedBlockTypes(outputSchema);
242
+ if (allowed === undefined)
243
+ return false;
244
+ for (const type of allowed) {
245
+ if (!TABLE_ONLY_BLOCK_TYPES.has(type))
246
+ return false;
247
+ }
248
+ return true;
249
+ }
250
+ function extractBlocksItemsSchema(outputSchema) {
251
+ const properties = outputSchema.properties;
252
+ if (!isPlainRecord(properties))
253
+ return undefined;
254
+ const blocks = properties.blocks;
255
+ if (!isPlainRecord(blocks))
256
+ return undefined;
257
+ const items = blocks.items;
258
+ return isPlainRecord(items) ? items : undefined;
259
+ }
260
+ function isPlainRecord(value) {
261
+ return typeof value === "object" && value !== null && !Array.isArray(value);
262
+ }
263
+ function hasBlocksArray(value) {
264
+ return typeof value === "object" && value !== null && Array.isArray(value.blocks);
265
+ }
266
+ function isFlatTablePayload(value) {
267
+ if (typeof value !== "object" || value === null)
268
+ return false;
269
+ const record = value;
270
+ return Array.isArray(record.columns) && Array.isArray(record.rows);
271
+ }
272
+ /**
273
+ * codex:local's terminal child value is deliberately an execution-evidence
274
+ * object rather than the legacy flat answer_query payload. Recognize only
275
+ * the complete shape: a partial or model-invented lookalike must fall back
276
+ * to unverified text instead of being promoted into a verified table.
277
+ */
278
+ function isCodexQueryResultPayload(value) {
279
+ if (!isPlainRecord(value) || !isPlainRecord(value.result_set))
280
+ return false;
281
+ const resultSet = value.result_set;
282
+ return (Array.isArray(resultSet.columns) &&
283
+ resultSet.columns.every((column) => typeof column === "string") &&
284
+ Array.isArray(resultSet.rows) &&
285
+ typeof value.sql === "string" &&
286
+ Array.isArray(value.source_tables) &&
287
+ value.source_tables.every((table) => typeof table === "string") &&
288
+ Array.isArray(value.filters) &&
289
+ typeof value.execution_passed === "boolean" &&
290
+ typeof value.validation_passed === "boolean" &&
291
+ (typeof value.validation_error === "string" || value.validation_error === null));
292
+ }
293
+ function codexQueryResultToFlatTable(value) {
294
+ const { columns, rows } = value.result_set;
295
+ const positionalRows = rows.map((row) => isPlainRecord(row) ? columns.map((column) => row[column]) : row);
296
+ return {
297
+ columns,
298
+ rows: positionalRows,
299
+ verified: value.execution_passed &&
300
+ value.validation_passed &&
301
+ value.validation_error === null,
302
+ definition: {
303
+ sql: value.sql,
304
+ source_tables: value.source_tables,
305
+ filters: value.filters,
306
+ },
307
+ };
308
+ }
309
+ /**
310
+ * Extracts a JSON object from a model's raw text response: strips
311
+ * ```json …``` / ``` fences, trims surrounding prose, and locates the outer
312
+ * `{ … }` object by its first `{` and matching last `}`. Throws (falls
313
+ * through to `JSON.parse`'s own error) if no object-shaped substring is
314
+ * found, so callers can treat "couldn't extract" and "extracted but invalid
315
+ * JSON" the same way.
316
+ */
317
+ function extractJsonObjectText(text) {
318
+ const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
319
+ const candidate = (fenced?.[1] ?? text).trim();
320
+ const start = candidate.indexOf("{");
321
+ const end = candidate.lastIndexOf("}");
322
+ if (start === -1 || end === -1 || end < start) {
323
+ throw new SyntaxError(`no JSON object found in model response: ${JSON.stringify(text.slice(0, 200))}`);
324
+ }
325
+ return candidate.slice(start, end + 1);
326
+ }
327
+ /**
328
+ * Best-effort extraction of a {@link RenderEnvelope} from dispatched's raw
329
+ * `finalText` — a CLI stdout string (`DispatchedResult.finalText`), not a
330
+ * structured artifact map the way in-process's `executeAgent` output is. Dispatched
331
+ * has no dataflow-artifact scan or tool-outcome capture of its own (see
332
+ * `runDispatchedDefault` — it only reads the dispatcher's terminal stdout line),
333
+ * so this is the only signal available to build a `form: "rich"` answer from.
334
+ *
335
+ * The agent is expected to emit its final structured answer as a JSON
336
+ * object, plain or ```json-fenced (the same contract the render model
337
+ * itself follows — see `extractJsonObjectText`), in one of the shapes
338
+ * {@link normalizeToEnvelopeShape} already reconciles:
339
+ * - already `{blocks: [...]}` (e.g. `generate_dashboard`) -> used as-is.
340
+ * - flat `{columns, rows, verified?, summary?, definition?}` (`answer_query`'s
341
+ * actual tool contract) -> reshaped into a `table` (+ `definition`) block.
342
+ * - an MCP `CallToolResult` (`{content: [{type:"text", text}], ...}` or
343
+ * `{structuredContent}`) -> unwrapped one level and the same rules
344
+ * re-applied to what's inside. This is a known trap (see the
345
+ * harness-tooltable-seed-dead follow-up): the real `query` MCP tool's raw
346
+ * result is this wrapper shape, not the flat one above, so an extractor
347
+ * that only checked for `{columns, rows}` would silently miss it.
348
+ *
349
+ * Returns `undefined` when nothing recognizable is found (not JSON at all,
350
+ * or JSON that matches none of the above), so the caller can fall back to
351
+ * `form: "text"` instead of fabricating an envelope.
352
+ */
353
+ export function extractEnvelopeFromText(text) {
354
+ let candidate;
355
+ try {
356
+ candidate = JSON.parse(extractJsonObjectText(text));
357
+ }
358
+ catch {
359
+ return undefined;
360
+ }
361
+ return normalizeExtractedCandidate(candidate);
362
+ }
363
+ /** Bounded: at most one MCP `CallToolResult` unwrap, so a pathological/self-referential payload can't recurse forever. */
364
+ const MAX_UNWRAP_DEPTH = 2;
365
+ function normalizeExtractedCandidate(value, depth = 0) {
366
+ const normalized = normalizeToEnvelopeShape(value);
367
+ if (hasBlocksArray(normalized))
368
+ return normalized;
369
+ if (depth >= MAX_UNWRAP_DEPTH)
370
+ return undefined;
371
+ const unwrapped = unwrapCallToolResult(value);
372
+ return unwrapped === undefined ? undefined : normalizeExtractedCandidate(unwrapped, depth + 1);
373
+ }
374
+ /**
375
+ * Unwraps one layer of an MCP `CallToolResult` (`{content, structuredContent,
376
+ * isError}`) to the value inside — `structuredContent` when present,
377
+ * otherwise the first `type: "text"` content item's own JSON (fence-stripped
378
+ * the same way a model response is). Returns `undefined` for anything that
379
+ * isn't recognizably this shape.
380
+ */
381
+ function unwrapCallToolResult(value) {
382
+ if (!isPlainRecord(value))
383
+ return undefined;
384
+ if (value.structuredContent !== undefined)
385
+ return value.structuredContent;
386
+ if (!Array.isArray(value.content))
387
+ return undefined;
388
+ const textItem = value.content.find((item) => isPlainRecord(item) && item.type === "text" && typeof item.text === "string");
389
+ if (textItem === undefined)
390
+ return undefined;
391
+ try {
392
+ return JSON.parse(extractJsonObjectText(textItem.text));
393
+ }
394
+ catch {
395
+ return undefined;
396
+ }
397
+ }
398
+ /** Wraps the original prompt with a stricter instruction for the one bounded retry. */
399
+ function reformatPrompt(prompt) {
400
+ return (`${prompt}\n\nYour previous response could not be parsed as a single JSON object. ` +
401
+ `Respond again with ONLY the JSON object — no prose, no markdown code fences.`);
402
+ }
403
+ function defaultRenderTier(agent) {
404
+ const independentSteps = agent.steps.filter((step) => step.realization.kind === "independent");
405
+ const lastIndependent = independentSteps[independentSteps.length - 1];
406
+ if (!lastIndependent) {
407
+ throw new NoRenderTierError(agent.id);
408
+ }
409
+ return lastIndependent.tier;
410
+ }
411
+ /**
412
+ * Renders every accumulated dataflow artifact plus `output_schema` into the
413
+ * synthesis prompt. Since there's no SDK-level `responseFormat` doing the
414
+ * asking for us (see `renderEnvelope`), the instruction to emit bare JSON
415
+ * matching the schema — and nothing else — has to live in the prompt text.
416
+ */
417
+ function buildEnvelopePrompt(agent, artifacts, userInput, outputSchema) {
418
+ const sections = [
419
+ `User's question: ${userInput}`,
420
+ `Synthesize the final structured answer for agent "${agent.id}" from the artifacts below.`,
421
+ ];
422
+ for (const [name, value] of artifacts) {
423
+ sections.push(`${name}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
424
+ }
425
+ sections.push(`Respond with ONLY a single JSON object conforming to this JSON Schema — no prose, ` +
426
+ `no markdown code fences, no explanation before or after it:\n${JSON.stringify(outputSchema)}`);
427
+ return sections.join("\n\n");
428
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Thrown when the render envelope stage (the two-stage structured-output
3
+ * synthesis) needs a tier to run on but the caller didn't supply one
4
+ * explicitly and the agent has no `independent` step to fall back to —
5
+ * e.g. an agent made up entirely of `repair_fold` steps. This should never
6
+ * happen for a v1-scoped agent (`executeAgent` already requires at least
7
+ * one independent step to produce anything), but the envelope stage
8
+ * doesn't assume that invariant on its own.
9
+ */
10
+ export declare class NoRenderTierError extends Error {
11
+ constructor(agentId: string);
12
+ }
13
+ /**
14
+ * Thrown when the model's envelope response parses as JSON but doesn't
15
+ * structurally match `agent.output_schema` — checked by hand via
16
+ * `collectJsonSchemaErrors` against the `generateText` output (see
17
+ * `renderEnvelope`; the envelope stage doesn't use `generateObject`'s
18
+ * SDK-level schema enforcement, so this replaces the errors that used to
19
+ * surface as a `NoObjectGeneratedError`'s cause).
20
+ */
21
+ export declare class EnvelopeSchemaError extends Error {
22
+ constructor(agentId: string, violations: readonly string[]);
23
+ }
24
+ /**
25
+ * Thrown when the model's envelope response still doesn't extract into
26
+ * parseable JSON after `renderEnvelope`'s one bounded reformat retry (see
27
+ * `extractJsonObjectText`). Distinct from `EnvelopeSchemaError`, which is
28
+ * for JSON that parses but doesn't match `output_schema` — this is for
29
+ * responses that never became valid JSON at all.
30
+ */
31
+ export declare class EnvelopeParseError extends Error {
32
+ constructor(agentId: string, cause: unknown);
33
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Thrown when the render envelope stage (the two-stage structured-output
3
+ * synthesis) needs a tier to run on but the caller didn't supply one
4
+ * explicitly and the agent has no `independent` step to fall back to —
5
+ * e.g. an agent made up entirely of `repair_fold` steps. This should never
6
+ * happen for a v1-scoped agent (`executeAgent` already requires at least
7
+ * one independent step to produce anything), but the envelope stage
8
+ * doesn't assume that invariant on its own.
9
+ */
10
+ export class NoRenderTierError extends Error {
11
+ constructor(agentId) {
12
+ super(`agent "${agentId}" has no "independent" step to default the render envelope's ` +
13
+ `tier from, and no explicit tier was supplied to renderEnvelope()`);
14
+ this.name = "NoRenderTierError";
15
+ }
16
+ }
17
+ /**
18
+ * Thrown when the model's envelope response parses as JSON but doesn't
19
+ * structurally match `agent.output_schema` — checked by hand via
20
+ * `collectJsonSchemaErrors` against the `generateText` output (see
21
+ * `renderEnvelope`; the envelope stage doesn't use `generateObject`'s
22
+ * SDK-level schema enforcement, so this replaces the errors that used to
23
+ * surface as a `NoObjectGeneratedError`'s cause).
24
+ */
25
+ export class EnvelopeSchemaError extends Error {
26
+ constructor(agentId, violations) {
27
+ super(`agent "${agentId}"'s render envelope does not match its output_schema:\n` +
28
+ violations.map((violation) => ` - ${violation}`).join("\n"));
29
+ this.name = "EnvelopeSchemaError";
30
+ }
31
+ }
32
+ /**
33
+ * Thrown when the model's envelope response still doesn't extract into
34
+ * parseable JSON after `renderEnvelope`'s one bounded reformat retry (see
35
+ * `extractJsonObjectText`). Distinct from `EnvelopeSchemaError`, which is
36
+ * for JSON that parses but doesn't match `output_schema` — this is for
37
+ * responses that never became valid JSON at all.
38
+ */
39
+ export class EnvelopeParseError extends Error {
40
+ constructor(agentId, cause) {
41
+ super(`agent "${agentId}"'s render envelope response could not be parsed as JSON after retrying: ` +
42
+ `${cause instanceof Error ? cause.message : String(cause)}`);
43
+ this.name = "EnvelopeParseError";
44
+ this.cause = cause;
45
+ }
46
+ }
@@ -0,0 +1,5 @@
1
+ export { extractEnvelopeFromText, getAllowedBlockTypes, isTableOnlySchema, renderEnvelope } from "./envelope.js";
2
+ export type { DashboardSeed, RenderEnvelope, RenderEnvelopeContext, ToolTableSeed } from "./envelope.js";
3
+ export { EnvelopeParseError, EnvelopeSchemaError, NoRenderTierError } from "./errors.js";
4
+ export { collectJsonSchemaErrors } from "./validate.js";
5
+ export type { JsonSchemaDocument } from "./validate.js";
@@ -0,0 +1,3 @@
1
+ export { extractEnvelopeFromText, getAllowedBlockTypes, isTableOnlySchema, renderEnvelope } from "./envelope.js";
2
+ export { EnvelopeParseError, EnvelopeSchemaError, NoRenderTierError } from "./errors.js";
3
+ export { collectJsonSchemaErrors } from "./validate.js";