@wrenai/genbi 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/README.md +64 -2
  2. package/dist/assets/{echarts-LTFC-ysd.js → echarts-DYM4hjvk.js} +2 -2
  3. package/dist/assets/index-A_Wrq7Ey.css +1 -0
  4. package/dist/assets/{index-Bo--QMUN.js → index-DsH129tV.js} +18 -17
  5. package/dist/index.html +2 -2
  6. package/dist-server/harness/bundle/schema.d.ts +38 -0
  7. package/dist-server/harness/bundle/schema.js +18 -10
  8. package/dist-server/harness/bundle/version.js +1 -1
  9. package/dist-server/harness/cli-args.d.ts +10 -2
  10. package/dist-server/harness/cli-args.js +24 -8
  11. package/dist-server/harness/cli.js +75 -9
  12. package/dist-server/harness/compile/cache.js +4 -4
  13. package/dist-server/harness/compile/compose-profile.d.ts +51 -14
  14. package/dist-server/harness/compile/compose-profile.js +85 -19
  15. package/dist-server/harness/compile/context-loader.d.ts +56 -0
  16. package/dist-server/harness/compile/context-loader.js +237 -0
  17. package/dist-server/harness/compile/errors.d.ts +19 -0
  18. package/dist-server/harness/compile/errors.js +35 -0
  19. package/dist-server/harness/compile/index.d.ts +4 -3
  20. package/dist-server/harness/compile/index.js +4 -3
  21. package/dist-server/harness/compile/pipeline.d.ts +15 -7
  22. package/dist-server/harness/compile/pipeline.js +62 -14
  23. package/dist-server/harness/compile/resolve-binary.d.ts +2 -2
  24. package/dist-server/harness/compile/resolve-binary.js +3 -3
  25. package/dist-server/harness/compile/types.d.ts +29 -0
  26. package/dist-server/harness/compile/warble-identity.d.ts +11 -0
  27. package/dist-server/harness/compile/warble-identity.js +13 -0
  28. package/dist-server/harness/components/ai-step.d.ts +4 -0
  29. package/dist-server/harness/components/ai-step.js +40 -0
  30. package/dist-server/harness/components/broker.d.ts +60 -0
  31. package/dist-server/harness/components/broker.js +117 -0
  32. package/dist-server/harness/components/capability-card.d.ts +98 -0
  33. package/dist-server/harness/components/capability-card.js +169 -0
  34. package/dist-server/harness/components/context.d.ts +2 -0
  35. package/dist-server/harness/components/context.js +30 -0
  36. package/dist-server/harness/components/display.d.ts +6 -0
  37. package/dist-server/harness/components/display.js +29 -0
  38. package/dist-server/harness/components/egress-judge.d.ts +12 -0
  39. package/dist-server/harness/components/egress-judge.js +27 -0
  40. package/dist-server/harness/components/egress.d.ts +142 -0
  41. package/dist-server/harness/components/egress.js +469 -0
  42. package/dist-server/harness/components/in-process.d.ts +7 -0
  43. package/dist-server/harness/components/in-process.js +245 -0
  44. package/dist-server/harness/components/normalize.d.ts +35 -0
  45. package/dist-server/harness/components/normalize.js +245 -0
  46. package/dist-server/harness/components/plan.d.ts +31 -0
  47. package/dist-server/harness/components/plan.js +184 -0
  48. package/dist-server/harness/components/report.d.ts +207 -0
  49. package/dist-server/harness/components/report.js +309 -0
  50. package/dist-server/harness/components/runner.d.ts +193 -0
  51. package/dist-server/harness/components/runner.js +492 -0
  52. package/dist-server/harness/components/semantic-guards.d.ts +20 -0
  53. package/dist-server/harness/components/semantic-guards.js +42 -0
  54. package/dist-server/harness/components/terminal-json.d.ts +18 -0
  55. package/dist-server/harness/components/terminal-json.js +82 -0
  56. package/dist-server/harness/components/usage.d.ts +14 -0
  57. package/dist-server/harness/components/usage.js +19 -0
  58. package/dist-server/harness/components/wren-access.d.ts +29 -0
  59. package/dist-server/harness/components/wren-access.js +233 -0
  60. package/dist-server/harness/components/zone-dry-run.d.ts +43 -0
  61. package/dist-server/harness/components/zone-dry-run.js +107 -0
  62. package/dist-server/harness/components/zone-gate.d.ts +76 -0
  63. package/dist-server/harness/components/zone-gate.js +144 -0
  64. package/dist-server/harness/events/index.d.ts +1 -1
  65. package/dist-server/harness/events/types.d.ts +52 -0
  66. package/dist-server/harness/index.d.ts +9 -2
  67. package/dist-server/harness/index.js +5 -1
  68. package/dist-server/harness/loop/executor.d.ts +1 -0
  69. package/dist-server/harness/loop/executor.js +37 -15
  70. package/dist-server/harness/npm-package-resolve.d.ts +2 -0
  71. package/dist-server/harness/npm-package-resolve.js +16 -4
  72. package/dist-server/harness/providers/adapters/openai-compatible.d.ts +13 -0
  73. package/dist-server/harness/providers/adapters/openai-compatible.js +17 -0
  74. package/dist-server/harness/providers/adapters/openai.d.ts +7 -0
  75. package/dist-server/harness/providers/adapters/openai.js +6 -0
  76. package/dist-server/harness/providers/adapters/pi-ai.d.ts +48 -0
  77. package/dist-server/harness/providers/adapters/pi-ai.js +518 -0
  78. package/dist-server/harness/providers/binding.d.ts +45 -4
  79. package/dist-server/harness/providers/binding.js +44 -8
  80. package/dist-server/harness/providers/index.d.ts +9 -3
  81. package/dist-server/harness/providers/index.js +10 -3
  82. package/dist-server/harness/providers/zone.d.ts +96 -0
  83. package/dist-server/harness/providers/zone.js +112 -0
  84. package/dist-server/harness/render/answering-query.d.ts +29 -0
  85. package/dist-server/harness/render/answering-query.js +62 -0
  86. package/dist-server/harness/render/envelope.d.ts +6 -0
  87. package/dist-server/harness/render/envelope.js +10 -3
  88. package/dist-server/harness/route/adapter-spec.d.ts +8 -9
  89. package/dist-server/harness/route/adapter-spec.js +13 -14
  90. package/dist-server/harness/route/codex-ask-events.js +2 -1
  91. package/dist-server/harness/route/codex-ask.d.ts +4 -0
  92. package/dist-server/harness/route/codex-ask.js +69 -30
  93. package/dist-server/harness/route/codex-host-query.d.ts +52 -0
  94. package/dist-server/harness/route/codex-host-query.js +191 -0
  95. package/dist-server/harness/route/codex-local-manifest.d.ts +9 -1
  96. package/dist-server/harness/route/codex-local-manifest.js +96 -15
  97. package/dist-server/harness/route/codex-wren-proxy.d.ts +33 -0
  98. package/dist-server/harness/route/codex-wren-proxy.js +227 -0
  99. package/dist-server/harness/route/describe.js +45 -6
  100. package/dist-server/harness/route/in-process.d.ts +22 -1
  101. package/dist-server/harness/route/in-process.js +54 -4
  102. package/dist-server/harness/route/route.js +5 -2
  103. package/dist-server/harness/route/types.d.ts +30 -1
  104. package/dist-server/harness/session/run.js +30 -10
  105. package/dist-server/harness/setup/runner.d.ts +2 -0
  106. package/dist-server/harness/setup/runner.js +38 -17
  107. package/dist-server/harness/setup/yaml-scalar.d.ts +23 -0
  108. package/dist-server/harness/setup/yaml-scalar.js +62 -0
  109. package/dist-server/server/adopt.js +1 -17
  110. package/dist-server/server/app.d.ts +1 -0
  111. package/dist-server/server/app.js +163 -14
  112. package/dist-server/server/auth-choice.d.ts +13 -1
  113. package/dist-server/server/auth-choice.js +66 -1
  114. package/dist-server/server/bin.js +177 -25
  115. package/dist-server/server/codex-local-grounding.d.ts +18 -0
  116. package/dist-server/server/codex-local-grounding.js +92 -0
  117. package/dist-server/server/codex-native-session.d.ts +25 -0
  118. package/dist-server/server/codex-native-session.js +1 -0
  119. package/dist-server/server/codex-runtime-composition.d.ts +34 -0
  120. package/dist-server/server/codex-runtime-composition.js +250 -0
  121. package/dist-server/server/codex-structured-ask.d.ts +23 -0
  122. package/dist-server/server/codex-structured-ask.js +102 -0
  123. package/dist-server/server/conn-config.js +4 -16
  124. package/dist-server/server/conversation-socket.d.ts +16 -0
  125. package/dist-server/server/conversation-socket.js +65 -0
  126. package/dist-server/server/db.d.ts +46 -0
  127. package/dist-server/server/db.js +88 -9
  128. package/dist-server/server/env-detect.d.ts +6 -0
  129. package/dist-server/server/env-detect.js +7 -0
  130. package/dist-server/server/fold.d.ts +22 -4
  131. package/dist-server/server/fold.js +37 -5
  132. package/dist-server/server/harness.d.ts +2 -2
  133. package/dist-server/server/harness.js +33 -7
  134. package/dist-server/server/interactive-terminal.d.ts +10 -6
  135. package/dist-server/server/interactive-terminal.js +33 -8
  136. package/dist-server/server/managed-wren-runtime.d.ts +116 -0
  137. package/dist-server/server/managed-wren-runtime.js +670 -0
  138. package/dist-server/server/native-artifacts.d.ts +23 -7
  139. package/dist-server/server/native-artifacts.js +43 -12
  140. package/dist-server/server/native-component-context.d.ts +19 -0
  141. package/dist-server/server/native-component-context.js +76 -0
  142. package/dist-server/server/native-component-preparation.d.ts +20 -0
  143. package/dist-server/server/native-component-preparation.js +129 -0
  144. package/dist-server/server/native-component-runtime.d.ts +36 -0
  145. package/dist-server/server/native-component-runtime.js +92 -0
  146. package/dist-server/server/native-components.d.ts +84 -0
  147. package/dist-server/server/native-components.js +160 -0
  148. package/dist-server/server/native-dispatch-process.d.ts +8 -0
  149. package/dist-server/server/native-dispatch-process.js +65 -0
  150. package/dist-server/server/native-runtime-spec.d.ts +89 -0
  151. package/dist-server/server/native-runtime-spec.js +169 -0
  152. package/dist-server/server/native-sessions.d.ts +172 -19
  153. package/dist-server/server/native-sessions.js +839 -244
  154. package/dist-server/server/native-shutdown.d.ts +6 -0
  155. package/dist-server/server/native-shutdown.js +28 -0
  156. package/dist-server/server/native-wren-home.d.ts +18 -0
  157. package/dist-server/server/native-wren-home.js +262 -0
  158. package/dist-server/server/node-pty-host.d.ts +2 -1
  159. package/dist-server/server/node-pty-host.js +3 -2
  160. package/dist-server/server/profile-registry.d.ts +139 -0
  161. package/dist-server/server/profile-registry.js +620 -0
  162. package/dist-server/server/runtime-binding.d.ts +4 -1
  163. package/dist-server/server/runtime-binding.js +50 -2
  164. package/dist-server/server/runtime-host/claude-component-step.d.ts +25 -0
  165. package/dist-server/server/runtime-host/claude-component-step.js +186 -0
  166. package/dist-server/server/runtime-host/codex-app-server.d.ts +75 -0
  167. package/dist-server/server/runtime-host/codex-app-server.js +239 -0
  168. package/dist-server/server/runtime-host/codex-compatibility.d.ts +110 -0
  169. package/dist-server/server/runtime-host/codex-compatibility.js +162 -0
  170. package/dist-server/server/runtime-host/codex-component-step.d.ts +19 -0
  171. package/dist-server/server/runtime-host/codex-component-step.js +308 -0
  172. package/dist-server/server/runtime-host/codex-conversation.d.ts +109 -0
  173. package/dist-server/server/runtime-host/codex-conversation.js +295 -0
  174. package/dist-server/server/runtime-host/codex-direct-tools.d.ts +23 -0
  175. package/dist-server/server/runtime-host/codex-direct-tools.js +59 -0
  176. package/dist-server/server/runtime-host/codex-events.d.ts +647 -0
  177. package/dist-server/server/runtime-host/codex-events.js +66 -0
  178. package/dist-server/server/runtime-host/codex-policy.d.ts +16 -0
  179. package/dist-server/server/runtime-host/codex-policy.js +151 -0
  180. package/dist-server/server/runtime-host/codex-process.d.ts +8 -0
  181. package/dist-server/server/runtime-host/codex-process.js +96 -0
  182. package/dist-server/server/runtime-host/codex-rpc.d.ts +47 -0
  183. package/dist-server/server/runtime-host/codex-rpc.js +182 -0
  184. package/dist-server/server/runtime-host/codex-session.d.ts +74 -0
  185. package/dist-server/server/runtime-host/codex-session.js +492 -0
  186. package/dist-server/server/runtime-host/codex-terminal-protocol.d.ts +35 -0
  187. package/dist-server/server/runtime-host/codex-terminal-protocol.js +243 -0
  188. package/dist-server/server/runtime-host/codex-terminal.d.ts +16 -0
  189. package/dist-server/server/runtime-host/codex-terminal.js +120 -0
  190. package/dist-server/server/runtime-host/index.d.ts +5 -0
  191. package/dist-server/server/runtime-host/index.js +3 -0
  192. package/dist-server/server/runtime-host/local.d.ts +29 -0
  193. package/dist-server/server/runtime-host/local.js +90 -0
  194. package/dist-server/server/runtime-host/policy.d.ts +8 -0
  195. package/dist-server/server/runtime-host/policy.js +78 -0
  196. package/dist-server/server/runtime-host/types.d.ts +52 -0
  197. package/dist-server/server/runtime-host/types.js +62 -0
  198. package/dist-server/server/structured-query-evidence.d.ts +7 -0
  199. package/dist-server/server/structured-query-evidence.js +47 -0
  200. package/dist-server/server/structured-runtime.d.ts +52 -0
  201. package/dist-server/server/structured-runtime.js +130 -0
  202. package/dist-server/server/structured-scope.d.ts +4 -0
  203. package/dist-server/server/structured-scope.js +12 -0
  204. package/dist-server/server/turn.d.ts +6 -1
  205. package/dist-server/server/turn.js +82 -25
  206. package/dist-server/server/wire-types.d.ts +67 -3
  207. package/dist-server/server/wren-profiles.js +1 -1
  208. package/managed-wren/README.md +60 -0
  209. package/managed-wren/dependency-wheels.json +282 -0
  210. package/managed-wren/manifest.json +373 -0
  211. package/managed-wren/release-inputs.json +45 -0
  212. package/managed-wren/runtime-tree.cjs +115 -0
  213. package/managed-wren/source-inputs.json +40 -0
  214. package/package.json +15 -6
  215. package/profiles/genbi-default/context/binding.yml +17 -7
  216. package/profiles/genbi-default/context/context.json +146 -0
  217. package/profiles/genbi-default/ir.golden.json +39 -43
  218. package/profiles/genbi-default/profile.yml +31 -0
  219. package/profiles/genbi-enrich-context/context/binding.yml +14 -3
  220. package/profiles/genbi-enrich-context/context/context.json +146 -0
  221. package/profiles/genbi-enrich-context/ir.golden.json +4 -5
  222. package/profiles/genbi-monitor/context/binding.yml +13 -5
  223. package/profiles/genbi-monitor/context/context.json +146 -0
  224. package/profiles/genbi-monitor/ir.golden.json +13 -7
  225. package/profiles/genbi-report/context/binding.yml +19 -0
  226. package/profiles/genbi-report/context/context.json +223 -0
  227. package/profiles/genbi-report/ir.golden.json +328 -0
  228. package/profiles/genbi-report/profile.yml +38 -0
  229. package/profiles/genbi-setup/ir.golden.json +3 -1
  230. package/dist/assets/index-Xs4q7pNo.css +0 -1
