@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,302 @@
1
+ /**
2
+ * Host-owned contract for deterministic, noninteractive dispatch. This is
3
+ * intentionally not the interactive terminal protocol: it contains no bytes
4
+ * from a PTY, dispatcher stream, prompt, tool input/output, or credential.
5
+ */
6
+ export const NATIVE_PRODUCER_VERSION = "1";
7
+ const MAX_RESULT_TEXT = 16 * 1024;
8
+ const MAX_CASSETTE_BYTES = 64 * 1024;
9
+ const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
10
+ const SAFE_DIGEST = /^sha256:[a-f0-9]{64}$/;
11
+ /** Stable, prompt-free cassette selector for one host-owned idempotent run. */
12
+ export function nativeProducerCassetteKey(request) {
13
+ return `${request.vendor}__${request.agentId}__${request.idempotencyKey}`;
14
+ }
15
+ /** Runs one noninteractive dispatch and always returns a typed, fail-closed response. */
16
+ export async function produceNoninteractiveNative(request, options) {
17
+ const parsed = validateRequest(request);
18
+ if (parsed === undefined)
19
+ return failed("invalid_request");
20
+ if (parsed.version !== NATIVE_PRODUCER_VERSION)
21
+ return failed("unsupported_version");
22
+ if (parsed.scope.bindingDigest !== options.fence.bindingDigest || !options.fence.validateRun(parsed))
23
+ return failed("scope_mismatch");
24
+ if (options.signal?.aborted)
25
+ return failed("cancelled");
26
+ const controller = new AbortController();
27
+ const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;
28
+ let stop;
29
+ const timeout = setTimeout(() => {
30
+ stop = "timeout";
31
+ controller.abort();
32
+ }, timeoutMs);
33
+ const cancel = () => {
34
+ stop = "cancelled";
35
+ controller.abort();
36
+ };
37
+ options.signal?.addEventListener("abort", cancel, { once: true });
38
+ const events = [];
39
+ try {
40
+ const dispatch = options.dispatch({
41
+ vendor: parsed.vendor,
42
+ agentId: parsed.agentId,
43
+ input: parsed.input,
44
+ scope: parsed.scope,
45
+ idempotencyKey: parsed.idempotencyKey,
46
+ signal: controller.signal,
47
+ fence: options.fence,
48
+ // Keep the private, potentially rich event stream inside the host. The
49
+ // bounded public lifecycle below is intentionally synthesized instead.
50
+ onEvent: (event) => events.push(event),
51
+ });
52
+ const result = await awaitWithAbort(dispatch, controller.signal);
53
+ if (stop === "timeout")
54
+ return failed("timeout");
55
+ if (!options.fence.validateRun(parsed))
56
+ return failed("scope_mismatch");
57
+ return complete(result, parsed, options.fence);
58
+ }
59
+ catch (error) {
60
+ if (stop === "timeout")
61
+ return failed("timeout");
62
+ if (isAbortError(error) || controller.signal.aborted)
63
+ return failed("cancelled");
64
+ return failed("producer_failed");
65
+ }
66
+ finally {
67
+ clearTimeout(timeout);
68
+ options.signal?.removeEventListener("abort", cancel);
69
+ // The dispatcher callback is intentionally retained only until completion.
70
+ events.length = 0;
71
+ }
72
+ }
73
+ /** Replays a sanitized producer cassette; absence, drift, and scope changes fail closed. */
74
+ export function replayNoninteractiveNative(request, cassette, fence) {
75
+ const parsed = validateRequest(request);
76
+ if (parsed === undefined)
77
+ return failed("invalid_request");
78
+ if (parsed.version !== NATIVE_PRODUCER_VERSION)
79
+ return failed("unsupported_version");
80
+ const parsedCassette = validateCassette(cassette, parsed.input);
81
+ if (isRecord(cassette) && typeof cassette.version === "string" && cassette.version !== NATIVE_PRODUCER_VERSION) {
82
+ return failed("unsupported_version");
83
+ }
84
+ if (parsedCassette === undefined)
85
+ return failed("missing_replay");
86
+ if (parsed.scope.bindingDigest !== fence.bindingDigest || !fence.validateRun(parsed))
87
+ return failed("scope_mismatch");
88
+ if (parsedCassette.version !== NATIVE_PRODUCER_VERSION)
89
+ return failed("unsupported_version");
90
+ if (parsedCassette.vendor !== parsed.vendor ||
91
+ parsedCassette.agentId !== parsed.agentId ||
92
+ !sameScope(parsedCassette.scope, parsed.scope))
93
+ return failed("scope_mismatch");
94
+ if (parsedCassette.key !== nativeProducerCassetteKey(parsed))
95
+ return failed("missing_replay");
96
+ if (parsedCassette.response.status === "completed" && !parsedCassette.response.artifacts.every((artifact) => fence.validateArtifact(artifact, parsed))) {
97
+ return failed("scope_mismatch");
98
+ }
99
+ return parsedCassette.response;
100
+ }
101
+ /** Creates a checked-in/replayable record only from an already-safe response. */
102
+ export function createNativeProducerCassette(key, request, response, fence) {
103
+ const parsed = validateRequest(request);
104
+ const parsedResponse = validateResponse(response, parsed?.input);
105
+ if (!parsed || !parsedResponse || parsed.scope.bindingDigest !== fence.bindingDigest || !fence.validateRun(parsed) || !SAFE_ID.test(key) || (parsedResponse.status === "completed" && !parsedResponse.artifacts.every((artifact) => fence.validateArtifact(artifact, parsed)))) {
106
+ throw new Error("native producer cassette is invalid");
107
+ }
108
+ if (key !== nativeProducerCassetteKey(parsed)) {
109
+ throw new Error("native producer cassette key does not match its idempotent request");
110
+ }
111
+ const cassette = {
112
+ version: NATIVE_PRODUCER_VERSION,
113
+ key,
114
+ vendor: parsed.vendor,
115
+ agentId: parsed.agentId,
116
+ scope: parsed.scope,
117
+ response: parsedResponse,
118
+ };
119
+ if (Buffer.byteLength(JSON.stringify(cassette)) > MAX_CASSETTE_BYTES) {
120
+ throw new Error("native producer cassette exceeds its bounded contract");
121
+ }
122
+ return cassette;
123
+ }
124
+ /**
125
+ * Adapts existing subscription dispatchers without widening their protocol.
126
+ * The native producer only admits the vendor selected by the route options.
127
+ */
128
+ export function createRouteNativeProducer(route, baseOptions) {
129
+ return async (input) => {
130
+ const choice = baseOptions.authChoice;
131
+ if (choice.mode !== "subscription" || choice.provider !== input.vendor) {
132
+ throw new Error("native producer vendor does not match the configured route");
133
+ }
134
+ const request = {
135
+ version: NATIVE_PRODUCER_VERSION,
136
+ vendor: input.vendor,
137
+ agentId: input.agentId,
138
+ input: input.input,
139
+ scope: input.scope,
140
+ idempotencyKey: input.idempotencyKey,
141
+ };
142
+ if (!input.fence.validateRoute(request, baseOptions.userProject)) {
143
+ throw new Error("native producer route fence rejected the request");
144
+ }
145
+ const result = await route({
146
+ ...baseOptions,
147
+ question: input.input,
148
+ agentId: input.agentId,
149
+ onEvent: input.onEvent,
150
+ signal: input.signal,
151
+ });
152
+ return { text: resultText(result) };
153
+ };
154
+ }
155
+ function resultText(result) {
156
+ if (result.backend === "agent") {
157
+ if (result.kind === "refusal")
158
+ throw new Error("producer refused result");
159
+ return JSON.stringify(result.envelope);
160
+ }
161
+ return result.finalText;
162
+ }
163
+ function complete(value, request, fence) {
164
+ if (!isRecord(value) || typeof value.text !== "string" || !value.text.trim())
165
+ return failed("malformed_output");
166
+ const text = sanitizeResultText(value.text, request.input);
167
+ if (text === undefined)
168
+ return failed("malformed_output");
169
+ const artifacts = value.artifacts === undefined ? [] : value.artifacts;
170
+ if (!Array.isArray(artifacts) || !artifacts.every((artifact) => validArtifact(artifact) && fence.validateArtifact(artifact, request))) {
171
+ return failed("malformed_output");
172
+ }
173
+ const rebuiltArtifacts = rebuildArtifacts(artifacts);
174
+ if (!rebuiltArtifacts)
175
+ return failed("malformed_output");
176
+ return {
177
+ version: NATIVE_PRODUCER_VERSION,
178
+ status: "completed",
179
+ lifecycle: [{ state: "accepted" }, { state: "running" }, { state: "completed" }],
180
+ result: { text },
181
+ artifacts: rebuiltArtifacts,
182
+ };
183
+ }
184
+ function failed(code) {
185
+ return {
186
+ version: NATIVE_PRODUCER_VERSION,
187
+ status: "failed",
188
+ lifecycle: [{ state: "accepted" }, { state: "running" }, { state: "failed" }],
189
+ error: { code },
190
+ artifacts: [],
191
+ };
192
+ }
193
+ function validateRequest(value) {
194
+ if (!isRecord(value) || !onlyKeys(value, ["version", "vendor", "agentId", "input", "scope", "idempotencyKey"]))
195
+ return undefined;
196
+ if (typeof value.version !== "string" || (value.vendor !== "claude" && value.vendor !== "codex") || typeof value.agentId !== "string" || typeof value.input !== "string" || typeof value.idempotencyKey !== "string")
197
+ return undefined;
198
+ if (!SAFE_ID.test(value.agentId) || !SAFE_ID.test(value.idempotencyKey) || value.input.length === 0 || value.input.length > MAX_RESULT_TEXT)
199
+ return undefined;
200
+ const scope = validateScope(value.scope);
201
+ if (!scope)
202
+ return undefined;
203
+ return { version: value.version, vendor: value.vendor, agentId: value.agentId, input: value.input, scope, idempotencyKey: value.idempotencyKey };
204
+ }
205
+ function validateCassette(value, prompt) {
206
+ if (!isRecord(value) || !onlyKeys(value, ["version", "key", "vendor", "agentId", "scope", "response"]))
207
+ return undefined;
208
+ if (value.version !== NATIVE_PRODUCER_VERSION || !SAFE_ID.test(String(value.key)) || (value.vendor !== "claude" && value.vendor !== "codex") || !SAFE_ID.test(String(value.agentId)))
209
+ return undefined;
210
+ const scope = validateScope(value.scope);
211
+ const response = validateResponse(value.response, prompt);
212
+ if (!scope || !response)
213
+ return undefined;
214
+ return { version: NATIVE_PRODUCER_VERSION, key: value.key, vendor: value.vendor, agentId: value.agentId, scope, response };
215
+ }
216
+ function validateResponse(value, prompt) {
217
+ if (!isRecord(value) || value.version !== NATIVE_PRODUCER_VERSION || !Array.isArray(value.lifecycle))
218
+ return undefined;
219
+ if (value.status === "completed" && isRecord(value.result) && typeof value.result.text === "string" && Array.isArray(value.artifacts)) {
220
+ if (!onlyKeys(value, ["version", "status", "lifecycle", "result", "artifacts"]))
221
+ return undefined;
222
+ const lifecycle = rebuildLifecycle(value.lifecycle, "completed");
223
+ const text = sanitizeResultText(value.result.text, prompt);
224
+ const artifacts = rebuildArtifacts(value.artifacts);
225
+ if (!lifecycle || text === undefined || !artifacts || !onlyKeys(value.result, ["text"]))
226
+ return undefined;
227
+ return { version: NATIVE_PRODUCER_VERSION, status: "completed", lifecycle, result: { text }, artifacts };
228
+ }
229
+ if (value.status === "failed" && isRecord(value.error) && typeof value.error.code === "string" && Array.isArray(value.artifacts) && value.artifacts.length === 0 && validErrorCode(value.error.code)) {
230
+ if (!onlyKeys(value, ["version", "status", "lifecycle", "error", "artifacts"]))
231
+ return undefined;
232
+ const lifecycle = rebuildLifecycle(value.lifecycle, "failed");
233
+ if (!lifecycle || !onlyKeys(value.error, ["code"]))
234
+ return undefined;
235
+ return { version: NATIVE_PRODUCER_VERSION, status: "failed", lifecycle, error: { code: value.error.code }, artifacts: [] };
236
+ }
237
+ return undefined;
238
+ }
239
+ function validateScope(value) {
240
+ if (!isRecord(value) || !onlyKeys(value, ["bindingDigest"]) || typeof value.bindingDigest !== "string" || !SAFE_DIGEST.test(value.bindingDigest))
241
+ return undefined;
242
+ return { bindingDigest: value.bindingDigest };
243
+ }
244
+ function validArtifact(value) {
245
+ return isRecord(value) && onlyKeys(value, ["id", "kind", "digest", "idempotencyKey"]) && SAFE_ID.test(String(value.id)) && SAFE_ID.test(String(value.idempotencyKey)) && SAFE_DIGEST.test(String(value.digest)) && ["dashboard", "report", "chart"].includes(String(value.kind));
246
+ }
247
+ function sanitizeResultText(text, prompt) {
248
+ if (Buffer.byteLength(text) > MAX_RESULT_TEXT ||
249
+ /[\u0000-\u001f\u007f-\u009f]/.test(text) ||
250
+ (prompt !== undefined && text.includes(prompt)) ||
251
+ /^\s*(?:\{|\[)/.test(text) ||
252
+ /\b(?:tool(?:[_ -]?(?:call|result|input|output))?|mcp(?:[_ -]?(?:tool|server|message))?|capability(?:[_ -]?(?:token|credential|grant))?|(?:api[_ -]?key|secret|token|password|authorization|credential))\b\s*[:=]|\bBearer\s+[A-Za-z0-9._-]+|\/(?:Users|home)\//i.test(text))
253
+ return undefined;
254
+ return text.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, "[redacted-id]");
255
+ }
256
+ function awaitWithAbort(promise, signal) {
257
+ if (signal.aborted)
258
+ return Promise.reject(new DOMException("cancelled", "AbortError"));
259
+ return new Promise((resolve, reject) => {
260
+ const abort = () => reject(new DOMException("cancelled", "AbortError"));
261
+ signal.addEventListener("abort", abort, { once: true });
262
+ promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
263
+ });
264
+ }
265
+ function isAbortError(error) {
266
+ return error instanceof DOMException && error.name === "AbortError";
267
+ }
268
+ function validErrorCode(value) {
269
+ return ["unsupported_version", "scope_mismatch", "invalid_request", "malformed_output", "missing_replay", "timeout", "cancelled", "producer_failed"].includes(value);
270
+ }
271
+ function rebuildLifecycle(value, terminal) {
272
+ if (value.length !== 3)
273
+ return undefined;
274
+ const states = ["accepted", "running", terminal];
275
+ const rebuilt = [];
276
+ for (let index = 0; index < states.length; index += 1) {
277
+ const entry = value[index];
278
+ if (!isRecord(entry) || !onlyKeys(entry, ["state"]) || entry.state !== states[index])
279
+ return undefined;
280
+ rebuilt.push({ state: states[index] });
281
+ }
282
+ return rebuilt;
283
+ }
284
+ function rebuildArtifacts(value) {
285
+ const rebuilt = [];
286
+ for (const artifact of value) {
287
+ if (!validArtifact(artifact))
288
+ return undefined;
289
+ const record = artifact;
290
+ rebuilt.push({ id: record.id, kind: record.kind, digest: record.digest, idempotencyKey: record.idempotencyKey });
291
+ }
292
+ return rebuilt;
293
+ }
294
+ function sameScope(a, b) {
295
+ return a.bindingDigest === b.bindingDigest;
296
+ }
297
+ function isRecord(value) {
298
+ return typeof value === "object" && value !== null && !Array.isArray(value);
299
+ }
300
+ function onlyKeys(value, allowed) {
301
+ return Object.keys(value).every((key) => allowed.includes(key));
302
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Resolves a binary shipped by an installed npm package's own `bin` field, using Node's module
3
+ * resolution rooted at this file — not `PATH`, and not a directory walk.
4
+ *
5
+ * This is what lets the `warble` binary and its dispatchers resolve to the exact pinned
6
+ * `@warble/*` version declared in `apps/genbi/package.json`, regardless of what else happens to be
7
+ * on `PATH` and regardless of how the current process was launched. `pnpm run <script>` prepends
8
+ * `node_modules/.bin` to `PATH`, so a bare `PATH` probe already tends to find a workspace-installed
9
+ * package when the process was started that way — but a process started directly (`node
10
+ * dist-server/server/bin.js`, a packaged deployment, a test runner) inherits no such `PATH`, and
11
+ * even when it does, `PATH` order rather than the pinned dependency version is what would decide
12
+ * the outcome. Resolving through `require.resolve` instead is deterministic in both cases: it
13
+ * always finds the version this package actually depends on.
14
+ *
15
+ * Returns `undefined` — never throws — when the package isn't installed, declares no matching
16
+ * `bin` entry, or the resolved file doesn't exist on disk, so callers can fall through to their
17
+ * next resolution tier.
18
+ */
19
+ export declare function resolveInstalledPackageBin(packageName: string, binName: string): string | undefined;
20
+ /**
21
+ * Named, explicit opt-in that re-enables the legacy "sibling `warble` git checkout"
22
+ * development-mode resolution tier in `resolveWarbleBinary`, `resolveAgentSdkCli`, and
23
+ * `resolveCodexLocalCli`. Off by default: once the pinned `@warble/*` packages are installed,
24
+ * silently preferring an ambient sibling checkout over them would make it easy to test against the
25
+ * wrong Warble version without noticing. Set to `"1"` only when deliberately developing GenBI
26
+ * against an uninstalled, unreleased Warble checkout.
27
+ */
28
+ export declare function isWarbleSiblingCheckoutDevModeEnabled(): boolean;
@@ -0,0 +1,47 @@
1
+ import { createRequire } from "node:module";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import path from "node:path";
4
+ /**
5
+ * Resolves a binary shipped by an installed npm package's own `bin` field, using Node's module
6
+ * resolution rooted at this file — not `PATH`, and not a directory walk.
7
+ *
8
+ * This is what lets the `warble` binary and its dispatchers resolve to the exact pinned
9
+ * `@warble/*` version declared in `apps/genbi/package.json`, regardless of what else happens to be
10
+ * on `PATH` and regardless of how the current process was launched. `pnpm run <script>` prepends
11
+ * `node_modules/.bin` to `PATH`, so a bare `PATH` probe already tends to find a workspace-installed
12
+ * package when the process was started that way — but a process started directly (`node
13
+ * dist-server/server/bin.js`, a packaged deployment, a test runner) inherits no such `PATH`, and
14
+ * even when it does, `PATH` order rather than the pinned dependency version is what would decide
15
+ * the outcome. Resolving through `require.resolve` instead is deterministic in both cases: it
16
+ * always finds the version this package actually depends on.
17
+ *
18
+ * Returns `undefined` — never throws — when the package isn't installed, declares no matching
19
+ * `bin` entry, or the resolved file doesn't exist on disk, so callers can fall through to their
20
+ * next resolution tier.
21
+ */
22
+ export function resolveInstalledPackageBin(packageName, binName) {
23
+ try {
24
+ const require = createRequire(import.meta.url);
25
+ const pkgJsonPath = require.resolve(`${packageName}/package.json`);
26
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
27
+ const binField = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.[binName];
28
+ if (binField === undefined)
29
+ return undefined;
30
+ const resolved = path.resolve(path.dirname(pkgJsonPath), binField);
31
+ return existsSync(resolved) ? resolved : undefined;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ /**
38
+ * Named, explicit opt-in that re-enables the legacy "sibling `warble` git checkout"
39
+ * development-mode resolution tier in `resolveWarbleBinary`, `resolveAgentSdkCli`, and
40
+ * `resolveCodexLocalCli`. Off by default: once the pinned `@warble/*` packages are installed,
41
+ * silently preferring an ambient sibling checkout over them would make it easy to test against the
42
+ * wrong Warble version without noticing. Set to `"1"` only when deliberately developing GenBI
43
+ * against an uninstalled, unreleased Warble checkout.
44
+ */
45
+ export function isWarbleSiblingCheckoutDevModeEnabled() {
46
+ return process.env.WREN_HARNESS_ALLOW_WARBLE_SIBLING_CHECKOUT === "1";
47
+ }
@@ -0,0 +1,7 @@
1
+ import type { LanguageModel } from "ai";
2
+ export declare const ANTHROPIC_ADAPTER_ID = "anthropic";
3
+ export interface AnthropicAdapterConfig {
4
+ readonly model: string;
5
+ readonly apiKey?: string;
6
+ }
7
+ export declare function createAnthropicAdapter(config: AnthropicAdapterConfig): LanguageModel;
@@ -0,0 +1,6 @@
1
+ import { createAnthropic } from "@ai-sdk/anthropic";
2
+ export const ANTHROPIC_ADAPTER_ID = "anthropic";
3
+ export function createAnthropicAdapter(config) {
4
+ const provider = createAnthropic(config.apiKey !== undefined ? { apiKey: config.apiKey } : {});
5
+ return provider.languageModel(config.model);
6
+ }
@@ -0,0 +1,4 @@
1
+ import { MockLanguageModelV4 } from "ai/test";
2
+ export declare const MOCK_ADAPTER_ID = "mock";
3
+ export type MockAdapterConfig = ConstructorParameters<typeof MockLanguageModelV4>[0];
4
+ export declare function createMockAdapter(config?: MockAdapterConfig): MockLanguageModelV4;
@@ -0,0 +1,5 @@
1
+ import { MockLanguageModelV4 } from "ai/test";
2
+ export const MOCK_ADAPTER_ID = "mock";
3
+ export function createMockAdapter(config = {}) {
4
+ return new MockLanguageModelV4(config);
5
+ }
@@ -0,0 +1,10 @@
1
+ import type { LanguageModel } from "ai";
2
+ export declare const OPENAI_COMPATIBLE_ADAPTER_ID = "openai-compatible";
3
+ export interface OpenAICompatibleAdapterConfig {
4
+ readonly baseURL: string;
5
+ readonly model: string;
6
+ readonly apiKey?: string;
7
+ /** Provider name reported in telemetry/errors. Defaults to the adapter id. */
8
+ readonly name?: string;
9
+ }
10
+ export declare function createOpenAICompatibleAdapter(config: OpenAICompatibleAdapterConfig): LanguageModel;
@@ -0,0 +1,10 @@
1
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
2
+ export const OPENAI_COMPATIBLE_ADAPTER_ID = "openai-compatible";
3
+ export function createOpenAICompatibleAdapter(config) {
4
+ const provider = createOpenAICompatible({
5
+ name: config.name ?? OPENAI_COMPATIBLE_ADAPTER_ID,
6
+ baseURL: config.baseURL,
7
+ ...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),
8
+ });
9
+ return provider.languageModel(config.model);
10
+ }
@@ -0,0 +1,23 @@
1
+ import type { LanguageModel } from "ai";
2
+ import type { ProviderRegistry } from "./registry.js";
3
+ /** Names an adapter id + the config to realize it with. */
4
+ export interface AdapterSpec<Config = unknown> {
5
+ readonly adapter: string;
6
+ readonly config: Config;
7
+ }
8
+ /**
9
+ * Runtime-injected tier -> adapter binding. This is a plain object supplied
10
+ * by the caller at run time — it is never read from a bundle and must never
11
+ * be committed to a bundle fixture. `tier` names are open strings owned by
12
+ * the bundle's steps; the binding only needs to cover the tiers a given run
13
+ * actually uses.
14
+ */
15
+ export interface TierBinding {
16
+ readonly tiers: Readonly<Record<string, AdapterSpec>>;
17
+ }
18
+ /** Resolve an open-string tier name to a concrete language model instance. */
19
+ export declare function resolveTierModel(binding: TierBinding, tier: string, registry: ProviderRegistry): LanguageModel;
20
+ /** Convenience: resolve the model for a bundle step via its `tier` field. */
21
+ export declare function resolveStepModel(step: {
22
+ readonly tier: string;
23
+ }, binding: TierBinding, registry: ProviderRegistry): LanguageModel;
@@ -0,0 +1,13 @@
1
+ import { UnknownTierError } from "./errors.js";
2
+ /** Resolve an open-string tier name to a concrete language model instance. */
3
+ export function resolveTierModel(binding, tier, registry) {
4
+ const spec = binding.tiers[tier];
5
+ if (!spec) {
6
+ throw new UnknownTierError(tier);
7
+ }
8
+ return registry.create(spec.adapter, spec.config);
9
+ }
10
+ /** Convenience: resolve the model for a bundle step via its `tier` field. */
11
+ export function resolveStepModel(step, binding, registry) {
12
+ return resolveTierModel(binding, step.tier, registry);
13
+ }
@@ -0,0 +1,6 @@
1
+ export declare class UnknownAdapterError extends Error {
2
+ constructor(adapterId: string);
3
+ }
4
+ export declare class UnknownTierError extends Error {
5
+ constructor(tier: string);
6
+ }
@@ -0,0 +1,12 @@
1
+ export class UnknownAdapterError extends Error {
2
+ constructor(adapterId) {
3
+ super(`no provider adapter registered for id: "${adapterId}"`);
4
+ this.name = "UnknownAdapterError";
5
+ }
6
+ }
7
+ export class UnknownTierError extends Error {
8
+ constructor(tier) {
9
+ super(`no binding entry for tier: "${tier}"`);
10
+ this.name = "UnknownTierError";
11
+ }
12
+ }
@@ -0,0 +1,18 @@
1
+ export { UnknownAdapterError, UnknownTierError } from "./errors.js";
2
+ export { createProviderRegistry } from "./registry.js";
3
+ export type { AdapterFactory, ProviderRegistry } from "./registry.js";
4
+ export { resolveStepModel, resolveTierModel } from "./binding.js";
5
+ export type { AdapterSpec, TierBinding } from "./binding.js";
6
+ export { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
7
+ export type { MockAdapterConfig } from "./adapters/mock.js";
8
+ export { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
9
+ export type { OpenAICompatibleAdapterConfig } from "./adapters/openai-compatible.js";
10
+ export { ANTHROPIC_ADAPTER_ID, createAnthropicAdapter } from "./adapters/anthropic.js";
11
+ export type { AnthropicAdapterConfig } from "./adapters/anthropic.js";
12
+ import { type ProviderRegistry } from "./registry.js";
13
+ /**
14
+ * A provider registry pre-populated with the built-in adapters
15
+ * (`mock`, `openai-compatible`, `anthropic`). Callers can still
16
+ * `register()` additional adapter ids onto the returned registry.
17
+ */
18
+ export declare function createDefaultProviderRegistry(): ProviderRegistry;
@@ -0,0 +1,22 @@
1
+ export { UnknownAdapterError, UnknownTierError } from "./errors.js";
2
+ export { createProviderRegistry } from "./registry.js";
3
+ export { resolveStepModel, resolveTierModel } from "./binding.js";
4
+ export { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
5
+ export { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
6
+ export { ANTHROPIC_ADAPTER_ID, createAnthropicAdapter } from "./adapters/anthropic.js";
7
+ import { createAnthropicAdapter, ANTHROPIC_ADAPTER_ID } from "./adapters/anthropic.js";
8
+ import { createMockAdapter, MOCK_ADAPTER_ID } from "./adapters/mock.js";
9
+ import { createOpenAICompatibleAdapter, OPENAI_COMPATIBLE_ADAPTER_ID, } from "./adapters/openai-compatible.js";
10
+ import { createProviderRegistry } from "./registry.js";
11
+ /**
12
+ * A provider registry pre-populated with the built-in adapters
13
+ * (`mock`, `openai-compatible`, `anthropic`). Callers can still
14
+ * `register()` additional adapter ids onto the returned registry.
15
+ */
16
+ export function createDefaultProviderRegistry() {
17
+ const registry = createProviderRegistry();
18
+ registry.register(MOCK_ADAPTER_ID, createMockAdapter);
19
+ registry.register(OPENAI_COMPATIBLE_ADAPTER_ID, createOpenAICompatibleAdapter);
20
+ registry.register(ANTHROPIC_ADAPTER_ID, createAnthropicAdapter);
21
+ return registry;
22
+ }
@@ -0,0 +1,16 @@
1
+ import type { LanguageModel } from "ai";
2
+ export type AdapterFactory<Config = unknown> = (config: Config) => LanguageModel;
3
+ export interface ProviderRegistry {
4
+ /** Register (or override) the adapter for an open-string adapter id. */
5
+ register<Config = unknown>(adapterId: string, factory: AdapterFactory<Config>): void;
6
+ /** Whether an adapter is registered for this id. */
7
+ has(adapterId: string): boolean;
8
+ /** Realize a language model instance for the given adapter id + config. */
9
+ create(adapterId: string, config: unknown): LanguageModel;
10
+ }
11
+ /**
12
+ * Create an empty, injectable provider registry keyed on an open-string
13
+ * adapter id. Callers register built-in and/or custom adapters onto it;
14
+ * the registry itself has no opinion on what ids exist.
15
+ */
16
+ export declare function createProviderRegistry(): ProviderRegistry;
@@ -0,0 +1,24 @@
1
+ import { UnknownAdapterError } from "./errors.js";
2
+ /**
3
+ * Create an empty, injectable provider registry keyed on an open-string
4
+ * adapter id. Callers register built-in and/or custom adapters onto it;
5
+ * the registry itself has no opinion on what ids exist.
6
+ */
7
+ export function createProviderRegistry() {
8
+ const adapters = new Map();
9
+ return {
10
+ register(adapterId, factory) {
11
+ adapters.set(adapterId, factory);
12
+ },
13
+ has(adapterId) {
14
+ return adapters.has(adapterId);
15
+ },
16
+ create(adapterId, config) {
17
+ const factory = adapters.get(adapterId);
18
+ if (!factory) {
19
+ throw new UnknownAdapterError(adapterId);
20
+ }
21
+ return factory(config);
22
+ },
23
+ };
24
+ }