@nhtio/adk 0.1.0-master-e6c58205 → 0.1.0-master-89b47bec

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 (290) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/__vite-browser-external-CU7uua8b.mjs +9 -0
  3. package/__vite-browser-external-CU7uua8b.mjs.map +1 -0
  4. package/__vite-browser-external-DAHxjh-D.js +13 -0
  5. package/__vite-browser-external-DAHxjh-D.js.map +1 -0
  6. package/batteries/embeddings/openai/adapter.cjs +1 -1
  7. package/batteries/embeddings/openai/adapter.mjs +1 -1
  8. package/batteries/embeddings/webllm/adapter.cjs +1 -1
  9. package/batteries/embeddings/webllm/adapter.mjs +1 -1
  10. package/batteries/llm/ollama/adapter.cjs +5 -5
  11. package/batteries/llm/ollama/adapter.mjs +5 -5
  12. package/batteries/llm/ollama/helpers.cjs +2 -2
  13. package/batteries/llm/ollama/helpers.mjs +2 -2
  14. package/batteries/llm/ollama/validation.cjs +1 -1
  15. package/batteries/llm/ollama/validation.mjs +1 -1
  16. package/batteries/llm/openai_chat_completions/adapter.cjs +5 -5
  17. package/batteries/llm/openai_chat_completions/adapter.mjs +5 -5
  18. package/batteries/llm/openai_chat_completions/helpers.cjs +2 -2
  19. package/batteries/llm/openai_chat_completions/helpers.mjs +2 -2
  20. package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
  21. package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
  22. package/batteries/llm/webllm_chat_completions/adapter.cjs +5 -5
  23. package/batteries/llm/webllm_chat_completions/adapter.mjs +5 -5
  24. package/batteries/llm/webllm_chat_completions/validation.cjs +1 -1
  25. package/batteries/llm/webllm_chat_completions/validation.mjs +1 -1
  26. package/batteries/media/contracts.cjs +38 -8
  27. package/batteries/media/contracts.cjs.map +1 -1
  28. package/batteries/media/contracts.d.ts +86 -6
  29. package/batteries/media/contracts.mjs +37 -9
  30. package/batteries/media/contracts.mjs.map +1 -1
  31. package/batteries/media/engines/audio_decode.cjs +46 -6
  32. package/batteries/media/engines/audio_decode.cjs.map +1 -1
  33. package/batteries/media/engines/audio_decode.d.ts +9 -5
  34. package/batteries/media/engines/audio_decode.mjs +47 -7
  35. package/batteries/media/engines/audio_decode.mjs.map +1 -1
  36. package/batteries/media/engines/data.cjs +159 -0
  37. package/batteries/media/engines/data.cjs.map +1 -0
  38. package/batteries/media/engines/data.d.ts +36 -0
  39. package/batteries/media/engines/data.mjs +157 -0
  40. package/batteries/media/engines/data.mjs.map +1 -0
  41. package/batteries/media/engines/exceljs.cjs +290 -0
  42. package/batteries/media/engines/exceljs.cjs.map +1 -0
  43. package/batteries/media/engines/exceljs.d.ts +41 -0
  44. package/batteries/media/engines/exceljs.mjs +288 -0
  45. package/batteries/media/engines/exceljs.mjs.map +1 -0
  46. package/batteries/media/engines/execa_executor.cjs +1 -1
  47. package/batteries/media/engines/execa_executor.mjs +1 -1
  48. package/batteries/media/engines/fs_workspace.cjs +1 -1
  49. package/batteries/media/engines/fs_workspace.mjs +1 -1
  50. package/batteries/media/engines/jimp.cjs +24 -2
  51. package/batteries/media/engines/jimp.cjs.map +1 -1
  52. package/batteries/media/engines/jimp.d.ts +3 -1
  53. package/batteries/media/engines/jimp.mjs +24 -2
  54. package/batteries/media/engines/jimp.mjs.map +1 -1
  55. package/batteries/media/engines/sharp.cjs +37 -4
  56. package/batteries/media/engines/sharp.cjs.map +1 -1
  57. package/batteries/media/engines/sharp.d.ts +5 -3
  58. package/batteries/media/engines/sharp.mjs +37 -4
  59. package/batteries/media/engines/sharp.mjs.map +1 -1
  60. package/batteries/media/engines/sheetjs.cjs +438 -0
  61. package/batteries/media/engines/sheetjs.cjs.map +1 -0
  62. package/batteries/media/engines/sheetjs.d.ts +51 -0
  63. package/batteries/media/engines/sheetjs.mjs +436 -0
  64. package/batteries/media/engines/sheetjs.mjs.map +1 -0
  65. package/batteries/media/engines/soffice.cjs +37 -3
  66. package/batteries/media/engines/soffice.cjs.map +1 -1
  67. package/batteries/media/engines/soffice.mjs +38 -4
  68. package/batteries/media/engines/soffice.mjs.map +1 -1
  69. package/batteries/media/engines/tesseract_js.cjs +1 -1
  70. package/batteries/media/engines/tesseract_js.mjs +1 -1
  71. package/batteries/media/engines/transformers_asr.cjs +1 -1
  72. package/batteries/media/engines/transformers_asr.mjs +1 -1
  73. package/batteries/media/forge.cjs +89 -20
  74. package/batteries/media/forge.cjs.map +1 -1
  75. package/batteries/media/forge.mjs +89 -20
  76. package/batteries/media/forge.mjs.map +1 -1
  77. package/batteries/media/formats.d.ts +9 -0
  78. package/batteries/media/index.d.ts +8 -6
  79. package/batteries/media/lint.cjs +270 -58
  80. package/batteries/media/lint.cjs.map +1 -1
  81. package/batteries/media/lint.d.ts +45 -0
  82. package/batteries/media/lint.mjs +267 -58
  83. package/batteries/media/lint.mjs.map +1 -1
  84. package/batteries/media/registry.d.ts +11 -2
  85. package/batteries/media/steps/data.d.ts +26 -0
  86. package/batteries/media/steps/doc.d.ts +17 -7
  87. package/batteries/media/steps/patch.d.ts +90 -0
  88. package/batteries/media/steps/sheet.d.ts +10 -6
  89. package/batteries/media/steps/text.d.ts +4 -1
  90. package/batteries/media/validate.d.ts +5 -0
  91. package/batteries/media/verbs.d.ts +12 -1
  92. package/batteries/media.cjs +869 -448
  93. package/batteries/media.cjs.map +1 -1
  94. package/batteries/media.mjs +829 -409
  95. package/batteries/media.mjs.map +1 -1
  96. package/batteries/storage/flydrive.cjs +3 -6
  97. package/batteries/storage/flydrive.cjs.map +1 -1
  98. package/batteries/storage/flydrive.mjs +3 -6
  99. package/batteries/storage/flydrive.mjs.map +1 -1
  100. package/batteries/storage/in_memory.cjs +1 -1
  101. package/batteries/storage/in_memory.mjs +1 -1
  102. package/batteries/storage/opfs.cjs +1 -1
  103. package/batteries/storage/opfs.mjs +1 -1
  104. package/batteries/tools/_shared.cjs +2 -2
  105. package/batteries/tools/_shared.mjs +2 -2
  106. package/batteries/tools/color.cjs +2 -2
  107. package/batteries/tools/color.mjs +2 -2
  108. package/batteries/tools/comparison.cjs +3 -3
  109. package/batteries/tools/comparison.mjs +3 -3
  110. package/batteries/tools/data_structure.cjs +3 -3
  111. package/batteries/tools/data_structure.mjs +3 -3
  112. package/batteries/tools/datetime_extended.cjs +3 -3
  113. package/batteries/tools/datetime_extended.mjs +3 -3
  114. package/batteries/tools/datetime_math.cjs +3 -3
  115. package/batteries/tools/datetime_math.mjs +3 -3
  116. package/batteries/tools/encoding.cjs +3 -3
  117. package/batteries/tools/encoding.mjs +3 -3
  118. package/batteries/tools/formatting.cjs +3 -3
  119. package/batteries/tools/formatting.mjs +3 -3
  120. package/batteries/tools/geo_basics.cjs +2 -2
  121. package/batteries/tools/geo_basics.mjs +2 -2
  122. package/batteries/tools/math.cjs +3 -3
  123. package/batteries/tools/math.mjs +3 -3
  124. package/batteries/tools/memory.cjs +6 -6
  125. package/batteries/tools/memory.mjs +6 -6
  126. package/batteries/tools/parsing.cjs +4 -4
  127. package/batteries/tools/parsing.mjs +4 -4
  128. package/batteries/tools/retrievables.cjs +5 -5
  129. package/batteries/tools/retrievables.mjs +5 -5
  130. package/batteries/tools/scrapper.cjs +1 -1
  131. package/batteries/tools/scrapper.mjs +1 -1
  132. package/batteries/tools/searxng.cjs +1 -1
  133. package/batteries/tools/searxng.mjs +1 -1
  134. package/batteries/tools/standing_instructions.cjs +4 -4
  135. package/batteries/tools/standing_instructions.mjs +4 -4
  136. package/batteries/tools/statistics.cjs +4 -4
  137. package/batteries/tools/statistics.mjs +4 -4
  138. package/batteries/tools/string_processing.cjs +3 -3
  139. package/batteries/tools/string_processing.mjs +3 -3
  140. package/batteries/tools/structured_data.cjs +3 -3
  141. package/batteries/tools/structured_data.mjs +3 -3
  142. package/batteries/tools/text_analysis.cjs +3 -3
  143. package/batteries/tools/text_analysis.mjs +3 -3
  144. package/batteries/tools/text_comparison.cjs +2 -2
  145. package/batteries/tools/text_comparison.mjs +2 -2
  146. package/batteries/tools/time.cjs +3 -3
  147. package/batteries/tools/time.mjs +3 -3
  148. package/batteries/tools/unit_conversion.cjs +2 -2
  149. package/batteries/tools/unit_conversion.mjs +2 -2
  150. package/batteries/tools/web_retrieval.cjs +1 -1
  151. package/batteries/tools/web_retrieval.mjs +1 -1
  152. package/batteries/tools.cjs +2 -2
  153. package/batteries/tools.mjs +2 -2
  154. package/batteries/vector/arangodb.cjs +1 -1
  155. package/batteries/vector/arangodb.mjs +1 -1
  156. package/batteries/vector/clickhouse.cjs +1 -1
  157. package/batteries/vector/clickhouse.mjs +1 -1
  158. package/batteries/vector/cloudflare.cjs +1 -1
  159. package/batteries/vector/cloudflare.mjs +1 -1
  160. package/batteries/vector/couchbase.cjs +1 -1
  161. package/batteries/vector/couchbase.mjs +1 -1
  162. package/batteries/vector/duckdb.cjs +1 -1
  163. package/batteries/vector/duckdb.mjs +1 -1
  164. package/batteries/vector/elasticsearch.cjs +1 -1
  165. package/batteries/vector/elasticsearch.mjs +1 -1
  166. package/batteries/vector/filters.cjs +1 -1
  167. package/batteries/vector/filters.mjs +1 -1
  168. package/batteries/vector/hnswlib.cjs +1 -1
  169. package/batteries/vector/hnswlib.mjs +1 -1
  170. package/batteries/vector/lancedb.cjs +1 -1
  171. package/batteries/vector/lancedb.mjs +1 -1
  172. package/batteries/vector/mariadb.cjs +1 -1
  173. package/batteries/vector/mariadb.mjs +1 -1
  174. package/batteries/vector/meilisearch.cjs +1 -1
  175. package/batteries/vector/meilisearch.mjs +1 -1
  176. package/batteries/vector/migrate.cjs +1 -1
  177. package/batteries/vector/migrate.mjs +1 -1
  178. package/batteries/vector/mongodb.cjs +1 -1
  179. package/batteries/vector/mongodb.mjs +1 -1
  180. package/batteries/vector/neo4j.cjs +1 -1
  181. package/batteries/vector/neo4j.mjs +1 -1
  182. package/batteries/vector/opensearch.cjs +1 -1
  183. package/batteries/vector/opensearch.mjs +1 -1
  184. package/batteries/vector/oracle23ai.cjs +1 -1
  185. package/batteries/vector/oracle23ai.mjs +1 -1
  186. package/batteries/vector/pinecone.cjs +1 -1
  187. package/batteries/vector/pinecone.mjs +1 -1
  188. package/batteries/vector/redis.cjs +1 -1
  189. package/batteries/vector/redis.mjs +1 -1
  190. package/batteries/vector/retrievable.cjs +1 -1
  191. package/batteries/vector/retrievable.mjs +1 -1
  192. package/batteries/vector/s3vectors.cjs +1 -1
  193. package/batteries/vector/s3vectors.mjs +1 -1
  194. package/batteries/vector/solr.cjs +1 -1
  195. package/batteries/vector/solr.mjs +1 -1
  196. package/batteries/vector/surrealdb.cjs +1 -1
  197. package/batteries/vector/surrealdb.mjs +1 -1
  198. package/batteries/vector/typesense.cjs +1 -1
  199. package/batteries/vector/typesense.mjs +1 -1
  200. package/batteries/vector/vespa.cjs +1 -1
  201. package/batteries/vector/vespa.mjs +1 -1
  202. package/batteries/vector/weaviate.cjs +1 -1
  203. package/batteries/vector/weaviate.mjs +1 -1
  204. package/batteries.cjs +2 -2
  205. package/batteries.mjs +2 -2
  206. package/bundled_xlsx.d.ts +1075 -0
  207. package/{common-BxoznqbX.mjs → common-BQoFv108.mjs} +7 -7
  208. package/{common-BxoznqbX.mjs.map → common-BQoFv108.mjs.map} +1 -1
  209. package/{common-BBK2e5ro.js → common-DmDn4Gfl.js} +7 -7
  210. package/{common-BBK2e5ro.js.map → common-DmDn4Gfl.js.map} +1 -1
  211. package/common.cjs +7 -7
  212. package/common.mjs +7 -7
  213. package/{dispatch_runner-Hn5idJYx.js → dispatch_runner-BwplbCPr.js} +3 -3
  214. package/{dispatch_runner-Hn5idJYx.js.map → dispatch_runner-BwplbCPr.js.map} +1 -1
  215. package/{dispatch_runner-DUrpj0xR.mjs → dispatch_runner-C1ar_D7W.mjs} +3 -3
  216. package/{dispatch_runner-DUrpj0xR.mjs.map → dispatch_runner-C1ar_D7W.mjs.map} +1 -1
  217. package/dispatch_runner.cjs +1 -1
  218. package/dispatch_runner.mjs +1 -1
  219. package/eslint.cjs +1 -1
  220. package/eslint.mjs +1 -1
  221. package/forge.cjs +4 -4
  222. package/forge.mjs +4 -4
  223. package/formats-CCylUHZw.mjs +161 -0
  224. package/formats-CCylUHZw.mjs.map +1 -0
  225. package/formats-DZVuqZUN.js +196 -0
  226. package/formats-DZVuqZUN.js.map +1 -0
  227. package/guards.cjs +8 -8
  228. package/guards.mjs +8 -8
  229. package/index.cjs +11 -11
  230. package/index.mjs +11 -11
  231. package/mcp/adk-docs-corpus.json +1 -1
  232. package/package.json +198 -182
  233. package/{scrapper-DLj_KkxK.mjs → scrapper-Dn2wXu0Y.mjs} +5 -5
  234. package/{scrapper-DLj_KkxK.mjs.map → scrapper-Dn2wXu0Y.mjs.map} +1 -1
  235. package/{scrapper-Cmf1CSA-.js → scrapper-mBZ0Odq7.js} +5 -5
  236. package/{scrapper-Cmf1CSA-.js.map → scrapper-mBZ0Odq7.js.map} +1 -1
  237. package/{searxng-BKs5s8Su.js → searxng-Dj-iP_Vz.js} +5 -5
  238. package/{searxng-BKs5s8Su.js.map → searxng-Dj-iP_Vz.js.map} +1 -1
  239. package/{searxng-BJ-aZcMz.mjs → searxng-DjFxtFf6.mjs} +5 -5
  240. package/{searxng-BJ-aZcMz.mjs.map → searxng-DjFxtFf6.mjs.map} +1 -1
  241. package/server.json +21 -0
  242. package/skills/adk-assembly/SKILL.md +2 -2
  243. package/{spooled_artifact-BR0IhU9y.js → spooled_artifact-BCmPrxZS.js} +3 -3
  244. package/{spooled_artifact-BR0IhU9y.js.map → spooled_artifact-BCmPrxZS.js.map} +1 -1
  245. package/{spooled_artifact-nTE6ujM8.mjs → spooled_artifact-DL36jnqA.mjs} +3 -3
  246. package/{spooled_artifact-nTE6ujM8.mjs.map → spooled_artifact-DL36jnqA.mjs.map} +1 -1
  247. package/spooled_artifact.cjs +2 -2
  248. package/spooled_artifact.mjs +2 -2
  249. package/{spooled_markdown_artifact-Dj3fNowz.js → spooled_markdown_artifact-CD4PrrpV.js} +3 -3
  250. package/{spooled_markdown_artifact-Dj3fNowz.js.map → spooled_markdown_artifact-CD4PrrpV.js.map} +1 -1
  251. package/{spooled_markdown_artifact-DIYzbQv8.mjs → spooled_markdown_artifact-Da-Xnebh.mjs} +3 -3
  252. package/{spooled_markdown_artifact-DIYzbQv8.mjs.map → spooled_markdown_artifact-Da-Xnebh.mjs.map} +1 -1
  253. package/{thought-Cde2Y_GK.mjs → thought-CtOoZUSE.mjs} +3 -3
  254. package/{thought-Cde2Y_GK.mjs.map → thought-CtOoZUSE.mjs.map} +1 -1
  255. package/{thought-BFtsnUTU.js → thought-eka4iUHa.js} +3 -3
  256. package/{thought-BFtsnUTU.js.map → thought-eka4iUHa.js.map} +1 -1
  257. package/{tool-CcxYxA5X.mjs → tool-CITF20yJ.mjs} +3 -3
  258. package/{tool-CcxYxA5X.mjs.map → tool-CITF20yJ.mjs.map} +1 -1
  259. package/{tool-B2nnkwNU.js → tool-Dhjca7rC.js} +3 -3
  260. package/{tool-B2nnkwNU.js.map → tool-Dhjca7rC.js.map} +1 -1
  261. package/{tool_call-BxL3eU1k.js → tool_call-Cyi0hUUc.js} +4 -4
  262. package/{tool_call-BxL3eU1k.js.map → tool_call-Cyi0hUUc.js.map} +1 -1
  263. package/{tool_call-B3MOog2R.mjs → tool_call-D_k36aI4.mjs} +4 -4
  264. package/{tool_call-B3MOog2R.mjs.map → tool_call-D_k36aI4.mjs.map} +1 -1
  265. package/{tool_registry-BaLUIzWz.mjs → tool_registry-CZ55mIti.mjs} +152 -143
  266. package/tool_registry-CZ55mIti.mjs.map +1 -0
  267. package/{tool_registry-BdKlPuHS.js → tool_registry-DwZ6pzN4.js} +151 -142
  268. package/tool_registry-DwZ6pzN4.js.map +1 -0
  269. package/{turn_runner-M2I8G2ok.js → turn_runner-CCZcsoDU.js} +5 -5
  270. package/{turn_runner-M2I8G2ok.js.map → turn_runner-CCZcsoDU.js.map} +1 -1
  271. package/{turn_runner-BV4BaZK9.mjs → turn_runner-DRPpVvsd.mjs} +5 -5
  272. package/{turn_runner-BV4BaZK9.mjs.map → turn_runner-DRPpVvsd.mjs.map} +1 -1
  273. package/turn_runner.cjs +1 -1
  274. package/turn_runner.mjs +1 -1
  275. package/{validate-JOHfh-BB.mjs → validate-C_hTnDBE.mjs} +95 -8
  276. package/validate-C_hTnDBE.mjs.map +1 -0
  277. package/{validate-DgDZkUKs.js → validate-gaSa9bsk.js} +95 -8
  278. package/validate-gaSa9bsk.js.map +1 -0
  279. package/xlsx-B0sTpEC2.js +30615 -0
  280. package/xlsx-B0sTpEC2.js.map +1 -0
  281. package/xlsx-DiMpn_Xv.mjs +30596 -0
  282. package/xlsx-DiMpn_Xv.mjs.map +1 -0
  283. package/xlsx.zahl-D2_Q-Xs4.mjs +6 -0
  284. package/xlsx.zahl-D2_Q-Xs4.mjs.map +1 -0
  285. package/xlsx.zahl-ZIcUQVv8.js +7 -0
  286. package/xlsx.zahl-ZIcUQVv8.js.map +1 -0
  287. package/tool_registry-BaLUIzWz.mjs.map +0 -1
  288. package/tool_registry-BdKlPuHS.js.map +0 -1
  289. package/validate-DgDZkUKs.js.map +0 -1
  290. package/validate-JOHfh-BB.mjs.map +0 -1