@@ -1,16 +1,25 @@
1
1
  import { execFile } from "node:child_process";
2
- import { mkdtemp, rm } from "node:fs/promises";
2
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { createFileSystemCompileCache } from "./cache.js";
7
7
  import { composeUserProfile } from "./compose-profile.js";
8
+ import { getContextLoaderIdentity, resolveContextLoader } from "./context-loader.js";
8
9
  import { WarbleCommandFailedError } from "./errors.js";
9
10
  import { hashDirectory, hashFiles } from "./fingerprint.js";
10
11
  import { resolveHubDir, resolveWarbleBinary } from "./resolve-binary.js";
11
12
  import { getWarbleIdentity } from "./warble-identity.js";
13
+ import { planDigest, VERCEL_HOST_CONTRACT } from "../components/plan.js";
12
14
  /** Fixed `providerFragmentHash` for `"native"` mode, which never reads `--provider` fragments at all. */
13
15
  const NATIVE_MODE_PROVIDER_HASH = "native:no-providers";
16
+ /**
17
+ * Fixed `contextLoaderIdentity` for `compileRawProfile`, which compiles a raw-source binding and so
18
+ * never runs the `wren-context-loader` generator. A sentinel rather than an empty string, for the
19
+ * same reason `NATIVE_MODE_PROVIDER_HASH` is one: "the generator was not involved" is a distinct
20
+ * fact, not an alias for some generator that happened to hash to nothing.
21
+ */
22
+ const RAW_PROFILE_CONTEXT_LOADER_IDENTITY = "raw-profile:no-context-loader";
14
23
  /**
15
24
  * This package's bundled wren capability-provider fragment (`providers/wren.provider.yaml`) — the
16
25
  * default `--provider` for in-process dispatch, matching how `fixtures/genbi-default.native.bundle.json`
@@ -29,6 +38,13 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
29
38
  * mode — additionally `warble dispatch --target vercel` for the vercel bundle this package's
30
39
  * `runAgent()` consumes.
31
40
  *
41
+ * The rebind runs WrenAI's own `wren-context-loader` generator over `userProject` and binds its
42
+ * output as a `kind: prepared` document, so Warble reads a projection WrenAI produced rather than
43
+ * introspecting the semantic layer itself. Resolving that generator loud-fails
44
+ * ({@link ContextLoaderNotFoundError}) — there is no fallback to Warble's built-in `wren_project`
45
+ * adapter, because the compiled IR is byte-identical either way and a silent degrade would be
46
+ * undetectable from the artifact.
47
+ *
32
48
  * A cache hit (see below) never *executes* `warble`, but by default DOES need to resolve + read
33
49
  * the binary — its content hash and the Hub root derived from its location are both cache-key
34
50
  * inputs (see `warbleIdentity` and `hubDir` below), so a hit can't be trusted without knowing which
@@ -43,13 +59,14 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
43
59
  * the same no-shell, structured-result `execFile` convention `harness/exec/local.ts` already uses.
44
60
  *
45
61
  * Results are cached on (profile content hash x user-context fingerprint x mode x resolved
46
- * provider-fragment content hash x warble binary identity x resolved Hub root) — see `./cache.js` —
47
- * so an unchanged profile/context/providers/compiler/Hub tuple returns the previous artifact
48
- * instead of recompiling. Folding in the provider-fragment hash, warble identity and Hub root
49
- * (rather than just profile+context+mode) matters: otherwise a custom `options.providers` and this
50
- * package's default fragment collide on the same key, and editing a provider fragment, rebuilding
51
- * `warble`, or compiling against a different component library would keep serving a stale artifact
52
- * forever, since there's no TTL/eviction.
62
+ * provider-fragment content hash x warble binary identity x `wren-context-loader` binary identity x
63
+ * resolved Hub root) — see `./cache.js` — so an unchanged profile/context/providers/compiler/
64
+ * generator/Hub tuple returns the previous artifact instead of recompiling. Folding in the
65
+ * provider-fragment hash, warble identity, generator identity and Hub root (rather than just
66
+ * profile+context+mode) matters: otherwise a custom `options.providers` and this package's default
67
+ * fragment collide on the same key, and editing a provider fragment, rebuilding `warble`, rebuilding
68
+ * the generator, or compiling against a different component library would keep serving a stale
69
+ * artifact forever, since there's no TTL/eviction.
53
70
  *
54
71
  * The `warble` invocations run inside a temporary scratch directory (the composed profile + raw
55
72
  * compiler output). When the cache relocates the artifacts to a durable home outside that scratch
@@ -60,10 +77,21 @@ const WARBLE_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
60
77
  * a caller-supplied `workDir` is never deleted.
61
78
  */
