@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,13 @@
1
+ /**
2
+ * A minimal, structural validator for the JSON Schema subset an
3
+ * `agent.output_schema` document actually uses (`type`, `properties`,
4
+ * `required`, `items`, `const`, and a `type` array for nullable unions like
5
+ * `["string", "null"]`). This is deliberately not a general-purpose JSON
6
+ * Schema engine — `jsonSchema()` from the AI SDK performs *no* runtime
7
+ * validation on its own unless given a `validate` callback (confirmed by
8
+ * reading `safeValidateTypes`: a schema with no `.validate` always reports
9
+ * success), so this is what gives the envelope stage real teeth: a
10
+ * malformed model response is rejected, not silently passed through.
11
+ */
12
+ export type JsonSchemaDocument = Record<string, unknown>;
13
+ export declare function collectJsonSchemaErrors(schema: JsonSchemaDocument, value: unknown): string[];
@@ -0,0 +1,88 @@
1
+ export function collectJsonSchemaErrors(schema, value) {
2
+ const errors = [];
3
+ validateNode(schema, value, "$", errors);
4
+ return errors;
5
+ }
6
+ function validateNode(schema, value, path, errors) {
7
+ if ("const" in schema) {
8
+ if (value !== schema.const) {
9
+ errors.push(`${path}: expected const ${JSON.stringify(schema.const)}, got ${JSON.stringify(value)}`);
10
+ }
11
+ return;
12
+ }
13
+ const type = schema.type;
14
+ if (type !== undefined && !matchesType(type, value)) {
15
+ errors.push(`${path}: expected type ${JSON.stringify(type)}, got ${describeType(value)}`);
16
+ return;
17
+ }
18
+ if (includesType(type, "object") && isPlainObject(value)) {
19
+ validateObjectShape(schema, value, path, errors);
20
+ }
21
+ if (includesType(type, "array") && Array.isArray(value)) {
22
+ validateArrayItems(schema, value, path, errors);
23
+ }
24
+ }
25
+ function validateObjectShape(schema, value, path, errors) {
26
+ const required = Array.isArray(schema.required) ? schema.required : [];
27
+ for (const key of required) {
28
+ if (typeof key === "string" && !(key in value)) {
29
+ errors.push(`${path}: missing required property "${key}"`);
30
+ }
31
+ }
32
+ const properties = isPlainObject(schema.properties) ? schema.properties : undefined;
33
+ if (!properties)
34
+ return;
35
+ for (const [key, propSchema] of Object.entries(properties)) {
36
+ if (key in value && isPlainObject(propSchema)) {
37
+ validateNode(propSchema, value[key], `${path}.${key}`, errors);
38
+ }
39
+ }
40
+ }
41
+ function validateArrayItems(schema, value, path, errors) {
42
+ if (!isPlainObject(schema.items))
43
+ return;
44
+ const itemsSchema = schema.items;
45
+ value.forEach((item, index) => validateNode(itemsSchema, item, `${path}[${index}]`, errors));
46
+ }
47
+ function matchesType(type, value) {
48
+ if (Array.isArray(type)) {
49
+ return type.some((candidate) => matchesSingleType(candidate, value));
50
+ }
51
+ return matchesSingleType(type, value);
52
+ }
53
+ function includesType(type, candidate) {
54
+ if (type === undefined)
55
+ return true;
56
+ return Array.isArray(type) ? type.includes(candidate) : type === candidate;
57
+ }
58
+ function matchesSingleType(type, value) {
59
+ switch (type) {
60
+ case "object":
61
+ return isPlainObject(value);
62
+ case "array":
63
+ return Array.isArray(value);
64
+ case "string":
65
+ return typeof value === "string";
66
+ case "number":
67
+ case "integer":
68
+ return typeof value === "number";
69
+ case "boolean":
70
+ return typeof value === "boolean";
71
+ case "null":
72
+ return value === null;
73
+ default:
74
+ // Unknown/unsupported schema type keyword — don't fail closed on
75
+ // something this minimal validator doesn't understand.
76
+ return true;
77
+ }
78
+ }
79
+ function isPlainObject(value) {
80
+ return typeof value === "object" && value !== null && !Array.isArray(value);
81
+ }
82
+ function describeType(value) {
83
+ if (value === null)
84
+ return "null";
85
+ if (Array.isArray(value))
86
+ return "array";
87
+ return typeof value;
88
+ }
@@ -0,0 +1,40 @@
1
+ import type { AuthChoice } from "../auth/index.js";
2
+ import type { AdapterSpec } from "../providers/index.js";
3
+ /** Default local endpoint used when a `LocalAuthChoice` omits `endpoint` (Ollama's default OpenAI-compatible port). */
4
+ export declare const DEFAULT_LOCAL_ENDPOINT = "http://localhost:11434/v1";
5
+ /**
6
+ * Default local model used when neither `LocalAuthChoice` (which has no
7
+ * `model` field of its own — see the API-surface mismatch note in this
8
+ * ticket's report) nor `InProcessOptions.model` supplies one.
9
+ */
10
+ export declare const DEFAULT_LOCAL_MODEL = "llama3.1";
11
+ export interface DeriveAdapterSpecOptions {
12
+ /** Only consulted for `authChoice.mode === "local"`. See `DEFAULT_LOCAL_MODEL`. */
13
+ readonly model?: string;
14
+ }
15
+ /**
16
+ * Derives a concrete `AdapterSpec` (adapter id + config) for in-process's three
17
+ * `AuthChoice` variants:
18
+ *
19
+ * - `api-key` — passed straight through: `authChoice.adapter`/`config` already
20
+ * name a `ProviderRegistry` adapter id and its config (70a's `toAuthChoice`
21
+ * only emits a placeholder `adapter: ""`; a real choice is expected to name
22
+ * a registered adapter such as `"anthropic"`).
23
+ * - `local` — mapped onto the built-in `openai-compatible` adapter, since
24
+ * "a model served on the local machine/network" (70a's doc comment) is
25
+ * exactly what that adapter targets. `endpoint` becomes `baseURL`; `model`
26
+ * comes from `options.model` (falling back to `DEFAULT_LOCAL_MODEL`) since
27
+ * `LocalAuthChoice` itself carries no model field.
28
+ * - `gateway` — also mapped onto `openai-compatible` (an operator-managed
29
+ * gateway is, from this adapter's point of view, just another OpenAI-compatible
30
+ * endpoint); `authChoice.config` must already carry `baseURL`/`model`
31
+ * (and optionally `apiKey`) matching `OpenAICompatibleAdapterConfig` — 70a
32
+ * leaves this config shape unspecified, so this is this ticket's own
33
+ * assumption, not a contract 70a promises. Unlike `local` (which defaults
34
+ * endpoint/model), `gateway` has no sensible default, so a missing/empty
35
+ * `baseURL`/`model` is a loud `wren-harness`-level error here rather than a
36
+ * bare "Invalid URL" deep inside the adapter.
37
+ */
38
+ export declare function deriveAdapterSpec(authChoice: Extract<AuthChoice, {
39
+ mode: "api-key" | "local" | "gateway";
40
+ }>, options?: DeriveAdapterSpecOptions): AdapterSpec;
@@ -0,0 +1,61 @@
1
+ import { OPENAI_COMPATIBLE_ADAPTER_ID } from "../providers/index.js";
2
+ /** Default local endpoint used when a `LocalAuthChoice` omits `endpoint` (Ollama's default OpenAI-compatible port). */
3
+ export const DEFAULT_LOCAL_ENDPOINT = "http://localhost:11434/v1";
4
+ /**
5
+ * Default local model used when neither `LocalAuthChoice` (which has no
6
+ * `model` field of its own — see the API-surface mismatch note in this
7
+ * ticket's report) nor `InProcessOptions.model` supplies one.
8
+ */
9
+ export const DEFAULT_LOCAL_MODEL = "llama3.1";
10
+ /**
11
+ * Derives a concrete `AdapterSpec` (adapter id + config) for in-process's three
12
+ * `AuthChoice` variants:
13
+ *
14
+ * - `api-key` — passed straight through: `authChoice.adapter`/`config` already
15
+ * name a `ProviderRegistry` adapter id and its config (70a's `toAuthChoice`
16
+ * only emits a placeholder `adapter: ""`; a real choice is expected to name
17
+ * a registered adapter such as `"anthropic"`).
18
+ * - `local` — mapped onto the built-in `openai-compatible` adapter, since
19
+ * "a model served on the local machine/network" (70a's doc comment) is
20
+ * exactly what that adapter targets. `endpoint` becomes `baseURL`; `model`
21
+ * comes from `options.model` (falling back to `DEFAULT_LOCAL_MODEL`) since
22
+ * `LocalAuthChoice` itself carries no model field.
23
+ * - `gateway` — also mapped onto `openai-compatible` (an operator-managed
24
+ * gateway is, from this adapter's point of view, just another OpenAI-compatible
25
+ * endpoint); `authChoice.config` must already carry `baseURL`/`model`
26
+ * (and optionally `apiKey`) matching `OpenAICompatibleAdapterConfig` — 70a
27
+ * leaves this config shape unspecified, so this is this ticket's own
28
+ * assumption, not a contract 70a promises. Unlike `local` (which defaults
29
+ * endpoint/model), `gateway` has no sensible default, so a missing/empty
30
+ * `baseURL`/`model` is a loud `wren-harness`-level error here rather than a
31
+ * bare "Invalid URL" deep inside the adapter.
32
+ */
33
+ export function deriveAdapterSpec(authChoice, options = {}) {
34
+ switch (authChoice.mode) {
35
+ case "api-key":
36
+ return { adapter: authChoice.adapter, config: authChoice.config ?? {} };
37
+ case "local":
38
+ return {
39
+ adapter: OPENAI_COMPATIBLE_ADAPTER_ID,
40
+ config: {
41
+ baseURL: authChoice.endpoint ?? DEFAULT_LOCAL_ENDPOINT,
42
+ model: options.model ?? DEFAULT_LOCAL_MODEL,
43
+ },
44
+ };
45
+ case "gateway": {
46
+ const config = authChoice.config ?? {};
47
+ const missing = [];
48
+ if (!isNonEmptyString(config["baseURL"]))
49
+ missing.push("baseURL");
50
+ if (!isNonEmptyString(config["model"]))
51
+ missing.push("model");
52
+ if (missing.length > 0) {
53
+ throw new Error(`gateway mode requires ${missing.join(" and ")} in config (pass --endpoint and --model)`);
54
+ }
55
+ return { adapter: OPENAI_COMPATIBLE_ADAPTER_ID, config };
56
+ }
57
+ }
58
+ }
59
+ function isNonEmptyString(value) {
60
+ return typeof value === "string" && value.trim().length > 0;
61
+ }
@@ -0,0 +1,30 @@
1
+ /** A resolved CLI invocation: the executable plus any args that must precede the caller's own args (e.g. a script path for a `tsx <script>` dev-mode invocation). */
2
+ export interface ResolvedCli {
3
+ readonly command: string;
4
+ readonly prefixArgs: readonly string[];
5
+ }
6
+ export declare class AgentSdkCliNotFoundError extends Error {
7
+ constructor(attempts: readonly string[]);
8
+ }
9
+ /**
10
+ * Resolves the warble `claude-agent-sdk` dispatcher CLI (dispatched's back-end)
11
+ * in four tiers, mirroring `resolveWarbleBinary`:
12
+ *
13
+ * 1. `explicit` — if given, treated as a ready-to-run command with no args
14
+ * prefix (no existence check — a bare name is trusted to resolve on
15
+ * `PATH` at spawn time, matching a documented override).
16
+ * 2. The pinned `@warble/claude-agent-sdk` npm package this workspace depends on, run via
17
+ * `node <dist/cli.js>` rather than relying on the file's own shebang/executable bit.
18
+ * 3. `PATH` — a bare `"warble-agent-sdk"` that `warble-agent-sdk --help` succeeds against. Kept as
19
+ * a fallback for a standalone install on a machine that hasn't run `pnpm install`.
20
+ * 4. A sibling `warble` repo's dev-mode invocation, gated behind the explicit
21
+ * `WREN_HARNESS_ALLOW_WARBLE_SIBLING_CHECKOUT=1` opt-in (see
22
+ * `isWarbleSiblingCheckoutDevModeEnabled`):
23
+ * `<ancestor's parent>/warble/dispatcher/claude-agent-sdk`'s
24
+ * `node_modules/.bin/tsx src/cli.ts`. Like `resolveWarbleBinary`'s equivalent tier, this only
25
+ * resolves in a local development layout with a `warble` checkout next to this repo, and must
26
+ * not silently win over the pinned package now that tier 2 exists.
27
+ *
28
+ * Throws {@link AgentSdkCliNotFoundError} (loud-fail) if none of the four work.
29
+ */
30
+ export declare function resolveAgentSdkCli(explicit?: string): Promise<ResolvedCli>;
@@ -0,0 +1,86 @@
1
+ import { execFile } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { isWarbleSiblingCheckoutDevModeEnabled, resolveInstalledPackageBin } from "../npm-package-resolve.js";
6
+ export class AgentSdkCliNotFoundError extends Error {
7
+ constructor(attempts) {
8
+ super(`could not resolve the warble-agent-sdk CLI:\n${attempts.map((attempt) => ` - ${attempt}`).join("\n")}`);
9
+ this.name = "AgentSdkCliNotFoundError";
10
+ }
11
+ }
12
+ /**
13
+ * Resolves the warble `claude-agent-sdk` dispatcher CLI (dispatched's back-end)
14
+ * in four tiers, mirroring `resolveWarbleBinary`:
15
+ *
16
+ * 1. `explicit` — if given, treated as a ready-to-run command with no args
17
+ * prefix (no existence check — a bare name is trusted to resolve on
18
+ * `PATH` at spawn time, matching a documented override).
19
+ * 2. The pinned `@warble/claude-agent-sdk` npm package this workspace depends on, run via
20
+ * `node <dist/cli.js>` rather than relying on the file's own shebang/executable bit.
21
+ * 3. `PATH` — a bare `"warble-agent-sdk"` that `warble-agent-sdk --help` succeeds against. Kept as
22
+ * a fallback for a standalone install on a machine that hasn't run `pnpm install`.
23
+ * 4. A sibling `warble` repo's dev-mode invocation, gated behind the explicit
24
+ * `WREN_HARNESS_ALLOW_WARBLE_SIBLING_CHECKOUT=1` opt-in (see
25
+ * `isWarbleSiblingCheckoutDevModeEnabled`):
26
+ * `<ancestor's parent>/warble/dispatcher/claude-agent-sdk`'s
27
+ * `node_modules/.bin/tsx src/cli.ts`. Like `resolveWarbleBinary`'s equivalent tier, this only
28
+ * resolves in a local development layout with a `warble` checkout next to this repo, and must
29
+ * not silently win over the pinned package now that tier 2 exists.
30
+ *
31
+ * Throws {@link AgentSdkCliNotFoundError} (loud-fail) if none of the four work.
32
+ */
33
+ export async function resolveAgentSdkCli(explicit) {
34
+ if (explicit !== undefined) {
35
+ return { command: explicit, prefixArgs: [] };
36
+ }
37
+ const attempts = [];
38
+ const packageBin = resolveInstalledPackageBin("@warble/claude-agent-sdk", "warble-agent-sdk");
39
+ if (packageBin !== undefined) {
40
+ return { command: process.execPath, prefixArgs: [packageBin] };
41
+ }
42
+ attempts.push(`"@warble/claude-agent-sdk" is not installed (or declares no "warble-agent-sdk" bin) in this workspace`);
43
+ if (await isExecutableOnPath("warble-agent-sdk")) {
44
+ return { command: "warble-agent-sdk", prefixArgs: [] };
45
+ }
46
+ attempts.push('not found on PATH (tried running "warble-agent-sdk --help")');
47
+ if (isWarbleSiblingCheckoutDevModeEnabled()) {
48
+ const sibling = findSiblingDevInvocation();
49
+ if (sibling !== undefined) {
50
+ return sibling;
51
+ }
52
+ attempts.push('no sibling "warble/dispatcher/claude-agent-sdk" package found with a built tsx binary ' +
53
+ '(searched ancestors of this package for "<ancestor\'s parent>/warble/dispatcher/claude-agent-sdk")');
54
+ }
55
+ else {
56
+ attempts.push(`sibling "warble" checkout dev mode is disabled (set WREN_HARNESS_ALLOW_WARBLE_SIBLING_CHECKOUT=1 to enable it)`);
57
+ }
58
+ throw new AgentSdkCliNotFoundError(attempts);
59
+ }
60
+ function isExecutableOnPath(bin) {
61
+ return new Promise((resolve) => {
62
+ execFile(bin, ["--help"], (error) => resolve(!error));
63
+ });
64
+ }
65
+ /**
66
+ * Walks up from this package's own directory (two levels below the package
67
+ * root, same convention as `resolveWarbleBinary`'s sibling lookup) looking
68
+ * for `warble/dispatcher/claude-agent-sdk`'s dev-mode `tsx` entry point.
69
+ */
70
+ function findSiblingDevInvocation() {
71
+ let dir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
72
+ const maxDepth = 10;
73
+ for (let i = 0; i < maxDepth; i += 1) {
74
+ const parent = path.dirname(dir);
75
+ const pkgDir = path.join(parent, "warble", "dispatcher", "claude-agent-sdk");
76
+ const tsx = path.join(pkgDir, "node_modules", ".bin", "tsx");
77
+ const entry = path.join(pkgDir, "src", "cli.ts");
78
+ if (existsSync(tsx) && existsSync(entry)) {
79
+ return { command: tsx, prefixArgs: [entry] };
80
+ }
81
+ if (parent === dir)
82
+ break; // reached filesystem root
83
+ dir = parent;
84
+ }
85
+ return undefined;
86
+ }
@@ -0,0 +1,31 @@
1
+ import type { ResolvedCli } from "./agent-sdk-cli.js";
2
+ export interface AgentSdkManifestCommand {
3
+ readonly command: string;
4
+ readonly args: readonly string[];
5
+ }
6
+ export interface BuildAgentSdkManifestArgsOptions {
7
+ readonly irPath: string;
8
+ /** Omitted for a raw bootstrap profile, which has no bound project yet. */
9
+ readonly userProject?: string;
10
+ }
11
+ /**
12
+ * Builds the argv for a `warble-agent-sdk manifest` invocation without
13
+ * spawning anything: `<agent-sdk-cli> manifest <ir.json> --project
14
+ * <userProject> --include-unavailable`. The display-only flag keeps an
15
+ * unsupported declared component visible without making it executable.
16
+ * Omitting `--out` is deliberate — the dispatcher's
17
+ * `manifest` subcommand writes the manifest JSON to stdout only when no
18
+ * `--out` is given (resolution summaries always go to stderr), so stdout
19
+ * stays pure JSON for `runAgentSdkManifest` to parse.
20
+ */
21
+ export declare function buildAgentSdkManifestArgs(cli: ResolvedCli, options: BuildAgentSdkManifestArgsOptions): AgentSdkManifestCommand;
22
+ /**
23
+ * Runs a `warble-agent-sdk manifest` invocation (built by
24
+ * {@link buildAgentSdkManifestArgs}) to completion and returns its stdout — a
25
+ * single JSON blob, not a stream, so this mirrors `compile/pipeline.ts`'s
26
+ * buffered `runWarble` rather than `dispatched.ts`'s streaming `spawnChat`
27
+ * (there's no incremental per-step output to relay for a describe/introspect
28
+ * call). Reuses `WarbleCommandFailedError` since it's already the generic
29
+ * "an external warble-family CLI exited non-zero" error shape.
30
+ */
31
+ export declare function runAgentSdkManifest(command: string, args: readonly string[]): Promise<string>;
@@ -0,0 +1,56 @@
1
+ import { execFile } from "node:child_process";
2
+ import { WarbleCommandFailedError } from "../compile/errors.js";
3
+ // Mirrors `compile/pipeline.ts`'s `WARBLE_COMMAND_TIMEOUT_MS` — a generous
4
+ // ceiling for a local, normally-sub-second command; guards against a hang,
5
+ // not meant to ever fire in normal operation.
6
+ const AGENT_SDK_MANIFEST_TIMEOUT_MS = 2 * 60 * 1000;
7
+ /**
8
+ * Builds the argv for a `warble-agent-sdk manifest` invocation without
9
+ * spawning anything: `<agent-sdk-cli> manifest <ir.json> --project
10
+ * <userProject> --include-unavailable`. The display-only flag keeps an
11
+ * unsupported declared component visible without making it executable.
12
+ * Omitting `--out` is deliberate — the dispatcher's
13
+ * `manifest` subcommand writes the manifest JSON to stdout only when no
14
+ * `--out` is given (resolution summaries always go to stderr), so stdout
15
+ * stays pure JSON for `runAgentSdkManifest` to parse.
16
+ */
17
+ export function buildAgentSdkManifestArgs(cli, options) {
18
+ return {
19
+ command: cli.command,
20
+ args: [
21
+ ...cli.prefixArgs,
22
+ "manifest",
23
+ options.irPath,
24
+ "--include-unavailable",
25
+ ...(options.userProject !== undefined ? ["--project", options.userProject] : []),
26
+ ],
27
+ };
28
+ }
29
+ /**
30
+ * Runs a `warble-agent-sdk manifest` invocation (built by
31
+ * {@link buildAgentSdkManifestArgs}) to completion and returns its stdout — a
32
+ * single JSON blob, not a stream, so this mirrors `compile/pipeline.ts`'s
33
+ * buffered `runWarble` rather than `dispatched.ts`'s streaming `spawnChat`
34
+ * (there's no incremental per-step output to relay for a describe/introspect
35
+ * call). Reuses `WarbleCommandFailedError` since it's already the generic
36
+ * "an external warble-family CLI exited non-zero" error shape.
37
+ */
38
+ export function runAgentSdkManifest(command, args) {
39
+ return new Promise((resolve, reject) => {
40
+ execFile(command, [...args], { maxBuffer: 32 * 1024 * 1024, timeout: AGENT_SDK_MANIFEST_TIMEOUT_MS }, (error, stdout, stderr) => {
41
+ if (error) {
42
+ const exitCode = typeof error.code === "number" ? error.code : 1;
43
+ const clarifiedStderr = stderr.length > 0
44
+ ? stderr
45
+ : error.killed === true
46
+ ? `warble-agent-sdk manifest subprocess timed out after ${AGENT_SDK_MANIFEST_TIMEOUT_MS}ms`
47
+ : error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER"
48
+ ? "warble-agent-sdk manifest subprocess exceeded the 32 MiB output buffer"
49
+ : stderr;
50
+ reject(new WarbleCommandFailedError(command, args, exitCode, clarifiedStderr, stdout));
51
+ return;
52
+ }
53
+ resolve(stdout);
54
+ });
55
+ });
56
+ }
@@ -0,0 +1,53 @@
1
+ import type { RenderEnvelope } from "../render/envelope.js";
2
+ export type ArtifactContentUnavailableReason = "missing" | "unreadable" | "outside_root" | "too_large";
3
+ /**
4
+ * `GET /api/artifacts/:id/content`'s response shape. An artifact's
5
+ * `location` is read back and classified into exactly one of these forms —
6
+ * never a fabricated fourth shape:
7
+ * - `envelope`: the file parses as JSON and is envelope-shaped (has a
8
+ * `blocks` array) — this is what dispatched (`generate_dashboard`/report/chart
9
+ * agents) always persists.
10
+ * - `text`: the file exists and is readable but isn't envelope-shaped JSON —
11
+ * this is the common case for in-process's `write_artifact` tool, which lets
12
+ * the model write arbitrary content (HTML, markdown, plain JSON, ...).
13
+ * - `unavailable`: nothing could be read back; `reason` says why. Every
14
+ * `unavailable` is a caller-visible degrade to metadata-only, never a
15
+ * thrown error — a missing/unreadable/outside-root/too-large artifact is
16
+ * an expected, honestly-reported state, not a route failure.
17
+ */
18
+ export type ArtifactContentDto = {
19
+ readonly form: "envelope";
20
+ readonly envelope: RenderEnvelope;
21
+ } | {
22
+ readonly form: "text";
23
+ readonly text: string;
24
+ readonly truncated: boolean;
25
+ } | {
26
+ readonly form: "unavailable";
27
+ readonly reason: ArtifactContentUnavailableReason;
28
+ };
29
+ /**
30
+ * Resolves an `ArtifactRow.location` back to its on-disk content, scoped to
31
+ * `artifactsRoot` (the same root `resolveArtifactsDir` computes for in-process/B
32
+ * writes — see `in-process.ts` and `turn.ts`'s `maybeCreateDispatchedArtifact`).
33
+ * `location` may be relative (in-process, written under the model's chosen
34
+ * `write_artifact` path, itself scoped to `artifactsRoot` at write time — see
35
+ * `resolveWithinScope` in `exec/local.ts`) or absolute (dispatched always writes
36
+ * an absolute `location`).
37
+ *
38
+ * Containment is checked twice, mirroring `resolveWithinScope`'s
39
+ * `path.relative()`-based escape check (never `startsWith`, which a sibling
40
+ * directory that merely shares the root as a string prefix — e.g.
41
+ * `<root>-evil` — would defeat):
42
+ *
43
+ * 1. On the nominal (pre-existence) resolved path — so a location that's
44
+ * outside the root by construction (`../../etc/passwd`, or the sibling-
45
+ * prefix case above) is refused with zero filesystem access.
46
+ * 2. On the `realpath`-resolved path — so a symlink that lives inside the
47
+ * root but *points* outside it is also refused, after resolving the
48
+ * symlink (metadata only) but still before any content read.
49
+ *
50
+ * The read size is capped (`MAX_CONTENT_BYTES`) via `statSync` before the
51
+ * file is ever read or parsed.
52
+ */
53
+ export declare function resolveArtifactContent(artifactsRoot: string, location: string): ArtifactContentDto;
@@ -0,0 +1,83 @@
1
+ import { readFileSync, realpathSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ /** Read-size cap applied before any read/parse — an artifact past
4
+ * this is refused as `too_large` rather than loaded whole into memory. */
5
+ const MAX_CONTENT_BYTES = 2 * 1024 * 1024; // 2 MiB
6
+ /**
7
+ * Resolves an `ArtifactRow.location` back to its on-disk content, scoped to
8
+ * `artifactsRoot` (the same root `resolveArtifactsDir` computes for in-process/B
9
+ * writes — see `in-process.ts` and `turn.ts`'s `maybeCreateDispatchedArtifact`).
10
+ * `location` may be relative (in-process, written under the model's chosen
11
+ * `write_artifact` path, itself scoped to `artifactsRoot` at write time — see
12
+ * `resolveWithinScope` in `exec/local.ts`) or absolute (dispatched always writes
13
+ * an absolute `location`).
14
+ *
15
+ * Containment is checked twice, mirroring `resolveWithinScope`'s
16
+ * `path.relative()`-based escape check (never `startsWith`, which a sibling
17
+ * directory that merely shares the root as a string prefix — e.g.
18
+ * `<root>-evil` — would defeat):
19
+ *
20
+ * 1. On the nominal (pre-existence) resolved path — so a location that's
21
+ * outside the root by construction (`../../etc/passwd`, or the sibling-
22
+ * prefix case above) is refused with zero filesystem access.
23
+ * 2. On the `realpath`-resolved path — so a symlink that lives inside the
24
+ * root but *points* outside it is also refused, after resolving the
25
+ * symlink (metadata only) but still before any content read.
26
+ *
27
+ * The read size is capped (`MAX_CONTENT_BYTES`) via `statSync` before the
28
+ * file is ever read or parsed.
29
+ */
30
+ export function resolveArtifactContent(artifactsRoot, location) {
31
+ const nominal = path.isAbsolute(location) ? path.resolve(location) : path.resolve(artifactsRoot, location);
32
+ if (escapesRoot(artifactsRoot, nominal))
33
+ return { form: "unavailable", reason: "outside_root" };
34
+ let realRoot;
35
+ let realTarget;
36
+ try {
37
+ realRoot = realpathSync(artifactsRoot);
38
+ realTarget = realpathSync(nominal);
39
+ }
40
+ catch {
41
+ return { form: "unavailable", reason: "missing" };
42
+ }
43
+ if (escapesRoot(realRoot, realTarget))
44
+ return { form: "unavailable", reason: "outside_root" };
45
+ let size;
46
+ try {
47
+ size = statSync(realTarget).size;
48
+ }
49
+ catch {
50
+ return { form: "unavailable", reason: "unreadable" };
51
+ }
52
+ if (size > MAX_CONTENT_BYTES)
53
+ return { form: "unavailable", reason: "too_large" };
54
+ let raw;
55
+ try {
56
+ raw = readFileSync(realTarget, "utf-8");
57
+ }
58
+ catch {
59
+ return { form: "unavailable", reason: "unreadable" };
60
+ }
61
+ const envelope = tryParseEnvelope(raw);
62
+ return envelope ? { form: "envelope", envelope } : { form: "text", text: raw, truncated: false };
63
+ }
64
+ /** Same escape condition as `exec/local.ts`'s `resolveWithinScope`. */
65
+ function escapesRoot(root, target) {
66
+ const relative = path.relative(root, target);
67
+ return relative.startsWith("..") || path.isAbsolute(relative);
68
+ }
69
+ function tryParseEnvelope(raw) {
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(raw);
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ return hasBlocksArray(parsed) ? parsed : undefined;
78
+ }
79
+ /** Mirrors the private `hasBlocksArray` shape-check in `render/envelope.ts` — reimplemented
80
+ * locally rather than exported from there, to keep that module's blast radius unchanged. */
81
+ function hasBlocksArray(value) {
82
+ return typeof value === "object" && value !== null && Array.isArray(value.blocks);
83
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Dispatched's own mapping FROM warble's `chat --stream-json` NDJSON stream TO this harness's
3
+ * mode-agnostic `AgentEvent` union (`harness/events/types.ts`). Two things live here:
4
+ *
5
+ * - `WarbleChatEventLike` — a LOCAL structural mirror of warble's own `WarbleChatEvent` vocabulary
6
+ * (`dispatcher/claude-agent-sdk/src/events.ts` in the warble repo). It is intentionally NOT
7
+ * imported from warble: warble is a separate process/package this harness only ever talks to
8
+ * over a subprocess's stdout, never a library dependency, so the wire contract is re-declared
9
+ * here by hand rather than crossing that boundary with a type import. Keep it in sync with
10
+ * warble's vocabulary if that ever changes.
11
+ * - `mapChatEventToAgentEvent` — a PURE function turning one parsed `WarbleChatEventLike` plus a
12
+ * small piece of running state (the current enclosing step id, and callId/stepId -> name
13
+ * memory) into zero or one `AgentEventInput`. Pure so it's unit-testable with plain data, no
14
+ * process/stream involved.
15
+ *
16
+ * Step/tool id mapping: warble's own mapper emits ONE enclosing `step_start` per turn (id = the
17
+ * Dispatched component's verb) and groups every `tool_call`/`tool_result` under it — see that
18
+ * module's doc comment for why. This function carries that same single `stepId` forward onto
19
+ * every `tool.call`/`tool.result` it produces (`ToolCallEvent`/`ToolResultEvent` both require a
20
+ * `stepId`), tracked in `state.currentStepId` as `step_start` events arrive.
21
+ *
22
+ * `answer` is deliberately mapped to `undefined` — the turn's final answer text is handled by the
23
+ * caller (`runDispatchedDefault`'s existing `answer`/`run.finish` emission), not duplicated here. `session`
24
+ * is likewise mapped to `undefined` for the same reason: it is session-resume metadata (the SDK's
25
+ * `session_id`, forwarded on both a successful AND a failed/`error_max_turns` turn), not a work-log
26
+ * event, and `spawnChat` intercepts it directly rather than routing it through this mapper.
27
+ */
28
+ import type { AgentEventInput } from "../events/index.js";
29
+ export type WarbleChatEventLike = {
30
+ readonly t: "step_start";
31
+ readonly id: string;
32
+ readonly name: string;
33
+ readonly parent: string | null;
34
+ readonly depth: number;
35
+ } | {
36
+ readonly t: "step_finish";
37
+ readonly id: string;
38
+ readonly ok: boolean;
39
+ readonly detail?: string;
40
+ } | {
41
+ readonly t: "tool_call";
42
+ readonly id: string;
43
+ readonly name: string;
44
+ readonly input?: unknown;
45
+ readonly parent: string | null;
46
+ readonly depth: number;
47
+ } | {
48
+ readonly t: "tool_result";
49
+ readonly id: string;
50
+ readonly ok: boolean;
51
+ readonly summary?: string;
52
+ readonly error?: string;
53
+ } | {
54
+ readonly t: "answer";
55
+ readonly text: string;
56
+ } | {
57
+ /**
58
+ * The turn's SDK session id (multi-turn resume anchor), emitted once per turn by
59
+ * `warble-agent-sdk chat --stream-json` on success AND on a failed turn (e.g.
60
+ * `error_max_turns`) — mirrors warble's own `WarbleChatEvent`'s `session` variant
61
+ * (`dispatcher/claude-agent-sdk/src/events.ts`). `id` is null only if the SDK never
62
+ * produced a session id at all.
63
+ */
64
+ readonly t: "session";
65
+ readonly id: string | null;
66
+ };
67
+ /**
68
+ * Parse + structurally validate one NDJSON line from `warble-agent-sdk chat --stream-json`.
69
+ * Returns `undefined` for a blank line, invalid JSON, or a shape that doesn't match any known
70
+ * `WarbleChatEventLike` variant — the caller drops such lines rather than failing the whole turn
71
+ * over one malformed/forward-incompatible line.
72
+ */
73
+ export declare function parseWarbleChatEventLine(line: string): WarbleChatEventLike | undefined;
74
+ /**
75
+ * Running state threaded across a turn's whole NDJSON stream. `currentStepId` is warble's single
76
+ * enclosing step for the turn (set on `step_start`); `stepNames`/`toolNames` remember the
77
+ * human-readable name for a given step/call id so a later `step_finish`/`tool_result` — which
78
+ * carry no name of their own in warble's vocabulary — can still populate `StepFinishEvent.name` /
79
+ * `ToolResultEvent.tool`.
80
+ */
81
+ export interface ChatEventMapperState {
82
+ currentStepId: string | undefined;
83
+ readonly stepNames: Map<string, string>;
84
+ readonly toolNames: Map<string, string>;
85
+ }
86
+ export declare function createChatEventMapperState(): ChatEventMapperState;
87
+ /**
88
+ * Maps one parsed warble chat event to the harness's `AgentEvent` vocabulary, mutating `state` as
89
+ * it goes (remembering step/tool names, tracking the current enclosing step). Returns `undefined`
90
+ * for `answer` (handled by the caller) and for a `tool_call`/`tool_result` this mapper cannot
91
+ * place under any known step — that should never happen given warble always opens its enclosing
92
+ * step before emitting a tool event, but falling back to the raw id rather than throwing keeps a
93
+ * single malformed/reordered line from crashing an otherwise-healthy live stream.
94
+ */
95
+ export declare function mapChatEventToAgentEvent(raw: WarbleChatEventLike, state: ChatEventMapperState): AgentEventInput | undefined;