@@ -1,11 +1,12 @@
1
- import { o as isError } from "../../tool_registry-BaLUIzWz.mjs";
2
- import { t as Tool } from "../../tool-CcxYxA5X.mjs";
3
- import "../../common-BxoznqbX.mjs";
4
- import { n as Media } from "../../tool_call-B3MOog2R.mjs";
5
- import { n as SpooledJsonArtifact } from "../../spooled_markdown_artifact-DIYzbQv8.mjs";
1
+ import { o as isError } from "../../tool_registry-CZ55mIti.mjs";
2
+ import { t as Tool } from "../../tool-CITF20yJ.mjs";
3
+ import "../../common-BQoFv108.mjs";
4
+ import { n as Media } from "../../tool_call-D_k36aI4.mjs";
5
+ import { n as SpooledJsonArtifact } from "../../spooled_markdown_artifact-Da-Xnebh.mjs";
6
6
  import "../../guards.mjs";
7
+ import { EMPTY_MIME } from "./contracts.mjs";
7
8
  import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../exceptions-BLTn4Ofs.mjs";
8
- import { m as toPipe, o as VERB_INDEX, s as foldVerb, t as availableVerbs } from "../../validate-JOHfh-BB.mjs";
9
+ import { m as toPipe, o as VERB_INDEX, s as foldVerb, t as availableVerbs } from "../../validate-C_hTnDBE.mjs";
9
10
  import { validator } from "@nhtio/validation";