62
79
  export async function compileProfile(options) {
63
- return compileProfileSource(options, await hashDirectory(path.resolve(options.userProject)), (workDir) => composeUserProfile({
80
+ // Resolving the generator is memoized here the same way `ensureWarbleBin` memoizes warble below:
81
+ // both the identity computation (a cache-key input) and the compose step (on a miss) need it, and
82
+ // resolution loud-fails rather than degrading to Warble's built-in adapter.
83
+ let resolvedLoader;
84
+ const ensureContextLoader = () => {
85
+ resolvedLoader ??= resolveContextLoader(options.contextLoaderBin);
86
+ return resolvedLoader;
87
+ };
88
+ return compileProfileSource(options,
89
+ // The compiled binding embeds the absolute project path, even for identical bytes.
90
+ planDigest({ project: path.resolve(options.userProject), content: await hashDirectory(path.resolve(options.userProject)) }), async () => options.contextLoaderIdentity ?? getContextLoaderIdentity(ensureContextLoader()), (workDir) => composeUserProfile({
64
91
  profileSource: options.profileSource,
65
92
  userProject: options.userProject,
66
93
  destDir: workDir,
94
+ contextLoaderBin: ensureContextLoader().bin,
67
95
  }));
68
96
  }
69
97
  /**
@@ -73,13 +101,18 @@ export async function compileProfile(options) {
73
101
  * the entry whenever the source profile changes.
74
102
  */
75
103
  export async function compileRawProfile(options) {
76
- return compileProfileSource(options, "raw-profile-context", async () => path.resolve(options.profileSource));
104
+ return compileProfileSource(options, "raw-profile-context", async () => RAW_PROFILE_CONTEXT_LOADER_IDENTITY, async () => path.resolve(options.profileSource));
77
105
  }
78
- async function compileProfileSource(options, contextFingerprint, prepareProfile) {
106
+ async function compileProfileSource(options, contextFingerprint,
107
+ /**
108
+ * Deferred so `compileRawProfile` never resolves a generator it has no use for, and so the
109
+ * `contextLoaderIdentity` escape hatch can still skip touching the binary on a hit.
110
+ */
111
+ contextLoaderIdentityOf, prepareProfile) {
79
112
  const cache = options.cache ?? createFileSystemCompileCache();
80
113
  const profileHash = await hashDirectory(path.resolve(options.profileSource));
81
114
  const providerPaths = options.providers ?? [DEFAULT_WREN_PROVIDER_PATH];
82
- const providerFragmentHash = options.mode === "agnostic" ? await hashFiles(providerPaths) : NATIVE_MODE_PROVIDER_HASH;
115
+ const providerFragmentHash = options.mode === "agnostic" ? `${await hashFiles(providerPaths)}:${planDigest(VERCEL_HOST_CONTRACT)}` : NATIVE_MODE_PROVIDER_HASH;
83
116
  // Resolving `warble` is memoized here so it happens at most once per call no matter how many of
84
117
  // the two spots below need it (identity computation, and — on a miss — actually running it).
85
118
  let resolvedWarbleBin;
@@ -101,7 +134,16 @@ async function compileProfileSource(options, contextFingerprint, prepareProfile)
101
134
  // without resolving the binary. `undefined` here means no `--hub-dir` is passed at all and
102
135
  // warble's compiled-in default applies — today's behaviour, preserved.
103
136
  const hubDir = options.hubDir ?? resolveHubDir(await ensureWarbleBin());
104
- const cacheKey = { profileHash, contextFingerprint, mode: options.mode, providerFragmentHash, warbleIdentity, hubDir };
137
+ const contextLoaderIdentity = await contextLoaderIdentityOf();
138
+ const cacheKey = {
139
+ profileHash,
140
+ contextFingerprint,
141
+ mode: options.mode,
142
+ providerFragmentHash,
143
+ warbleIdentity,
144
+ contextLoaderIdentity,
145
+ hubDir,
146
+ };
105
147
  const cached = await cache.get(cacheKey);
106
148
  if (cached !== undefined) {
107
149
  return { ...cached, cacheHit: true, ...(resolvedWarbleBin !== undefined ? { warbleBin: resolvedWarbleBin } : {}) };
@@ -126,7 +168,13 @@ async function compileProfileSource(options, contextFingerprint, prepareProfile)
126
168
  if (options.mode === "agnostic") {
127
169
  const bundleOutDir = path.join(workDir, "bundle");
128
170
  const providerArgs = providerPaths.flatMap((provider) => ["--provider", provider]);
129
- await runWarble(warbleBin, ["dispatch", "--target", "vercel", ...providerArgs, irPath, "--out", bundleOutDir]);
171
+ const ir = JSON.parse(await readFile(irPath, "utf8"));
172
+ const composed = ir.components.some((component) => component.llm_calls.some((step) => (step.component_calls?.length ?? 0) > 0));
173
+ const hostPath = path.join(workDir, "component-host.json");
174
+ if (composed)
175
+ await writeFile(hostPath, JSON.stringify(VERCEL_HOST_CONTRACT), { mode: 0o600 });
176
+ await runWarble(warbleBin, ["dispatch", "--target", "vercel", ...providerArgs,
177
+ ...(composed ? ["--host-contract", hostPath] : []), irPath, "--out", bundleOutDir]);
130
178
  bundlePath = path.join(bundleOutDir, "bundle.json");
131
179
  }
132
180
  const freshEntry = { irPath, ...(bundlePath !== undefined ? { bundlePath } : {}) };
@@ -46,8 +46,8 @@ export declare function resolveWarbleBinary(explicit?: string): Promise<string>;
46
46
  * 1. The binary's own checkout — a `<root>/target/release/warble` puts the Hub at
47
47
  * `<root>/hub/components`. Skipped for a bare `"warble"` resolved off `PATH`, which names no
48
48
  * directory to walk up from.
49
- * 2. A sibling `warble` checkout's `hub/components`, via the same ancestor walk tier 3 of
50
- * {@link resolveWarbleBinary} uses.
49
+ * 2. A sibling `warble` checkout's `hub/components`, only when the explicit sibling-checkout
50
+ * development mode is enabled. An installed release must use its own default Hub.
51
51
  *
52
52
  * Returns `undefined` when neither resolves; callers then pass no `--hub-dir` at all, leaving
53
53
  * today's compiled-in default in charge rather than pointing warble at a directory we guessed.
@@ -98,8 +98,8 @@ function isExecutableOnPath(bin) {
98
98
  * 1. The binary's own checkout — a `<root>/target/release/warble` puts the Hub at
99
99
  * `<root>/hub/components`. Skipped for a bare `"warble"` resolved off `PATH`, which names no
100
100
  * directory to walk up from.
101
- * 2. A sibling `warble` checkout's `hub/components`, via the same ancestor walk tier 3 of
102
- * {@link resolveWarbleBinary} uses.
101
+ * 2. A sibling `warble` checkout's `hub/components`, only when the explicit sibling-checkout
102
+ * development mode is enabled. An installed release must use its own default Hub.
103
103
  *
104
104
  * Returns `undefined` when neither resolves; callers then pass no `--hub-dir` at all, leaving
105
105
  * today's compiled-in default in charge rather than pointing warble at a directory we guessed.
@@ -113,7 +113,7 @@ export function resolveHubDir(warbleBin) {
113
113
  if (existsSync(candidate))
114
114
  return candidate;
115
115
  }
116
- return findSiblingWarbleEntry("hub", "components");
116
+ return isWarbleSiblingCheckoutDevModeEnabled() ? findSiblingWarbleEntry("hub", "components") : undefined;
117
117
  }
118
118
  /**
119
119
  * Walks up from this package's own directory (`harness/compile/` in source, `dist/compile/` once
@@ -26,6 +26,20 @@ export interface CompileCacheKey {
26
26
  * compiled, instead of silently serving an artifact built by a now-stale `warble`.
27
27
  */
28
28
  readonly warbleIdentity: string;
29
+ /**
30
+ * Content identity of the resolved `wren-context-loader` binary — the generator that renders the
31
+ * user's semantic layer into the prepared-context document a `kind: prepared` binding reads (see
32
+ * `getBinaryIdentity`). Fixed sentinel for `compileRawProfile`, which binds a raw source and never
33
+ * runs the generator at all.
34
+ *
35
+ * This is here for the *generator's* identity, not the project's: `contextFingerprint` already
36
+ * content-hashes the user project's semantic-layer files, so folding in the generated document
37
+ * would say nothing new about the project. What it would miss is a changed generator — a new
38
+ * version that projects the same layer differently (or extracts a facet the old one dropped)
39
+ * produces a different document, and therefore a different compiled artifact, from byte-identical
40
+ * project inputs. Same reason `warbleIdentity` is here, one stage earlier in the pipeline.
41
+ */
42
+ readonly contextLoaderIdentity: string;
29
43
  /**
30
44
  * The Hub component-library root passed to `warble compile --hub-dir` (see `resolveHubDir`), or
31
45
  * `undefined` when none could be derived and the compile therefore ran against warble's own
@@ -75,6 +89,21 @@ export interface CompileProfileOptions {
75
89
  * hit skip touching the binary at all.
76
90
  */
77
91
  readonly warbleIdentity?: string;
92
+ /**
93
+ * Explicit path to the `wren-context-loader` binary. See `resolveContextLoaderBinary` for the
94
+ * resolution order when omitted (explicit → `WREN_HARNESS_CONTEXT_LOADER_BIN` → in-repo build →
95
+ * verified exact `@wrenai/context-loader` package → loud failure; there is no fallback to
96
+ * Warble's built-in `wren_project` adapter).
97
+ */
98
+ readonly contextLoaderBin?: string;
99
+ /**
100
+ * Precomputed identity for the resolved `wren-context-loader` binary (see
101
+ * `CompileCacheKey.contextLoaderIdentity`). Same purpose as `warbleIdentity`: without it,
102
+ * computing the cache key has to resolve and read the generator even for what would otherwise be
103
+ * a cache hit. Pass this, `warbleIdentity` and `hubDir` together to make a hit touch no binary at
104
+ * all. Ignored by `compileRawProfile`, which never runs the generator.
105
+ */
106
+ readonly contextLoaderIdentity?: string;
78
107
  /**
79
108
  * Explicit Hub component-library root for `warble compile --hub-dir`. When omitted it is derived
80
109
  * from the resolved `warble` binary (see `resolveHubDir`) so the compiler and its Hub always come
@@ -10,3 +10,14 @@
10
10
  * override), a cache hit still never *executes* warble, only resolves + reads its file.
11
11
  */
12
12
  export declare function getWarbleIdentity(binPath: string): Promise<string>;
13
+ /**
14
+ * The generic form of {@link getWarbleIdentity}: a memoized content hash of any binary whose
15
+ * identity belongs in the compile cache key. Also used for the `wren-context-loader` generator (see
16
+ * `CompileCacheKey.contextLoaderIdentity`), which is a compiler of the same kind one stage earlier
17
+ * — it turns the user's semantic layer into the prepared-context document Warble then compiles.
18
+ *
19
+ * Hashes content rather than shelling out for a version string: it needs no subprocess, so folding
20
+ * a binary's identity into the key never costs an execution, and it catches a rebuild that keeps
21
+ * the same version number.
22
+ */
23
+ export declare function getBinaryIdentity(binPath: string): Promise<string>;
@@ -19,6 +19,19 @@ const identityCache = new Map();
19
19
  * override), a cache hit still never *executes* warble, only resolves + reads its file.
20
20
  */
21
21
  export async function getWarbleIdentity(binPath) {
22
+ return getBinaryIdentity(binPath);
23
+ }
24
+ /**
25
+ * The generic form of {@link getWarbleIdentity}: a memoized content hash of any binary whose
26
+ * identity belongs in the compile cache key. Also used for the `wren-context-loader` generator (see
27
+ * `CompileCacheKey.contextLoaderIdentity`), which is a compiler of the same kind one stage earlier
28
+ * — it turns the user's semantic layer into the prepared-context document Warble then compiles.
29
+ *
30
+ * Hashes content rather than shelling out for a version string: it needs no subprocess, so folding
31
+ * a binary's identity into the key never costs an execution, and it catches a rebuild that keeps
32
+ * the same version number.
33
+ */
34
+ export async function getBinaryIdentity(binPath) {
22
35
  const resolved = path.resolve(binPath);
23
36
  let cached = identityCache.get(resolved);
24
37
  if (cached === undefined) {
@@ -0,0 +1,4 @@
1
+ import { type LanguageModel } from "ai";
2
+ import type { StepResponse, StepRun } from "./runner.js";
3
+ /** A fresh AI SDK loop for each host-owned step. No caller history or ambient tools. */
4
+ export declare function runAiComponentStep(run: StepRun, model: LanguageModel): Promise<StepResponse>;
@@ -0,0 +1,40 @@
1
+ import { ToolLoopAgent, isStepCount, jsonSchema, tool } from "ai";
2
+ import { readSlots } from "./egress.js";
3
+ const SINGLE_ANSWER_STEPS = 12;
4
+ /**
5
+ * Model turns one step may take. A per-slot step gets two turns per slot (a query and one
6
+ * retry, for a model that issues one tool call per turn) plus four for lookups and the answer.
7
+ */
8
+ function stepCap(run) {
9
+ const read = run.perSlot ? readSlots(run) : undefined;
10
+ const slots = read && !("error" in read) ? read.slots.length : 0;
11
+ return Math.max(SINGLE_ANSWER_STEPS, 2 * slots + 4);
12
+ }
13
+ /** A fresh AI SDK loop for each host-owned step. No caller history or ambient tools. */
14
+ export async function runAiComponentStep(run, model) {
15
+ const tools = {};
16
+ for (const [name, execute] of Object.entries(run.tools)) {
17
+ const inputSchema = run.toolSchemas[name];
18
+ if (!inputSchema)
19
+ throw new Error("Component tool has no closed input schema");
20
+ tools[name] = tool({ description: run.toolDescriptions[name] ?? name,
21
+ inputSchema: jsonSchema(inputSchema),
22
+ execute: async (input) => execute(input), });
23
+ }
24
+ const prompt = JSON.stringify({ request: run.request, input: run.input, consumes: run.consumes });
25
+ const agent = new ToolLoopAgent({ model, tools, stopWhen: isStepCount(stepCap(run)),
26
+ instructions: [run.brief, run.prompt].filter(Boolean).join("\n\n"),
27
+ });
28
+ const result = await agent.generate({ prompt, abortSignal: run.signal });
29
+ const toolErrors = result.steps.some((step) => step.content.some((part) => part.type === "tool-error"));
30
+ // A single-answer step fails on any tool error, so the declared repair step runs and an
31
+ // unrepaired failure ends the child. In a per-slot invocation a tool error is one slot's
32
+ // outcome (the model reports it as `{slot_id, status: "unanswerable"}` and answers the rest);
33
+ // the step itself fails only when the loop did not complete. Normalization still grounds
34
+ // every answered slot in an observed query, so the leniency cannot promote a claim to data.
35
+ return {
36
+ value: result.text,
37
+ failed: result.finishReason !== "stop" || (!run.perSlot && toolErrors),
38
+ usage: { inputTokens: result.totalUsage.inputTokens ?? 0, outputTokens: result.totalUsage.outputTokens ?? 0 },
39
+ };
40
+ }
@@ -0,0 +1,60 @@
1
+ import type { ComponentEvidence, ComponentPlan, ExecutionPlan, RunnerHost, StepResponse, StepRun } from "./runner.js";
2
+ import type { ComponentInvocationResult } from "@warble/claude-agent-sdk";
3
+ /** Captured by the host from its project, session and approved runtime records. */
4
+ export interface ComponentIdentity {
5
+ readonly session: string;
6
+ readonly vendor: string;
7
+ readonly account: string;
8
+ readonly generation: string;
9
+ readonly project: string;
10
+ readonly bindingRevision: string;
11
+ readonly contextDigest: string;
12
+ readonly planDigest: string;
13
+ }
14
+ export interface ComponentAccess {
15
+ /** Must execute against the captured project and enforce read-only access. */
16
+ query(input: {
17
+ sql: string;
18
+ limit: number;
19
+ }, signal: AbortSignal): Promise<unknown>;
20
+ inspect(signal: AbortSignal): Promise<unknown>;
21
+ close(): Promise<void>;
22
+ }
23
+ /** Which host context a step receives: the full prepared snapshot (private zone) or the capability card (public zone). */
24
+ export interface ContextSurface {
25
+ readonly kind: "snapshot" | "card";
26
+ readonly text: string;
27
+ }
28
+ /** The step run as the host's `step` sees it: the assembled prompt plus its named parts for fingerprinting. */
29
+ export type BrokeredStepRun = StepRun & {
30
+ /** Named prompt parts exactly as assembled into `prompt`, for the per-surface fingerprint (per surface and in total). Absent when a host bypasses the broker. */
31
+ readonly surfaces?: Readonly<Record<string, string>>;
32
+ readonly contextKind?: ContextSurface["kind"] | "none";
33
+ };
34
+ export interface ComponentBrokerOptions {
35
+ readonly plan: ExecutionPlan;
36
+ /** Fresh snapshots captured from the same project binding used by prepare(). */
37
+ readonly contexts: Readonly<Record<string, {
38
+ binding: Readonly<Record<string, unknown>>;
39
+ snapshot: unknown;
40
+ }>>;
41
+ readonly verifierBinary: string;
42
+ readonly identity: ComponentIdentity;
43
+ readonly currentIdentity: () => ComponentIdentity | undefined;
44
+ /** Opens the exact model/tool authority represented by the verified host context. */
45
+ readonly prepare: (component: ComponentPlan, identity: ComponentIdentity, signal: AbortSignal) => Promise<ComponentAccess>;
46
+ readonly step: (run: BrokeredStepRun, component: ComponentPlan, identity: ComponentIdentity) => Promise<StepResponse>;
47
+ /**
48
+ * Chooses the host context surface for one step. Default: the component's prepared snapshot.
49
+ * A zone-aware host returns the capability card for public-zone steps so the snapshot never
50
+ * reaches a public model.
51
+ */
52
+ readonly contextSurface?: (component: ComponentPlan, run: StepRun) => ContextSurface | undefined;
53
+ readonly normalize: (component: ComponentPlan, evidence: ComponentEvidence, signal: AbortSignal, context: unknown) => Promise<ComponentInvocationResult>;
54
+ readonly persistRoot?: RunnerHost["persistRoot"];
55
+ /** The egress verification seam; see `RunnerHost.verifyChild`. */
56
+ readonly verifyChild?: RunnerHost["verifyChild"];
57
+ readonly onEvent?: RunnerHost["onEvent"];
58
+ }
59
+ /** Typed host access only: models cannot supply paths, connection config, identity or commands. */
60
+ export declare function createComponentBroker(options: ComponentBrokerOptions): RunnerHost;
@@ -0,0 +1,117 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { z } from "zod";
3
+ import { planDigest } from "./plan.js";
4
+ import { verifyComponentContext } from "./context.js";
5
+ const queryInput = z.object({ sql: z.string().trim().min(1).max(65_536), limit: z.number().int().positive().optional() }).strict();
6
+ const emptyInput = z.object({}).strict();
7
+ const QUERY_SCHEMA = { type: "object", additionalProperties: false, required: ["sql"], properties: {
8
+ sql: { type: "string", minLength: 1, maxLength: 65_536 }, limit: { type: "integer", minimum: 1 },
9
+ } };
10
+ const INSPECT_SCHEMA = { type: "object", additionalProperties: false, properties: {} };
11
+ /** Typed host access only: models cannot supply paths, connection config, identity or commands. */
12
+ export function createComponentBroker(options) {
13
+ const identity = Object.freeze({ ...options.identity });
14
+ if (Object.values(identity).some((value) => !value.trim()))
15
+ throw new Error("Missing component execution identity");
16
+ const plan = structuredClone(options.plan);
17
+ const contexts = structuredClone(options.contexts);
18
+ if (identity.planDigest !== plan.identity || identity.contextDigest !== planDigest(contexts))
19
+ throw new Error("Component plan/context identity mismatch");
20
+ const owners = new WeakMap();
21
+ const current = () => isDeepStrictEqual(identity, options.currentIdentity());
22
+ const check = (signal) => { signal.throwIfAborted(); if (!current())
23
+ throw new Error("Component binding expired"); };
24
+ return {
25
+ async prepare(component, signal) {
26
+ check(signal);
27
+ if (!isDeepStrictEqual(plan.components[component.id], component))
28
+ throw new Error("Component plan mismatch");
29
+ const context = contexts[component.id];
30
+ if (context && !isDeepStrictEqual(context.binding, component.declaration.context_binding))
31
+ throw new Error("Component context binding mismatch");
32
+ const conditions = component.declaration.context_precondition;
33
+ if (conditions !== undefined && !Array.isArray(conditions))
34
+ throw new Error("Invalid component preconditions");
35
+ if (Array.isArray(conditions) && conditions.length > 0) {
36
+ if (!context)
37
+ throw new Error("Missing component context");
38
+ await verifyComponentContext(options.verifierBinary, context.snapshot, conditions, signal);
39
+ }
40
+ check(signal);
41
+ const guards = z.array(z.object({ name: z.string(), locked: z.boolean(), threshold: z.number().positive().optional() }).passthrough()).parse(component.declaration.guardrails);
42
+ if (!guards.some((guard) => guard.name === "read_only_execution" && guard.locked))
43
+ throw new Error("Component read-only guard is required");
44
+ const rowLimit = Math.min(10_000, guards.find((guard) => guard.name === "row_limit")?.threshold ?? 1000);
45
+ const timeoutMs = Math.min(120_000, (guards.find((guard) => guard.name === "statement_timeout")?.threshold ?? 30) * 1000);
46
+ const grants = [...new Map(component.steps.flatMap((step) => step.tools).map((grant) => [`${grant.source}:${grant.name}`, grant])).values()];
47
+ const operations = grants.map((grant) => {
48
+ if ((grant.source === "native" && grant.name === "query") || grant.source === "host:sql_execution:read_only")
49
+ return "query";
50
+ if ((grant.source === "native" && grant.name === "semantic_introspect") || grant.source === "host:semantic_introspection")
51
+ return "inspect";
52
+ throw new Error("Unsupported component tool binding");
53
+ });
54
+ const access = await options.prepare(component, identity, signal);
55
+ let closed = false;
56
+ let closing;
57
+ const close = () => { closed = true; return closing ??= access.close(); };
58
+ try {
59
+ check(signal);
60
+ }
61
+ catch (error) {
62
+ await close();
63
+ throw error;
64
+ }
65
+ const binding = {
66
+ isCurrent: current, close,
67
+ tools: grants.map((grant, index) => ({ ...grant,
68
+ inputSchema: operations[index] === "query" ? QUERY_SCHEMA : INSPECT_SCHEMA,
69
+ async execute(input, parent) {
70
+ check(parent);
71
+ if (closed)
72
+ throw new Error("Component access is closed");
73
+ const bounded = AbortSignal.any([parent, AbortSignal.timeout(timeoutMs)]);
74
+ let result;
75
+ if (operations[index] === "query") {
76
+ const parsed = queryInput.parse(input);
77
+ result = await access.query({ sql: parsed.sql, limit: Math.min(rowLimit, parsed.limit ?? rowLimit) }, bounded);
78
+ }
79
+ else {
80
+ emptyInput.parse(input);
81
+ result = await access.inspect(bounded);
82
+ }
83
+ check(bounded);
84
+ if (closed)
85
+ throw new Error("Component access is closed");
86
+ return result;
87
+ },
88
+ })),
89
+ async normalize(evidence, parent) { check(parent); if (closed)
90
+ throw new Error("Component access is closed"); return options.normalize(component, evidence, parent, structuredClone(context?.snapshot)); },
91
+ };
92
+ owners.set(binding, component);
93
+ return binding;
94
+ },
95
+ async runStep(run, binding) {
96
+ check(run.signal);
97
+ const component = owners.get(binding);
98
+ if (!component || !binding.isCurrent())
99
+ throw new Error("Invalid component binding");
100
+ const context = contexts[component.id];
101
+ const render = z.object({ render_blocks: z.array(z.object({ type: z.string(), fields: z.record(z.string(), z.string()) }).strict()).optional() }).passthrough().parse(component.declaration.effect ?? {}).render_blocks;
102
+ const surface = options.contextSurface ? options.contextSurface(component, run)
103
+ : context ? { kind: "snapshot", text: `Host semantic context:\n${JSON.stringify(context.snapshot)}` } : undefined;
104
+ const renderText = run.terminal && render?.length ? `Render output: Return only one JSON object with a blocks array. Each block has a type and its declared fields. Use only these block contracts (a trailing ? means optional): ${JSON.stringify(render)}. Copy data and definitions only from successful host tool or child results. A KPI label must identify its returned column; do not invent units or deltas. If evidence is insufficient, return {"status":"refused","message":"Insufficient verified data"}.` : "";
105
+ const surfaces = { prompt: run.prompt };
106
+ if (surface)
107
+ surfaces["context"] = surface.text;
108
+ if (renderText)
109
+ surfaces["render"] = renderText;
110
+ const prompt = [run.prompt, surface?.text ?? "", renderText].filter(Boolean).join("\n\n");
111
+ return options.step({ ...run, prompt, surfaces: Object.freeze(surfaces), contextKind: surface?.kind ?? "none" }, component, identity);
112
+ },
113
+ ...(options.persistRoot ? { persistRoot: options.persistRoot } : {}),
114
+ ...(options.verifyChild ? { verifyChild: options.verifyChild } : {}),
115
+ ...(options.onEvent ? { onEvent: options.onEvent } : {}),
116
+ };
117
+ }
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+ import type { DisclosurePolicy } from "../providers/zone.js";
3
+ /** The catalog fields the card may read. Everything else the document carries is dropped here. */
4
+ export declare const capabilityCatalogSchema: z.ZodObject<{
5
+ catalog_version: z.ZodLiteral<1>;
6
+ project: z.ZodDefault<z.ZodObject<{
7
+ name: z.ZodOptional<z.ZodString>;
8
+ data_source: z.ZodOptional<z.ZodString>;
9
+ }, z.core.$strip>>;
10
+ models: z.ZodDefault<z.ZodArray<z.ZodObject<{
11
+ description: z.ZodOptional<z.ZodString>;
12
+ name: z.ZodString;
13
+ primary_key: z.ZodDefault<z.ZodArray<z.ZodString>>;
14
+ columns: z.ZodDefault<z.ZodArray<z.ZodObject<{
15
+ description: z.ZodOptional<z.ZodString>;
16
+ name: z.ZodString;
17
+ type: z.ZodString;
18
+ not_null: z.ZodOptional<z.ZodBoolean>;
19
+ is_calculated: z.ZodOptional<z.ZodBoolean>;
20
+ is_hidden: z.ZodOptional<z.ZodBoolean>;
21
+ relationship: z.ZodOptional<z.ZodString>;
22
+ enum_values: z.ZodDefault<z.ZodArray<z.ZodObject<{
23
+ value: z.ZodString;
24
+ meaning: z.ZodString;
25
+ }, z.core.$strip>>>;
26
+ }, z.core.$strip>>>;
27
+ }, z.core.$strip>>>;
28
+ relationships: z.ZodDefault<z.ZodArray<z.ZodObject<{
29
+ name: z.ZodString;
30
+ models: z.ZodArray<z.ZodString>;
31
+ join_type: z.ZodString;
32
+ condition: z.ZodString;
33
+ }, z.core.$strip>>>;
34
+ cubes: z.ZodDefault<z.ZodArray<z.ZodObject<{
35
+ description: z.ZodOptional<z.ZodString>;
36
+ name: z.ZodString;
37
+ base_object: z.ZodString;
38
+ measures: z.ZodDefault<z.ZodArray<z.ZodObject<{
39
+ description: z.ZodOptional<z.ZodString>;
40
+ name: z.ZodString;
41
+ type: z.ZodString;
42
+ granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
43
+ date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
44
+ }, z.core.$strip>>>;
45
+ dimensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
46
+ description: z.ZodOptional<z.ZodString>;
47
+ name: z.ZodString;
48
+ type: z.ZodString;
49
+ granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
50
+ date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
51
+ }, z.core.$strip>>>;
52
+ time_dimensions: z.ZodDefault<z.ZodArray<z.ZodObject<{
53
+ description: z.ZodOptional<z.ZodString>;
54
+ name: z.ZodString;
55
+ type: z.ZodString;
56
+ granularities: z.ZodDefault<z.ZodArray<z.ZodString>>;
57
+ date_range: z.ZodOptional<z.ZodTuple<[z.ZodString, z.ZodString], null>>;
58
+ }, z.core.$strip>>>;
59
+ }, z.core.$strip>>>;
60
+ views: z.ZodDefault<z.ZodArray<z.ZodObject<{
61
+ description: z.ZodOptional<z.ZodString>;
62
+ name: z.ZodString;
63
+ }, z.core.$strip>>>;
64
+ }, z.core.$strip>;
65
+ export type CapabilityCatalog = z.infer<typeof capabilityCatalogSchema>;
66
+ export interface CapabilityCard {
67
+ readonly text: string;
68
+ readonly bytes: number;
69
+ readonly truncated: boolean;
70
+ /** sha256 hex over `text`; the value a public step's `context` surface fingerprint carries when it received the card. */
71
+ readonly digest: string;
72
+ /** Lines dropped by the size bound, for the trace warning. */
73
+ readonly omittedLines: number;
74
+ }
75
+ /** The disclosure-policy fields the card may carry. Nothing else of the policy is read. */
76
+ export type CardDisclosureLimits = Pick<DisclosurePolicy, "min_group_size" | "max_rows" | "allowed_shapes">;
77
+ export interface CapabilityCardOptions {
78
+ /** Upper bound on the rendered card in UTF-8 bytes. Default 32 KiB. */
79
+ readonly maxBytes?: number;
80
+ /**
81
+ * The bound disclosure policy, when there is one. The card states its shape limits so the
82
+ * planner plans within them instead of planning slots egress then refuses. Only the three
83
+ * fields of `CardDisclosureLimits` are read; patterns and the judge budget never reach the card.
84
+ */
85
+ readonly disclosure?: CardDisclosureLimits;
86
+ }
87
+ export declare const DEFAULT_CARD_MAX_BYTES: number;
88
+ /** Renders the card's lines from an already allow-listed catalog. Pure and order-preserving. */
89
+ export declare function renderCapabilityCardLines(catalog: CapabilityCatalog, disclosure?: CardDisclosureLimits): string[];
90
+ /**
91
+ * Builds the card from a raw catalog document. Deterministic: the same
92
+ * catalog yields the same bytes. Size-bounded: when the rendering exceeds
93
+ * `maxBytes`, trailing lines are dropped until it fits and a marker line
94
+ * says how many were omitted; the caller traces that as a warning. The
95
+ * disclosure limits are never dropped: with a policy bound, the whole head
96
+ * is kept even when that alone exceeds `maxBytes`.
97
+ */
98
+ export declare function buildCapabilityCard(catalog: unknown, options?: CapabilityCardOptions): CapabilityCard;