10
11
  //#region src/batteries/media/forge.ts
11
12
  /**
@@ -82,6 +83,44 @@ var enumerateMedia = (ctx) => {
82
83
  return out;
83
84
  };
84
85
  var failure = (code, message) => `Error (${code}): ${message}`;
86
+ /**
87
+ * Extract the format token from an `empty:<format>` sentinel id, or undefined for normal ids.
88
+ * Harness-minted ids are UUIDs, which can never start with `empty:` — the sentinel occupies
89
+ * input space that was previously a guaranteed MEDIA_NOT_FOUND.
90
+ */
91
+ var emptyFormatOf = (mediaId) => {
92
+ if (!mediaId.toLowerCase().startsWith("empty:")) return void 0;
93
+ return mediaId.slice(6).trim().toLowerCase();
94
+ };
95
+ /** The format tokens creatable in this deployment (reachable from {@link EMPTY_MIME}). */
96
+ var creatableFormats = (registry) => registry.convertTargets(EMPTY_MIME);
97
+ /** Materialize a new blank media payload for the sentinel, or a model-actionable failure. */
98
+ var materializeEmpty = async (registry, format, signal) => {
99
+ const creatable = creatableFormats(registry);
100
+ if (!creatable.includes(format)) return {
101
+ ok: false,
102
+ failure: failure("EMPTY_FORMAT_UNAVAILABLE", `cannot create "${format}" media in this deployment. Creatable formats: ${creatable.join(", ") || "(none)"}. Do not retry this format here.`)
103
+ };
104
+ const output = (await registry.convert({
105
+ bytes: new Uint8Array(0),
106
+ mimeType: EMPTY_MIME,
107
+ filename: "untitled",
108
+ to: format,
109
+ signal
110
+ })).outputs[0];
111
+ if (!output) return {
112
+ ok: false,
113
+ failure: failure("EMPTY_GENERATION_FAILED", `generating "${format}" produced no output`)
114
+ };
115
+ return {
116
+ ok: true,
117
+ payload: {
118
+ bytes: output.bytes,
119
+ mimeType: output.mimeType,
120
+ filename: `untitled.${format}`
121
+ }
122
+ };
123
+ };
85
124
  var mediaKindOf = (mimeType) => {
86
125
  const mime = mimeType.toLowerCase();
87
126
  if (mime.startsWith("image/")) return "image";
@@ -115,13 +154,17 @@ var renderResult = async (ctx, result, toolName) => {
115
154
  return typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2);
116
155
  };
117
156
  /** Resolve + acquire the input media or produce a model-actionable failure string. */
118
- var acquire = async (ctx, resolveMedia, mediaId) => {
157
+ var acquire = async (ctx, registry, resolveMedia, mediaId) => {
158
+ const format = emptyFormatOf(mediaId);
159
+ if (format !== void 0) return materializeEmpty(registry, format, ctx.abortSignal);
119
160
  const media = await resolveMedia(ctx, mediaId);
120
161
  if (!media) {
121
162
  const visible = enumerateMedia(ctx);
163
+ const creatable = creatableFormats(registry);
164
+ const createHint = creatable.length > 0 ? ` To create NEW media instead, pass media_id "empty:<format>" (available: ${creatable.join(", ")}).` : "";
122
165
  return {
123
166
  ok: false,
124
- failure: failure("MEDIA_NOT_FOUND", `no media with id "${mediaId}". ${visible.length > 0 ? `Visible media ids: ${visible.map((m) => `${m.id} (${m.filename})`).join(", ")}` : "No media is visible in this turn — ask the user to attach a file, or call list_media to check."}`)
167
+ failure: failure("MEDIA_NOT_FOUND", `no media with id "${mediaId}". ${visible.length > 0 ? `Visible media ids: ${visible.map((m) => `${m.id} (${m.filename})`).join(", ")}` : "No media is visible in this turn — ask the user to attach a file, or call list_media to check."}${createHint}`)
125
168
  };
126
169
  }
127
170
  return {
@@ -130,7 +173,13 @@ var acquire = async (ctx, resolveMedia, mediaId) => {
130
173
  };
131
174
  };
132
175
  /** Build a per-run @id ref resolver bound to this dispatch's visible media. */
133
- var refResolverFor = (ctx, resolveMedia) => async (id) => {
176
+ var refResolverFor = (ctx, registry, resolveMedia) => async (id) => {
177
+ const format = emptyFormatOf(id);
178
+ if (format !== void 0) {
179
+ const made = await materializeEmpty(registry, format, ctx.abortSignal);
180
+ if (!made.ok) throw new Error(made.failure);
181
+ return made.payload;
182
+ }
134
183
  const media = await resolveMedia(ctx, id);
135
184
  if (!media) {
136
185
  const visible = enumerateMedia(ctx).map((m) => m.id).join(", ");
@@ -201,6 +250,21 @@ var grammarText = (pipeline) => {
201
250
  replace: "[SSN]"
202
251
  }
203
252
  }] }));
253
+ const creatable = creatableFormats(pipeline.capabilities);
254
+ const creating = creatable.length > 0 ? [
255
+ "",
256
+ "Creating new media:",
257
+ `Pass media_id "empty:<format>" to create a NEW blank file instead of processing an existing one (available: ${creatable.join(", ")}).`,
258
+ `Example: media_id "empty:xlsx" with q "${toPipe({ steps: [{
259
+ verb: "sheet.update_cells",
260
+ args: { updates: [{
261
+ address: "A1",
262
+ value: "Title"
263
+ }] }
264
+ }] })}"`,
265
+ "Blank images are 1024×1024 white — resize in the same statement (empty:png + \"image resize width=64\").",
266
+ "The same sentinel works as a ref: merge with=@empty:xlsx."
267
+ ] : [];
204
268
  return [
205
269
  "Statements are pipe expressions: verb name=value ... | verb name=value ...",
206
270
  "Named args only. Indices are 1-based. Quote values containing spaces or dashes.",
@@ -209,6 +273,7 @@ var grammarText = (pipeline) => {
209
273
  "",
210
274
  "Available verbs:",
211
275
  ...lines,
276
+ ...creating,
212
277
  "",
213
278
  "Examples (remember: inside JSON tool args, backslashes must be doubled — \\\\d):",
214
279
  ...examples.map((e) => ` ${e}`)
@@ -228,13 +293,16 @@ var buildListMediaTool = (overrides) => {
228
293
  };
229
294
  var buildCompositeTool = (pipeline, resolveMedia, gate, overrides) => {
230
295
  const name = overrides?.media_query?.name ?? "media_query";
296
+ const description = overrides?.media_query?.description ?? `Runs a media-processing statement against a file from this conversation, locally (no external services). Provide media_id (from list_media) and either q (a pipe statement) or ops (structured steps).
297
+
298
+ ${grammarText(pipeline)}`;
299
+ const creatable = creatableFormats(pipeline.capabilities);
300
+ const mediaIdDescription = creatable.length > 0 ? `The id of the media to process (call list_media to discover ids), or "empty:<format>" to create a new blank file (available: ${creatable.join(", ")}).` : "The id of the media to process (call list_media to discover ids).";
231
301
  return new Tool({
232
302
  name,
233
- description: overrides?.media_query?.description ?? `Runs a media-processing statement against a file from this conversation, locally (no external services). Provide media_id (from list_media) and either q (a pipe statement) or ops (structured steps).
234
-
235
- ${grammarText(pipeline)}`,
303
+ description,
236
304
  inputSchema: validator.object({
237
- media_id: validator.string().required().description("The id of the media to process (call list_media to discover ids)."),
305
+ media_id: validator.string().required().description(mediaIdDescription),
238
306
  q: validator.string().description("A pipe statement, e.g. \"select pages=2-5 | extract text\"."),
239
307
  ops: validator.array().items(validator.object({
240
308
  verb: validator.string().required(),
@@ -246,12 +314,12 @@ ${grammarText(pipeline)}`,
246
314
  if (q === void 0 === (ops === void 0)) return failure("BAD_REQUEST", "provide exactly one of q (pipe statement) or ops (structured steps)");
247
315
  const dispatch = ctx;
248
316
  await runGate(gate, dispatch, name, args);
249
- const acquired = await acquire(dispatch, resolveMedia, mediaId);
317
+ const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId);
250
318
  if (!acquired.ok) return acquired.failure;
251
319
  try {
252
320
  const runOptions = {
253
321
  signal: dispatch.abortSignal,
254
- resolveRef: refResolverFor(dispatch, resolveMedia)
322
+ resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia)
255
323
  };
256
324
  return await renderResult(dispatch, q !== void 0 ? await pipeline.query(acquired.payload, q, runOptions) : await pipeline.ops(acquired.payload, ops, runOptions), name);
257
325
  } catch (err) {
@@ -269,8 +337,9 @@ var opArgsFrom = (args) => {
269
337
  }
270
338
  return out;
271
339
  };
272
- var granularSchemaFor = (spec) => {
273
- const shape = { media_id: validator.string().required().description("The id of the media to process (call list_media to discover ids).") };
340
+ var granularSchemaFor = (spec, creatable) => {
341
+ const mediaIdDescription = creatable.length > 0 ? `The id of the media to process (call list_media to discover ids), or "empty:<format>" to create a new blank file (available: ${creatable.join(", ")}).` : "The id of the media to process (call list_media to discover ids).";
342
+ const shape = { media_id: validator.string().required().description(mediaIdDescription) };
274
343
  for (const [argName, arg] of Object.entries(spec.args)) {
275
344
  let schema;
276
345
  switch (arg.type) {
@@ -335,12 +404,12 @@ var buildGranularTool = (pipeline, spec, resolveMedia, gate, overrides) => {
335
404
  return new Tool({
336
405
  name,
337
406
  description: overrides?.[defaultName]?.description ?? `${spec.description} Processes a media file from this conversation locally. Indices are 1-based.`,
338
- inputSchema: granularSchemaFor(spec),
407
+ inputSchema: granularSchemaFor(spec, creatableFormats(pipeline.capabilities)),
339
408
  handler: async (args, ctx) => {
340
409
  const dispatch = ctx;
341
410
  const { media_id: mediaId } = args;
342
411
  await runGate(gate, dispatch, name, args);
343
- const acquired = await acquire(dispatch, resolveMedia, mediaId);
412
+ const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId);
344
413
  if (!acquired.ok) return acquired.failure;
345
414
  const opArgs = opArgsFrom(args);
346
415
  normalizeRefArgs(spec, opArgs);
@@ -350,7 +419,7 @@ var buildGranularTool = (pipeline, spec, resolveMedia, gate, overrides) => {
350
419
  args: opArgs
351
420
  }], {
352
421
  signal: dispatch.abortSignal,
353
- resolveRef: refResolverFor(dispatch, resolveMedia)
422
+ resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia)
354
423
  }), name);
355
424
  } catch (err) {
356
425
  return renderError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"forge.mjs","names":[],"sources":["../../../src/batteries/media/forge.ts"],"sourcesContent":["/**\n * Agent glue: forge ADK tools from a media pipeline, so a model can drive local media work.\n *\n * @module @nhtio/adk/batteries/media/forge\n *\n * @remarks\n * The pipeline core (`@nhtio/adk/batteries/media`) is agent-agnostic; this module is the layer\n * that knows the ADK loop. {@link forgeMediaTools} mints `Tool` instances over a configured\n * {@link @nhtio/adk/batteries/media!MediaPipeline} in one of two surfaces (the consumer picks\n * per deployment):\n *\n * - `'composite'` — one `media_query` tool taking `{ media_id, q }` (a pipe expression — the\n * headline LLM DSL) or `{ media_id, ops }` (structured form), plus `list_media`. The tool\n * description embeds the engine-narrowed verb grammar so the model never sees a verb the\n * deployment can't run. One round-trip for multi-step work.\n * - `'granular'` — one narrow tool per available verb (`doc_select`, `sheet_update_cells`, …),\n * each internally a one-verb plan. Friendlier to small models; bigger tool list.\n *\n * Media flows in by reference: the model passes `media_id` values it discovered via the\n * `list_media` tool (or inline id markers, where the LLM battery renders them); the resolver\n * scans `ctx.turnMessages[].attachments` and `ctx.turnToolCalls[].results` by default. Outputs\n * are persisted through `ctx.storeMediaBytes` and returned as `Media.toolGenerated(...)`, so\n * file results land on `ToolCall.results` as first-class media.\n *\n * Processing failures return readable strings (`Error (CODE): …`) the model can act on; the\n * pipe DSL's own syntax/semantic errors render the same way, so a model can repair its\n * statement and retry. An optional {@link ToolGateFn} runs before every execution — the seam\n * for human-approval/RBAC flows built on `ctx.waitFor` (the ADK gates primitive).\n */\n\nimport { toPipe } from './plan'\nimport { isError } from '@nhtio/adk/guards'\nimport { availableVerbs } from './validate'\nimport { validator } from '@nhtio/validation'\nimport { VERB_INDEX, foldVerb } from './verbs'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from './exceptions'\nimport { Tool, Media, SpooledJsonArtifact } from '@nhtio/adk/common'\nimport type { MediaPipeline } from './index'\nimport type { MediaOp, MediaArgValue } from './plan'\nimport type { VerbSpec, VerbArgSpec } from './verbs'\nimport type { DispatchContext } from '@nhtio/adk/types'\nimport type { StepPayload, PlanResult } from './runtime'\n\n/**\n * Resolves a `media_id` to a {@link @nhtio/adk!Media} visible in the current dispatch.\n * The default implementation scans turn messages' attachments and prior tool-call results.\n */\nexport type MediaResolverFn = (\n ctx: DispatchContext,\n mediaId: string\n) => Media | undefined | Promise<Media | undefined>\n\n/**\n * Optional per-call gate run before any pipeline execution. Throwing aborts the call and\n * surfaces through the standard tool-error path. The canonical implementation awaits\n * `ctx.waitFor({ reason: 'tool_approval', payload: call })` and throws on denial — WHO\n * approves and HOW is the consumer's contract; this is the seam.\n */\nexport type ToolGateFn = (\n ctx: DispatchContext,\n call: { tool: string; args: unknown }\n) => void | Promise<void>\n\n/** Options for {@link forgeMediaTools}. */\nexport interface ForgeMediaToolsOptions {\n /** Which tool surface to mint. */\n surface: 'composite' | 'granular'\n /** Media-id resolution. Default: scan turn attachments + tool-call Media results. */\n resolveMedia?: MediaResolverFn\n /** Optional pre-execution gate (see {@link ToolGateFn}). */\n gate?: ToolGateFn\n /** Per-tool name/description overrides, keyed by the minted tool's default name. */\n overrides?: Record<string, { name?: string; description?: string }>\n}\n\n/**\n * The default media resolver: scan `ctx.turnMessages[].attachments`, then\n * `ctx.turnToolCalls[].results`, for a Media with the given id.\n *\n * @param ctx - The dispatch context.\n * @param mediaId - The id to find.\n * @returns The Media, or `undefined` when nothing in the turn carries that id.\n */\nexport const defaultResolveMedia: MediaResolverFn = (ctx, mediaId) => {\n for (const message of ctx.turnMessages) {\n for (const attachment of message.attachments ?? []) {\n if (attachment.id === mediaId) return attachment\n }\n }\n for (const toolCall of ctx.turnToolCalls) {\n const results = toolCall.results\n if (Media.isMedia(results) && results.id === mediaId) return results\n if (Array.isArray(results)) {\n const hit = results.find((r) => Media.isMedia(r) && r.id === mediaId)\n if (hit) return hit as Media\n }\n }\n return undefined\n}\n\n/** Enumerate every Media visible in the turn (for `list_media` and MEDIA_NOT_FOUND hints). */\nconst enumerateMedia = (\n ctx: DispatchContext\n): Array<{\n id: string\n filename: string\n kind: string\n mimeType: string\n source?: string\n origin: string\n}> => {\n const out: Array<{\n id: string\n filename: string\n kind: string\n mimeType: string\n source?: string\n origin: string\n }> = []\n for (const message of ctx.turnMessages) {\n for (const attachment of message.attachments ?? []) {\n out.push({\n id: attachment.id,\n filename: attachment.filename,\n kind: attachment.kind,\n mimeType: attachment.mimeType,\n source: attachment.source,\n origin: 'attachment',\n })\n }\n }\n for (const toolCall of ctx.turnToolCalls) {\n const results = toolCall.results\n const mediaList = Media.isMedia(results)\n ? [results]\n : Array.isArray(results)\n ? results.filter((r): r is Media => Media.isMedia(r))\n : []\n for (const media of mediaList) {\n out.push({\n id: media.id,\n filename: media.filename,\n kind: media.kind,\n mimeType: media.mimeType,\n source: media.source,\n origin: 'tool-result',\n })\n }\n }\n return out\n}\n\nconst failure = (code: string, message: string): string => `Error (${code}): ${message}`\n\nconst mediaKindOf = (mimeType: string): 'image' | 'audio' | 'video' | 'document' => {\n const mime = mimeType.toLowerCase()\n if (mime.startsWith('image/')) return 'image'\n if (mime.startsWith('audio/')) return 'audio'\n if (mime.startsWith('video/')) return 'video'\n return 'document'\n}\n\n/** Load a resolved Media's bytes into a pipeline payload. */\nconst toPayload = async (media: Media): Promise<StepPayload> => ({\n bytes: await media.asBytes(),\n mimeType: media.mimeType,\n filename: media.filename,\n})\n\n/** Persist a payload through the dispatch byte conduit and mint a first-party Media. */\nconst toMedia = async (\n ctx: DispatchContext,\n payload: StepPayload,\n toolName: string\n): Promise<Media> => {\n const id = crypto.randomUUID()\n const reader = await ctx.storeMediaBytes(id, payload.bytes)\n return Media.toolGenerated({\n id,\n kind: mediaKindOf(payload.mimeType),\n mimeType: payload.mimeType,\n filename: payload.filename,\n reader,\n source: `tool:${toolName}`,\n })\n}\n\n/** Render a plan result as a tool handler return value. */\nconst renderResult = async (\n ctx: DispatchContext,\n result: PlanResult,\n toolName: string\n): Promise<string | Media | Media[]> => {\n if (result.kind === 'media') return toMedia(ctx, result.payload, toolName)\n if (result.kind === 'media-list') {\n return Promise.all(result.payloads.map((p) => toMedia(ctx, p, toolName)))\n }\n return typeof result.data === 'string' ? result.data : JSON.stringify(result.data, null, 2)\n}\n\n/** Resolve + acquire the input media or produce a model-actionable failure string. */\nconst acquire = async (\n ctx: DispatchContext,\n resolveMedia: MediaResolverFn,\n mediaId: string\n): Promise<{ ok: true; payload: StepPayload } | { ok: false; failure: string }> => {\n const media = await resolveMedia(ctx, mediaId)\n if (!media) {\n const visible = enumerateMedia(ctx)\n const hint =\n visible.length > 0\n ? `Visible media ids: ${visible.map((m) => `${m.id} (${m.filename})`).join(', ')}`\n : 'No media is visible in this turn — ask the user to attach a file, or call list_media to check.'\n return {\n ok: false,\n failure: failure('MEDIA_NOT_FOUND', `no media with id \"${mediaId}\". ${hint}`),\n }\n }\n return { ok: true, payload: await toPayload(media) }\n}\n\n/** Build a per-run @id ref resolver bound to this dispatch's visible media. */\nconst refResolverFor =\n (ctx: DispatchContext, resolveMedia: MediaResolverFn) =>\n async (id: string): Promise<StepPayload> => {\n const media = await resolveMedia(ctx, id)\n if (!media) {\n const visible = enumerateMedia(ctx)\n .map((m) => m.id)\n .join(', ')\n throw new Error(\n `referenced media @${id} was not found in this turn. Visible ids: ${visible || '(none)'}`\n )\n }\n return toPayload(media)\n }\n\n/** Run the optional gate; a throw becomes the tool's error (standard downstream wrapping). */\nconst runGate = async (\n gate: ToolGateFn | undefined,\n ctx: DispatchContext,\n tool: string,\n args: unknown\n): Promise<void> => {\n if (gate) await gate(ctx, { tool, args })\n}\n\n/** Map a thrown pipeline error to a readable failure string the model can act on. */\nconst renderError = (err: unknown): string => {\n if (isError(err) && err.name.startsWith('E_MEDIA_')) {\n return failure(err.name.replace(/^E_MEDIA_/, ''), err.message)\n }\n throw err\n}\n\n// ── grammar text generation (composite surface) ──────────────────────────────\n\nconst argHelp = (name: string, arg: VerbArgSpec): string => {\n const req = arg.required ? '' : '?'\n const type =\n arg.type === 'enum' ? (arg.values ?? []).join('|') : arg.type === 'json' ? \"'<JSON>'\" : arg.type\n return `${name}${req}=${type}`\n}\n\nconst verbHelp = (spec: VerbSpec): string => {\n const verb = spec.id.replace(/\\./g, ' ')\n const args = Object.entries(spec.args)\n .map(([n, a]) => argHelp(n, a))\n .join(' ')\n return args.length > 0 ? `${verb} ${args}` : verb\n}\n\n/**\n * Generate the model-facing grammar text for the configured deployment: only available verbs,\n * with arg signatures, plus few-shot examples rendered from real plans via `toPipe` so they\n * can never drift from the parser.\n */\nconst grammarText = (pipeline: MediaPipeline): string => {\n const available = new Set(availableVerbs(pipeline.capabilities))\n const lines: string[] = []\n for (const spec of VERB_INDEX.values()) {\n if (!available.has(spec.id.replace(/[._]+/g, ' '))) continue\n lines.push(`- ${verbHelp(spec)} — ${spec.description}`)\n }\n const examples: string[] = [\n toPipe({\n steps: [\n { verb: 'select', args: { pages: [2, 3, 4, 5] } },\n { verb: 'extract.text', args: {} },\n ],\n }),\n toPipe({\n steps: [\n { verb: 'extract.text', args: {} },\n { verb: 'chunk', args: { by: 'sentence', size: 512 } },\n ],\n }),\n ]\n if (available.has('redact')) {\n examples.push(\n toPipe({\n steps: [\n {\n verb: 'redact',\n args: { match: [{ source: '\\\\d{3}-\\\\d{2}-\\\\d{4}', flags: '' }], replace: '[SSN]' },\n },\n ],\n })\n )\n }\n return [\n 'Statements are pipe expressions: verb name=value ... | verb name=value ...',\n 'Named args only. Indices are 1-based. Quote values containing spaces or dashes.',\n 'Structured payloads are quoted JSON: updates=\\'[{\"address\":\"B2\",\"value\":3}]\\'.',\n 'Reference other media inline by id: merge with=@<media id> (ids come from list_media).',\n '',\n 'Available verbs:',\n ...lines,\n '',\n 'Examples (remember: inside JSON tool args, backslashes must be doubled — \\\\\\\\d):',\n ...examples.map((e) => ` ${e}`),\n ].join('\\n')\n}\n\n// ── the tools ────────────────────────────────────────────────────────────────\n\nconst buildListMediaTool = (overrides: ForgeMediaToolsOptions['overrides']): Tool => {\n const name = overrides?.list_media?.name ?? 'list_media'\n return new Tool({\n name,\n description:\n overrides?.list_media?.description ??\n 'Lists every media file visible in this conversation turn (user attachments and files produced by prior tool calls), with the media ids other media tools require.',\n inputSchema: validator.object({}),\n artifactConstructor: () => SpooledJsonArtifact,\n handler: async (_args, ctx) => {\n const media = enumerateMedia(ctx as DispatchContext)\n return JSON.stringify(media, null, 2)\n },\n })\n}\n\nconst buildCompositeTool = (\n pipeline: MediaPipeline,\n resolveMedia: MediaResolverFn,\n gate: ToolGateFn | undefined,\n overrides: ForgeMediaToolsOptions['overrides']\n): Tool => {\n const name = overrides?.media_query?.name ?? 'media_query'\n const description =\n overrides?.media_query?.description ??\n `Runs a media-processing statement against a file from this conversation, locally (no external services). Provide media_id (from list_media) and either q (a pipe statement) or ops (structured steps).\n\n${grammarText(pipeline)}`\n return new Tool({\n name,\n description,\n inputSchema: validator.object({\n media_id: validator\n .string()\n .required()\n .description('The id of the media to process (call list_media to discover ids).'),\n q: validator\n .string()\n .description('A pipe statement, e.g. \"select pages=2-5 | extract text\".'),\n ops: validator\n .array()\n .items(\n validator.object({\n verb: validator.string().required(),\n args: validator.object().unknown(true).required(),\n })\n )\n .description('Structured alternative to q: [{ verb, args }] steps.'),\n }),\n handler: async (args, ctx) => {\n const {\n media_id: mediaId,\n q,\n ops,\n } = args as { media_id: string; q?: string; ops?: MediaOp[] }\n if ((q === undefined) === (ops === undefined)) {\n return failure(\n 'BAD_REQUEST',\n 'provide exactly one of q (pipe statement) or ops (structured steps)'\n )\n }\n const dispatch = ctx as DispatchContext\n await runGate(gate, dispatch, name, args)\n const acquired = await acquire(dispatch, resolveMedia, mediaId)\n if (!acquired.ok) return acquired.failure\n try {\n const runOptions = {\n signal: dispatch.abortSignal,\n resolveRef: refResolverFor(dispatch, resolveMedia),\n }\n const result =\n q !== undefined\n ? await pipeline.query(acquired.payload, q, runOptions)\n : await pipeline.ops(acquired.payload, ops as MediaOp[], runOptions)\n return await renderResult(dispatch, result, name)\n } catch (err) {\n return renderError(err)\n }\n },\n })\n}\n\n/** Convert a granular tool's flat args into the verb's op args (drops media_id). */\nconst opArgsFrom = (args: Record<string, unknown>): Record<string, MediaArgValue> => {\n const out: Record<string, MediaArgValue> = {}\n for (const [k, v] of Object.entries(args)) {\n if (k === 'media_id' || v === undefined) continue\n out[k] = v as MediaArgValue\n }\n return out\n}\n\nconst granularSchemaFor = (spec: VerbSpec): ReturnType<typeof validator.object> => {\n const shape: Record<string, ReturnType<typeof validator.any>> = {\n media_id: validator\n .string()\n .required()\n .description('The id of the media to process (call list_media to discover ids).'),\n }\n for (const [argName, arg] of Object.entries(spec.args)) {\n let schema\n switch (arg.type) {\n case 'number':\n schema = validator.number()\n if (arg.min !== undefined) schema = schema.min(arg.min)\n if (arg.max !== undefined) schema = schema.max(arg.max)\n break\n case 'boolean':\n schema = validator.boolean()\n break\n case 'enum':\n schema = validator.string().valid(...(arg.values ?? []))\n break\n case 'number-list':\n schema = validator.array().items(validator.number().min(arg.min ?? 1))\n break\n case 'string-list':\n schema = arg.values\n ? validator.array().items(validator.string().valid(...arg.values))\n : validator.array().items(validator.string())\n break\n case 'media-ref':\n schema = validator\n .string()\n .description('A media id (from list_media) for the other media in this operation.')\n break\n case 'media-ref-list':\n schema = validator.array().items(validator.string())\n break\n case 'name-or-index':\n schema = validator\n .alternatives()\n .try(validator.number().integer().min(1), validator.string())\n break\n case 'regex-or-string-list':\n schema = validator\n .alternatives()\n .try(validator.string(), validator.array().items(validator.string()))\n break\n case 'json':\n schema = validator.any().optional()\n break\n default:\n schema = validator.string()\n }\n schema = schema.description(arg.description)\n if (arg.required) schema = schema.required()\n shape[argName] = schema\n }\n return validator.object(shape)\n}\n\n/** Normalize a granular tool's media-ref string args to MediaRef IR values. */\nconst normalizeRefArgs = (spec: VerbSpec, args: Record<string, MediaArgValue>): void => {\n for (const [argName, arg] of Object.entries(spec.args)) {\n const value = args[argName]\n if (value === undefined) continue\n if (arg.type === 'media-ref' && typeof value === 'string') {\n args[argName] = { kind: 'id', id: value }\n }\n if (arg.type === 'media-ref-list' && Array.isArray(value)) {\n args[argName] = (value as unknown[]).map((v) =>\n typeof v === 'string' ? { kind: 'id', id: v } : (v as MediaArgValue)\n ) as MediaArgValue\n }\n }\n}\n\nconst buildGranularTool = (\n pipeline: MediaPipeline,\n spec: VerbSpec,\n resolveMedia: MediaResolverFn,\n gate: ToolGateFn | undefined,\n overrides: ForgeMediaToolsOptions['overrides']\n): Tool => {\n const defaultName = spec.id.replace(/\\./g, '_')\n const name = overrides?.[defaultName]?.name ?? defaultName\n return new Tool({\n name,\n description:\n overrides?.[defaultName]?.description ??\n `${spec.description} Processes a media file from this conversation locally. Indices are 1-based.`,\n inputSchema: granularSchemaFor(spec),\n handler: async (args, ctx) => {\n const dispatch = ctx as DispatchContext\n const { media_id: mediaId } = args as { media_id: string }\n await runGate(gate, dispatch, name, args)\n const acquired = await acquire(dispatch, resolveMedia, mediaId)\n if (!acquired.ok) return acquired.failure\n const opArgs = opArgsFrom(args as Record<string, unknown>)\n normalizeRefArgs(spec, opArgs)\n try {\n const result = await pipeline.ops(acquired.payload, [{ verb: spec.id, args: opArgs }], {\n signal: dispatch.abortSignal,\n resolveRef: refResolverFor(dispatch, resolveMedia),\n })\n return await renderResult(dispatch, result, name)\n } catch (err) {\n return renderError(err)\n }\n },\n })\n}\n\n/**\n * Forge agent tools over a configured media pipeline.\n *\n * @remarks\n * The minted set follows the pipeline's configured engines: in the composite surface the\n * `media_query` grammar text only advertises available verbs; in the granular surface a tool\n * is minted only for verbs whose engine (if any) is configured. Either way `list_media` is\n * included — it is the model's entry point for discovering `media_id` values.\n *\n * The returned record is keyed by tool name so consumers can register selectively or pass\n * `Object.values(tools)` to `TurnRunnerConfig.tools`.\n *\n * @param pipeline - A pipeline from `createMediaPipeline`.\n * @param options - Surface, resolver, gate, and overrides.\n * @returns The minted tools, keyed by name.\n */\nexport const forgeMediaTools = (\n pipeline: MediaPipeline,\n options: ForgeMediaToolsOptions\n): Record<string, Tool> => {\n if (options?.surface !== 'composite' && options?.surface !== 'granular') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `forgeMediaTools requires surface: 'composite' | 'granular'`,\n ])\n }\n const resolveMedia = options.resolveMedia ?? defaultResolveMedia\n const tools: Record<string, Tool> = {}\n const listTool = buildListMediaTool(options.overrides)\n tools[listTool.name] = listTool\n\n if (options.surface === 'composite') {\n const composite = buildCompositeTool(pipeline, resolveMedia, options.gate, options.overrides)\n tools[composite.name] = composite\n return tools\n }\n\n const available = new Set(availableVerbs(pipeline.capabilities))\n for (const spec of VERB_INDEX.values()) {\n if (!available.has(spec.id.replace(/[._]+/g, ' '))) continue\n const tool = buildGranularTool(pipeline, spec, resolveMedia, options.gate, options.overrides)\n tools[tool.name] = tool\n }\n return tools\n}\n\n/** Re-exported so consumers can fold verbs the same way the forge does. */\nexport { foldVerb }\n"],"mappings":"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"}
1
+ {"version":3,"file":"forge.mjs","names":[],"sources":["../../../src/batteries/media/forge.ts"],"sourcesContent":["/**\n * Agent glue: forge ADK tools from a media pipeline, so a model can drive local media work.\n *\n * @module @nhtio/adk/batteries/media/forge\n *\n * @remarks\n * The pipeline core (`@nhtio/adk/batteries/media`) is agent-agnostic; this module is the layer\n * that knows the ADK loop. {@link forgeMediaTools} mints `Tool` instances over a configured\n * {@link @nhtio/adk/batteries/media!MediaPipeline} in one of two surfaces (the consumer picks\n * per deployment):\n *\n * - `'composite'` — one `media_query` tool taking `{ media_id, q }` (a pipe expression — the\n * headline LLM DSL) or `{ media_id, ops }` (structured form), plus `list_media`. The tool\n * description embeds the engine-narrowed verb grammar so the model never sees a verb the\n * deployment can't run. One round-trip for multi-step work.\n * - `'granular'` — one narrow tool per available verb (`doc_select`, `sheet_update_cells`, …),\n * each internally a one-verb plan. Friendlier to small models; bigger tool list.\n *\n * Media flows in by reference: the model passes `media_id` values it discovered via the\n * `list_media` tool (or inline id markers, where the LLM battery renders them); the resolver\n * scans `ctx.turnMessages[].attachments` and `ctx.turnToolCalls[].results` by default. Outputs\n * are persisted through `ctx.storeMediaBytes` and returned as `Media.toolGenerated(...)`, so\n * file results land on `ToolCall.results` as first-class media.\n *\n * Processing failures return readable strings (`Error (CODE): …`) the model can act on; the\n * pipe DSL's own syntax/semantic errors render the same way, so a model can repair its\n * statement and retry. An optional {@link ToolGateFn} runs before every execution — the seam\n * for human-approval/RBAC flows built on `ctx.waitFor` (the ADK gates primitive).\n */\n\nimport { toPipe } from './plan'\nimport { EMPTY_MIME } from './contracts'\nimport { isError } from '@nhtio/adk/guards'\nimport { availableVerbs } from './validate'\nimport { validator } from '@nhtio/validation'\nimport { VERB_INDEX, foldVerb } from './verbs'\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from './exceptions'\nimport { Tool, Media, SpooledJsonArtifact } from '@nhtio/adk/common'\nimport type { MediaPipeline } from './index'\nimport type { EngineRegistry } from './registry'\nimport type { MediaOp, MediaArgValue } from './plan'\nimport type { VerbSpec, VerbArgSpec } from './verbs'\nimport type { DispatchContext } from '@nhtio/adk/types'\nimport type { StepPayload, PlanResult } from './runtime'\n\n/**\n * Resolves a `media_id` to a {@link @nhtio/adk!Media} visible in the current dispatch.\n * The default implementation scans turn messages' attachments and prior tool-call results.\n */\nexport type MediaResolverFn = (\n ctx: DispatchContext,\n mediaId: string\n) => Media | undefined | Promise<Media | undefined>\n\n/**\n * Optional per-call gate run before any pipeline execution. Throwing aborts the call and\n * surfaces through the standard tool-error path. The canonical implementation awaits\n * `ctx.waitFor({ reason: 'tool_approval', payload: call })` and throws on denial — WHO\n * approves and HOW is the consumer's contract; this is the seam.\n */\nexport type ToolGateFn = (\n ctx: DispatchContext,\n call: { tool: string; args: unknown }\n) => void | Promise<void>\n\n/** Options for {@link forgeMediaTools}. */\nexport interface ForgeMediaToolsOptions {\n /** Which tool surface to mint. */\n surface: 'composite' | 'granular'\n /** Media-id resolution. Default: scan turn attachments + tool-call Media results. */\n resolveMedia?: MediaResolverFn\n /** Optional pre-execution gate (see {@link ToolGateFn}). */\n gate?: ToolGateFn\n /** Per-tool name/description overrides, keyed by the minted tool's default name. */\n overrides?: Record<string, { name?: string; description?: string }>\n}\n\n/**\n * The default media resolver: scan `ctx.turnMessages[].attachments`, then\n * `ctx.turnToolCalls[].results`, for a Media with the given id.\n *\n * @param ctx - The dispatch context.\n * @param mediaId - The id to find.\n * @returns The Media, or `undefined` when nothing in the turn carries that id.\n */\nexport const defaultResolveMedia: MediaResolverFn = (ctx, mediaId) => {\n for (const message of ctx.turnMessages) {\n for (const attachment of message.attachments ?? []) {\n if (attachment.id === mediaId) return attachment\n }\n }\n for (const toolCall of ctx.turnToolCalls) {\n const results = toolCall.results\n if (Media.isMedia(results) && results.id === mediaId) return results\n if (Array.isArray(results)) {\n const hit = results.find((r) => Media.isMedia(r) && r.id === mediaId)\n if (hit) return hit as Media\n }\n }\n return undefined\n}\n\n/** Enumerate every Media visible in the turn (for `list_media` and MEDIA_NOT_FOUND hints). */\nconst enumerateMedia = (\n ctx: DispatchContext\n): Array<{\n id: string\n filename: string\n kind: string\n mimeType: string\n source?: string\n origin: string\n}> => {\n const out: Array<{\n id: string\n filename: string\n kind: string\n mimeType: string\n source?: string\n origin: string\n }> = []\n for (const message of ctx.turnMessages) {\n for (const attachment of message.attachments ?? []) {\n out.push({\n id: attachment.id,\n filename: attachment.filename,\n kind: attachment.kind,\n mimeType: attachment.mimeType,\n source: attachment.source,\n origin: 'attachment',\n })\n }\n }\n for (const toolCall of ctx.turnToolCalls) {\n const results = toolCall.results\n const mediaList = Media.isMedia(results)\n ? [results]\n : Array.isArray(results)\n ? results.filter((r): r is Media => Media.isMedia(r))\n : []\n for (const media of mediaList) {\n out.push({\n id: media.id,\n filename: media.filename,\n kind: media.kind,\n mimeType: media.mimeType,\n source: media.source,\n origin: 'tool-result',\n })\n }\n }\n return out\n}\n\nconst failure = (code: string, message: string): string => `Error (${code}): ${message}`\n\n// ── media generation (the empty:<format> sentinel) ───────────────────────────\n\n/**\n * Extract the format token from an `empty:<format>` sentinel id, or undefined for normal ids.\n * Harness-minted ids are UUIDs, which can never start with `empty:` — the sentinel occupies\n * input space that was previously a guaranteed MEDIA_NOT_FOUND.\n */\nconst emptyFormatOf = (mediaId: string): string | undefined => {\n if (!mediaId.toLowerCase().startsWith('empty:')) return undefined\n return mediaId.slice('empty:'.length).trim().toLowerCase()\n}\n\n/** The format tokens creatable in this deployment (reachable from {@link EMPTY_MIME}). */\nconst creatableFormats = (registry: EngineRegistry): readonly string[] =>\n registry.convertTargets(EMPTY_MIME)\n\n/** Materialize a new blank media payload for the sentinel, or a model-actionable failure. */\nconst materializeEmpty = async (\n registry: EngineRegistry,\n format: string,\n signal?: AbortSignal\n): Promise<{ ok: true; payload: StepPayload } | { ok: false; failure: string }> => {\n const creatable = creatableFormats(registry)\n if (!creatable.includes(format)) {\n return {\n ok: false,\n failure: failure(\n 'EMPTY_FORMAT_UNAVAILABLE',\n `cannot create \"${format}\" media in this deployment. Creatable formats: ${creatable.join(', ') || '(none)'}. Do not retry this format here.`\n ),\n }\n }\n const result = await registry.convert({\n bytes: new Uint8Array(0),\n mimeType: EMPTY_MIME,\n filename: 'untitled',\n to: format,\n signal,\n })\n const output = result.outputs[0]\n if (!output) {\n return {\n ok: false,\n failure: failure('EMPTY_GENERATION_FAILED', `generating \"${format}\" produced no output`),\n }\n }\n return {\n ok: true,\n payload: {\n bytes: output.bytes,\n mimeType: output.mimeType,\n filename: `untitled.${format}`,\n },\n }\n}\n\nconst mediaKindOf = (mimeType: string): 'image' | 'audio' | 'video' | 'document' => {\n const mime = mimeType.toLowerCase()\n if (mime.startsWith('image/')) return 'image'\n if (mime.startsWith('audio/')) return 'audio'\n if (mime.startsWith('video/')) return 'video'\n return 'document'\n}\n\n/** Load a resolved Media's bytes into a pipeline payload. */\nconst toPayload = async (media: Media): Promise<StepPayload> => ({\n bytes: await media.asBytes(),\n mimeType: media.mimeType,\n filename: media.filename,\n})\n\n/** Persist a payload through the dispatch byte conduit and mint a first-party Media. */\nconst toMedia = async (\n ctx: DispatchContext,\n payload: StepPayload,\n toolName: string\n): Promise<Media> => {\n const id = crypto.randomUUID()\n const reader = await ctx.storeMediaBytes(id, payload.bytes)\n return Media.toolGenerated({\n id,\n kind: mediaKindOf(payload.mimeType),\n mimeType: payload.mimeType,\n filename: payload.filename,\n reader,\n source: `tool:${toolName}`,\n })\n}\n\n/** Render a plan result as a tool handler return value. */\nconst renderResult = async (\n ctx: DispatchContext,\n result: PlanResult,\n toolName: string\n): Promise<string | Media | Media[]> => {\n if (result.kind === 'media') return toMedia(ctx, result.payload, toolName)\n if (result.kind === 'media-list') {\n return Promise.all(result.payloads.map((p) => toMedia(ctx, p, toolName)))\n }\n return typeof result.data === 'string' ? result.data : JSON.stringify(result.data, null, 2)\n}\n\n/** Resolve + acquire the input media or produce a model-actionable failure string. */\nconst acquire = async (\n ctx: DispatchContext,\n registry: EngineRegistry,\n resolveMedia: MediaResolverFn,\n mediaId: string\n): Promise<{ ok: true; payload: StepPayload } | { ok: false; failure: string }> => {\n // The empty:<format> sentinel creates NEW media instead of resolving existing bytes.\n const format = emptyFormatOf(mediaId)\n if (format !== undefined) return materializeEmpty(registry, format, ctx.abortSignal)\n const media = await resolveMedia(ctx, mediaId)\n if (!media) {\n const visible = enumerateMedia(ctx)\n const creatable = creatableFormats(registry)\n const createHint =\n creatable.length > 0\n ? ` To create NEW media instead, pass media_id \"empty:<format>\" (available: ${creatable.join(', ')}).`\n : ''\n const hint =\n visible.length > 0\n ? `Visible media ids: ${visible.map((m) => `${m.id} (${m.filename})`).join(', ')}`\n : 'No media is visible in this turn — ask the user to attach a file, or call list_media to check.'\n return {\n ok: false,\n failure: failure('MEDIA_NOT_FOUND', `no media with id \"${mediaId}\". ${hint}${createHint}`),\n }\n }\n return { ok: true, payload: await toPayload(media) }\n}\n\n/** Build a per-run @id ref resolver bound to this dispatch's visible media. */\nconst refResolverFor =\n (ctx: DispatchContext, registry: EngineRegistry, resolveMedia: MediaResolverFn) =>\n async (id: string): Promise<StepPayload> => {\n // @empty:<format> refs mint a blank file inline (merge with=@empty:xlsx, …).\n const format = emptyFormatOf(id)\n if (format !== undefined) {\n const made = await materializeEmpty(registry, format, ctx.abortSignal)\n if (!made.ok) throw new Error(made.failure)\n return made.payload\n }\n const media = await resolveMedia(ctx, id)\n if (!media) {\n const visible = enumerateMedia(ctx)\n .map((m) => m.id)\n .join(', ')\n throw new Error(\n `referenced media @${id} was not found in this turn. Visible ids: ${visible || '(none)'}`\n )\n }\n return toPayload(media)\n }\n\n/** Run the optional gate; a throw becomes the tool's error (standard downstream wrapping). */\nconst runGate = async (\n gate: ToolGateFn | undefined,\n ctx: DispatchContext,\n tool: string,\n args: unknown\n): Promise<void> => {\n if (gate) await gate(ctx, { tool, args })\n}\n\n/** Map a thrown pipeline error to a readable failure string the model can act on. */\nconst renderError = (err: unknown): string => {\n if (isError(err) && err.name.startsWith('E_MEDIA_')) {\n return failure(err.name.replace(/^E_MEDIA_/, ''), err.message)\n }\n throw err\n}\n\n// ── grammar text generation (composite surface) ──────────────────────────────\n\nconst argHelp = (name: string, arg: VerbArgSpec): string => {\n const req = arg.required ? '' : '?'\n const type =\n arg.type === 'enum' ? (arg.values ?? []).join('|') : arg.type === 'json' ? \"'<JSON>'\" : arg.type\n return `${name}${req}=${type}`\n}\n\nconst verbHelp = (spec: VerbSpec): string => {\n const verb = spec.id.replace(/\\./g, ' ')\n const args = Object.entries(spec.args)\n .map(([n, a]) => argHelp(n, a))\n .join(' ')\n return args.length > 0 ? `${verb} ${args}` : verb\n}\n\n/**\n * Generate the model-facing grammar text for the configured deployment: only available verbs,\n * with arg signatures, plus few-shot examples rendered from real plans via `toPipe` so they\n * can never drift from the parser.\n */\nconst grammarText = (pipeline: MediaPipeline): string => {\n const available = new Set(availableVerbs(pipeline.capabilities))\n const lines: string[] = []\n for (const spec of VERB_INDEX.values()) {\n if (!available.has(spec.id.replace(/[._]+/g, ' '))) continue\n lines.push(`- ${verbHelp(spec)} — ${spec.description}`)\n }\n const examples: string[] = [\n toPipe({\n steps: [\n { verb: 'select', args: { pages: [2, 3, 4, 5] } },\n { verb: 'extract.text', args: {} },\n ],\n }),\n toPipe({\n steps: [\n { verb: 'extract.text', args: {} },\n { verb: 'chunk', args: { by: 'sentence', size: 512 } },\n ],\n }),\n ]\n if (available.has('redact')) {\n examples.push(\n toPipe({\n steps: [\n {\n verb: 'redact',\n args: { match: [{ source: '\\\\d{3}-\\\\d{2}-\\\\d{4}', flags: '' }], replace: '[SSN]' },\n },\n ],\n })\n )\n }\n const creatable = creatableFormats(pipeline.capabilities)\n const creating: string[] =\n creatable.length > 0\n ? [\n '',\n 'Creating new media:',\n `Pass media_id \"empty:<format>\" to create a NEW blank file instead of processing an existing one (available: ${creatable.join(', ')}).`,\n `Example: media_id \"empty:xlsx\" with q \"${toPipe({\n steps: [\n {\n verb: 'sheet.update_cells',\n args: { updates: [{ address: 'A1', value: 'Title' }] },\n },\n ],\n })}\"`,\n 'Blank images are 1024×1024 white — resize in the same statement (empty:png + \"image resize width=64\").',\n 'The same sentinel works as a ref: merge with=@empty:xlsx.',\n ]\n : []\n return [\n 'Statements are pipe expressions: verb name=value ... | verb name=value ...',\n 'Named args only. Indices are 1-based. Quote values containing spaces or dashes.',\n 'Structured payloads are quoted JSON: updates=\\'[{\"address\":\"B2\",\"value\":3}]\\'.',\n 'Reference other media inline by id: merge with=@<media id> (ids come from list_media).',\n '',\n 'Available verbs:',\n ...lines,\n ...creating,\n '',\n 'Examples (remember: inside JSON tool args, backslashes must be doubled — \\\\\\\\d):',\n ...examples.map((e) => ` ${e}`),\n ].join('\\n')\n}\n\n// ── the tools ────────────────────────────────────────────────────────────────\n\nconst buildListMediaTool = (overrides: ForgeMediaToolsOptions['overrides']): Tool => {\n const name = overrides?.list_media?.name ?? 'list_media'\n return new Tool({\n name,\n description:\n overrides?.list_media?.description ??\n 'Lists every media file visible in this conversation turn (user attachments and files produced by prior tool calls), with the media ids other media tools require.',\n inputSchema: validator.object({}),\n artifactConstructor: () => SpooledJsonArtifact,\n handler: async (_args, ctx) => {\n const media = enumerateMedia(ctx as DispatchContext)\n return JSON.stringify(media, null, 2)\n },\n })\n}\n\nconst buildCompositeTool = (\n pipeline: MediaPipeline,\n resolveMedia: MediaResolverFn,\n gate: ToolGateFn | undefined,\n overrides: ForgeMediaToolsOptions['overrides']\n): Tool => {\n const name = overrides?.media_query?.name ?? 'media_query'\n const description =\n overrides?.media_query?.description ??\n `Runs a media-processing statement against a file from this conversation, locally (no external services). Provide media_id (from list_media) and either q (a pipe statement) or ops (structured steps).\n\n${grammarText(pipeline)}`\n const creatable = creatableFormats(pipeline.capabilities)\n const mediaIdDescription =\n creatable.length > 0\n ? `The id of the media to process (call list_media to discover ids), or \"empty:<format>\" to create a new blank file (available: ${creatable.join(', ')}).`\n : 'The id of the media to process (call list_media to discover ids).'\n return new Tool({\n name,\n description,\n inputSchema: validator.object({\n media_id: validator.string().required().description(mediaIdDescription),\n q: validator\n .string()\n .description('A pipe statement, e.g. \"select pages=2-5 | extract text\".'),\n ops: validator\n .array()\n .items(\n validator.object({\n verb: validator.string().required(),\n args: validator.object().unknown(true).required(),\n })\n )\n .description('Structured alternative to q: [{ verb, args }] steps.'),\n }),\n handler: async (args, ctx) => {\n const {\n media_id: mediaId,\n q,\n ops,\n } = args as { media_id: string; q?: string; ops?: MediaOp[] }\n if ((q === undefined) === (ops === undefined)) {\n return failure(\n 'BAD_REQUEST',\n 'provide exactly one of q (pipe statement) or ops (structured steps)'\n )\n }\n const dispatch = ctx as DispatchContext\n await runGate(gate, dispatch, name, args)\n const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId)\n if (!acquired.ok) return acquired.failure\n try {\n const runOptions = {\n signal: dispatch.abortSignal,\n resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia),\n }\n const result =\n q !== undefined\n ? await pipeline.query(acquired.payload, q, runOptions)\n : await pipeline.ops(acquired.payload, ops as MediaOp[], runOptions)\n return await renderResult(dispatch, result, name)\n } catch (err) {\n return renderError(err)\n }\n },\n })\n}\n\n/** Convert a granular tool's flat args into the verb's op args (drops media_id). */\nconst opArgsFrom = (args: Record<string, unknown>): Record<string, MediaArgValue> => {\n const out: Record<string, MediaArgValue> = {}\n for (const [k, v] of Object.entries(args)) {\n if (k === 'media_id' || v === undefined) continue\n out[k] = v as MediaArgValue\n }\n return out\n}\n\nconst granularSchemaFor = (\n spec: VerbSpec,\n creatable: readonly string[]\n): ReturnType<typeof validator.object> => {\n const mediaIdDescription =\n creatable.length > 0\n ? `The id of the media to process (call list_media to discover ids), or \"empty:<format>\" to create a new blank file (available: ${creatable.join(', ')}).`\n : 'The id of the media to process (call list_media to discover ids).'\n const shape: Record<string, ReturnType<typeof validator.any>> = {\n media_id: validator.string().required().description(mediaIdDescription),\n }\n for (const [argName, arg] of Object.entries(spec.args)) {\n let schema\n switch (arg.type) {\n case 'number':\n schema = validator.number()\n if (arg.min !== undefined) schema = schema.min(arg.min)\n if (arg.max !== undefined) schema = schema.max(arg.max)\n break\n case 'boolean':\n schema = validator.boolean()\n break\n case 'enum':\n schema = validator.string().valid(...(arg.values ?? []))\n break\n case 'number-list':\n schema = validator.array().items(validator.number().min(arg.min ?? 1))\n break\n case 'string-list':\n schema = arg.values\n ? validator.array().items(validator.string().valid(...arg.values))\n : validator.array().items(validator.string())\n break\n case 'media-ref':\n schema = validator\n .string()\n .description('A media id (from list_media) for the other media in this operation.')\n break\n case 'media-ref-list':\n schema = validator.array().items(validator.string())\n break\n case 'name-or-index':\n schema = validator\n .alternatives()\n .try(validator.number().integer().min(1), validator.string())\n break\n case 'regex-or-string-list':\n schema = validator\n .alternatives()\n .try(validator.string(), validator.array().items(validator.string()))\n break\n case 'json':\n schema = validator.any().optional()\n break\n default:\n schema = validator.string()\n }\n schema = schema.description(arg.description)\n if (arg.required) schema = schema.required()\n shape[argName] = schema\n }\n return validator.object(shape)\n}\n\n/** Normalize a granular tool's media-ref string args to MediaRef IR values. */\nconst normalizeRefArgs = (spec: VerbSpec, args: Record<string, MediaArgValue>): void => {\n for (const [argName, arg] of Object.entries(spec.args)) {\n const value = args[argName]\n if (value === undefined) continue\n if (arg.type === 'media-ref' && typeof value === 'string') {\n args[argName] = { kind: 'id', id: value }\n }\n if (arg.type === 'media-ref-list' && Array.isArray(value)) {\n args[argName] = (value as unknown[]).map((v) =>\n typeof v === 'string' ? { kind: 'id', id: v } : (v as MediaArgValue)\n ) as MediaArgValue\n }\n }\n}\n\nconst buildGranularTool = (\n pipeline: MediaPipeline,\n spec: VerbSpec,\n resolveMedia: MediaResolverFn,\n gate: ToolGateFn | undefined,\n overrides: ForgeMediaToolsOptions['overrides']\n): Tool => {\n const defaultName = spec.id.replace(/\\./g, '_')\n const name = overrides?.[defaultName]?.name ?? defaultName\n return new Tool({\n name,\n description:\n overrides?.[defaultName]?.description ??\n `${spec.description} Processes a media file from this conversation locally. Indices are 1-based.`,\n inputSchema: granularSchemaFor(spec, creatableFormats(pipeline.capabilities)),\n handler: async (args, ctx) => {\n const dispatch = ctx as DispatchContext\n const { media_id: mediaId } = args as { media_id: string }\n await runGate(gate, dispatch, name, args)\n const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId)\n if (!acquired.ok) return acquired.failure\n const opArgs = opArgsFrom(args as Record<string, unknown>)\n normalizeRefArgs(spec, opArgs)\n try {\n const result = await pipeline.ops(acquired.payload, [{ verb: spec.id, args: opArgs }], {\n signal: dispatch.abortSignal,\n resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia),\n })\n return await renderResult(dispatch, result, name)\n } catch (err) {\n return renderError(err)\n }\n },\n })\n}\n\n/**\n * Forge agent tools over a configured media pipeline.\n *\n * @remarks\n * The minted set follows the pipeline's configured engines: in the composite surface the\n * `media_query` grammar text only advertises available verbs; in the granular surface a tool\n * is minted only for verbs whose engine (if any) is configured. Either way `list_media` is\n * included — it is the model's entry point for discovering `media_id` values.\n *\n * The returned record is keyed by tool name so consumers can register selectively or pass\n * `Object.values(tools)` to `TurnRunnerConfig.tools`.\n *\n * @param pipeline - A pipeline from `createMediaPipeline`.\n * @param options - Surface, resolver, gate, and overrides.\n * @returns The minted tools, keyed by name.\n */\nexport const forgeMediaTools = (\n pipeline: MediaPipeline,\n options: ForgeMediaToolsOptions\n): Record<string, Tool> => {\n if (options?.surface !== 'composite' && options?.surface !== 'granular') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `forgeMediaTools requires surface: 'composite' | 'granular'`,\n ])\n }\n const resolveMedia = options.resolveMedia ?? defaultResolveMedia\n const tools: Record<string, Tool> = {}\n const listTool = buildListMediaTool(options.overrides)\n tools[listTool.name] = listTool\n\n if (options.surface === 'composite') {\n const composite = buildCompositeTool(pipeline, resolveMedia, options.gate, options.overrides)\n tools[composite.name] = composite\n return tools\n }\n\n const available = new Set(availableVerbs(pipeline.capabilities))\n for (const spec of VERB_INDEX.values()) {\n if (!available.has(spec.id.replace(/[._]+/g, ' '))) continue\n const tool = buildGranularTool(pipeline, spec, resolveMedia, options.gate, options.overrides)\n tools[tool.name] = tool\n }\n return tools\n}\n\n/** Re-exported so consumers can fold verbs the same way the forge does. */\nexport { foldVerb }\n"],"mappings":"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"}
@@ -28,11 +28,18 @@ export declare const MIME: {
28
28
  readonly PAGES: "application/x-iwork-pages-sffpages";
29
29
  readonly NUMBERS: "application/x-iwork-numbers-sffnumbers";
30
30
  readonly KEYNOTE: "application/x-iwork-keynote-sffkey";
31
+ readonly XLSM: "application/vnd.ms-excel.sheet.macroEnabled.12";
32
+ readonly XLSB: "application/vnd.ms-excel.sheet.binary.macroEnabled.12";
33
+ readonly FODS: "application/vnd.oasis.opendocument.spreadsheet-flat-xml";
34
+ readonly SYLK: "application/x-sylk";
35
+ readonly DIF: "application/x-dif";
36
+ readonly DBF: "application/x-dbf";
31
37
  readonly TXT: "text/plain";
32
38
  readonly MD: "text/markdown";
33
39
  readonly CSV: "text/csv";
34
40
  readonly HTML: "text/html";
35
41
  readonly JSON: "application/json";
42
+ readonly YAML: "application/yaml";
36
43
  readonly PNG: "image/png";
37
44
  readonly JPEG: "image/jpeg";
38
45
  readonly WEBP: "image/webp";
@@ -45,6 +52,8 @@ export declare const MIME: {
45
52
  export declare const LEGACY_OFFICE_MIMES: ReadonlySet<string>;
46
53
  /** Apple iWork MIME types (extraction only — mutation is rejected). */
47
54
  export declare const IWORK_MIMES: ReadonlySet<string>;
55
+ /** Every MIME the battery classifies as a spreadsheet (drives verb applicability + normalize). */
56
+ export declare const SPREADSHEET_MIMES: ReadonlySet<string>;
48
57
  /**
49
58
  * Classify a MIME type into the verb table's broad format family.
50
59
  *
@@ -41,9 +41,8 @@
41
41
  */
42
42
  import { MediaChain } from "./builder";
43
43
  import type { MediaOp, MediaPlan } from "./plan";
44
- import type { CapabilityProbe } from "./validate";
45
- import type { EngineSelectionMiddlewareFn } from "./registry";
46
44
  import type { MediaEngine, EngineResolver } from "./contracts";
45
+ import type { EngineRegistry, EngineSelectionMiddlewareFn } from "./registry";
47
46
  import type { StepPayload, PlanResult, MediaStepMiddlewareFn } from "./runtime";
48
47
  export { MediaChain } from "./builder";
49
48
  export type { ChainExecutor, ChainInput, MediaChainRef, CellUpdate, ResizeOptions, SheetNamespace, SlidesNamespace, ImageNamespace, AudioNamespace, } from "./builder";
@@ -55,8 +54,8 @@ export { validateSegments, validateOps, availableVerbs } from "./validate";
55
54
  export type { ValidateOptions, CapabilityProbe } from "./validate";
56
55
  export { VERBS, VERB_INDEX, FOLDED_VERBS, foldVerb, suggestVerbs } from "./verbs";
57
56
  export type { VerbSpec, VerbArgSpec, VerbArgType, VerbOutput, VerbRequirement, FormatFamily, } from "./verbs";
58
- export { PCM_MIME, pcmToBytes, bytesToPcm, implementsMediaEngine } from "./contracts";
59
- export type { MediaEngine, EngineResolver, ConvertCapability, ConvertRequest, ConvertResult, ConvertOutput, ConvertOptions, OcrConvertOptions, AsrConvertOptions, ImagesConvertOptions, MutateCapability, MutateRequest, MimePattern, } from "./contracts";
57
+ export { PCM_MIME, EMPTY_MIME, pcmToBytes, bytesToPcm, implementsMediaEngine } from "./contracts";
58
+ export type { MediaEngine, EngineResolver, ConvertCapability, ConvertRequest, ConvertResult, ConvertOutput, ConvertOptions, OcrConvertOptions, AsrConvertOptions, ImagesConvertOptions, MutateCapability, MutateRequest, EditCapability, EditRequest, EditResult, EditSummary, MimePattern, } from "./contracts";
60
59
  export { buildEngineRegistry } from "./registry";
61
60
  export type { EngineRegistry, EngineSelectionContext, EngineSelectionMiddlewareFn, } from "./registry";
62
61
  export type { StepPayload, StepResult, PlanResult, StepContext, StepImpl, MediaStepMiddlewareFn, } from "./runtime";
@@ -114,8 +113,11 @@ export interface MediaPipeline {
114
113
  ops(input: StepPayload, ops: MediaOp[], options?: RunOptions): Promise<PlanResult>;
115
114
  /** Validate a pipe string or ops array to a plan WITHOUT executing (dry-run/compile). */
116
115
  compile(statement: string | MediaOp[]): MediaPlan;
117
- /** The deployment's capabilities (drives verb narrowing). */
118
- readonly capabilities: CapabilityProbe;
116
+ /**
117
+ * The deployment's engine registry (drives verb narrowing AND generation/edit dispatch —
118
+ * the runtime value has always been the registry; the declared type now says so).
119
+ */
120
+ readonly capabilities: EngineRegistry;
119
121
  /** The resolved engines, in supply order — inspect ids and declared capabilities. */
120
122
  readonly engines: readonly MediaEngine[];
121
123
  }