@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,4 +1,4 @@
1
- import { implementsBinaryExecutor, implementsScratchWorkspace } from "../contracts.mjs";
1
+ import { EMPTY_MIME, implementsBinaryExecutor, implementsScratchWorkspace } from "../contracts.mjs";
2
2
  import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4Ofs.mjs";
3
3
  //#region src/batteries/media/engines/soffice.ts
4
4
  /**
@@ -148,6 +148,34 @@ var CONVERT_TOKEN = {
148
148
  md: "txt:Text",
149
149
  json: "csv"
150
150
  };
151
+ /**
152
+ * Seed extension per generation target: which silo's empty file to materialize when the
153
+ * source is {@link EMPTY_MIME}. LibreOffice treats a zero-byte input as an empty document of
154
+ * the extension's type (verified across all three silos, both directions, on 25.x — pinned by
155
+ * a binary-gated spec), so generation needs no templates at all. The seed is always a
156
+ * DIFFERENT extension than the target within the same silo, because soffice won't "convert"
157
+ * docx to docx.
158
+ */
159
+ var SEED_EXT_FOR_TARGET = {
160
+ docx: "odt",
161
+ odt: "docx",
162
+ doc: "docx",
163
+ rtf: "docx",
164
+ pdf: "docx",
165
+ html: "docx",
166
+ txt: "docx",
167
+ md: "docx",
168
+ xlsx: "ods",
169
+ ods: "xlsx",
170
+ xls: "xlsx",
171
+ csv: "xlsx",
172
+ json: "xlsx",
173
+ pptx: "odp",
174
+ odp: "pptx",
175
+ ppt: "pptx"
176
+ };
177
+ /** Every format this engine can generate from {@link EMPTY_MIME}. */
178
+ var GENERATION_TARGETS = Object.keys(SEED_EXT_FOR_TARGET);
151
179
  var validateOptions = (options, name) => {
152
180
  if (typeof options?.path !== "string" || options.path.length === 0) throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`]);
153
181
  if (!implementsBinaryExecutor(options.executor)) throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires an executor implementing the BinaryExecutor contract`]);
@@ -198,9 +226,10 @@ var MIMES_OF = (exts) => Object.entries(EXT_BY_MIME).filter(([, ext]) => exts.in
198
226
  var sofficeEngine = (options) => {
199
227
  validateOptions(options, "sofficeEngine");
200
228
  const convert = async (request) => {
201
- const inputExt = EXT_BY_MIME[request.mimeType.toLowerCase().split(";")[0].trim()];
229
+ const mime = request.mimeType.toLowerCase().split(";")[0].trim();
230
+ const inputExt = mime === "application/x-adk-empty" ? SEED_EXT_FOR_TARGET[request.to] : EXT_BY_MIME[mime];
202
231
  if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`);
203
- const targets = TARGETS_BY_EXT[inputExt] ?? [];
232
+ const targets = mime === "application/x-adk-empty" ? GENERATION_TARGETS : TARGETS_BY_EXT[inputExt] ?? [];
204
233
  if (!targets.includes(request.to)) throw new Error(`cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(", ")}`);
205
234
  const convertTo = CONVERT_TOKEN[request.to] ?? request.to;
206
235
  const outputExt = request.to === "md" ? "txt" : request.to === "json" ? "csv" : request.to;
@@ -234,7 +263,12 @@ var sofficeEngine = (options) => {
234
263
  group(["pptx"], TARGETS_BY_EXT.pptx),
235
264
  group(["odp"], TARGETS_BY_EXT.odp),
236
265
  group(["ppt"], TARGETS_BY_EXT.ppt),
237
- group(["pdf"], TARGETS_BY_EXT.pdf)
266
+ group(["pdf"], TARGETS_BY_EXT.pdf),
267
+ {
268
+ from: [EMPTY_MIME],
269
+ to: GENERATION_TARGETS,
270
+ convert
271
+ }
238
272
  ]
239
273
  };
240
274
  };
@@ -1 +1 @@
1
- {"version":3,"file":"soffice.mjs","names":[],"sources":["../../../../src/batteries/media/engines/soffice.ts"],"sourcesContent":["/**\n * The LibreOffice-backed {@link @nhtio/adk/batteries/media/contracts!MediaEngine}: document,\n * spreadsheet, and presentation conversion via the `soffice` binary.\n *\n * @module @nhtio/adk/batteries/media/engines/soffice\n *\n * @remarks\n * Document conversion is the one capability in the media battery with no mature\n * cross-environment equivalent, so this engine is binary-backed by design. It composes the\n * two BYO runtime contracts: a {@link @nhtio/adk/batteries/media/contracts!BinaryExecutor}\n * runs the invocation (bundled: `execa_executor`) and a\n * {@link @nhtio/adk/batteries/media/contracts!ScratchWorkspace} exchanges bytes with it\n * (bundled: `fs_workspace`). The executor and workspace must agree on path visibility —\n * that pairing is the consumer's composition decision.\n *\n * One engine, one capability kind: the conversion matrix is declared as convert capability\n * groups (each format silo converts within itself plus to PDF/HTML; PDF converts to\n * html/txt/docx/odt). The spreadsheet group covers ODS/legacy-xls to xlsx — what used to be\n * a separate \"normalize\" engine is just an edge in the matrix.\n */\n\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport { implementsBinaryExecutor, implementsScratchWorkspace } from '../contracts'\nimport type {\n MediaEngine,\n ConvertCapability,\n ConvertRequest,\n ConvertResult,\n BinaryExecutor,\n ScratchWorkspaceFactory,\n} from '../contracts'\n\n/** Options for {@link sofficeEngine}. */\nexport interface SofficeEngineOptions {\n /** Path (or resolvable name) of the soffice binary. */\n path: string\n /** Runs the soffice invocation. Required — no platform default. */\n executor: BinaryExecutor\n /** Mints a scratch dir per invocation whose paths the executor can open. Required. */\n workspace: ScratchWorkspaceFactory\n /** Per-invocation timeout. Default 120000. */\n timeoutMs?: number\n}\n\nconst MIME_BY_TARGET: Record<string, string> = {\n pdf: 'application/pdf',\n html: 'text/html',\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n doc: 'application/msword',\n rtf: 'application/rtf',\n odt: 'application/vnd.oasis.opendocument.text',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n xls: 'application/vnd.ms-excel',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n ppt: 'application/vnd.ms-powerpoint',\n odp: 'application/vnd.oasis.opendocument.presentation',\n}\n\nconst EXT_BY_MIME: Record<string, string> = {\n 'application/pdf': 'pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',\n 'application/msword': 'doc',\n 'application/rtf': 'rtf',\n 'text/rtf': 'rtf',\n 'application/vnd.oasis.opendocument.text': 'odt',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',\n 'application/vnd.ms-excel': 'xls',\n 'application/vnd.oasis.opendocument.spreadsheet': 'ods',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',\n 'application/vnd.ms-powerpoint': 'ppt',\n 'application/vnd.oasis.opendocument.presentation': 'odp',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/csv': 'csv',\n}\n\n/** Server-parity conversion matrix: same-silo targets plus pdf/html. */\nconst TARGETS_BY_EXT: Record<string, readonly string[]> = {\n docx: ['pdf', 'html', 'txt', 'md', 'odt', 'doc', 'rtf'],\n odt: ['pdf', 'html', 'txt', 'md', 'docx', 'doc', 'rtf'],\n doc: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'rtf'],\n rtf: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'doc'],\n xlsx: ['pdf', 'html', 'csv', 'json', 'ods', 'xls'],\n ods: ['pdf', 'html', 'csv', 'json', 'xlsx', 'xls'],\n xls: ['pdf', 'html', 'csv', 'json', 'xlsx', 'ods'],\n pptx: ['pdf', 'html', 'odp', 'ppt'],\n odp: ['pdf', 'html', 'pptx', 'ppt'],\n ppt: ['pdf', 'html', 'pptx', 'odp'],\n pdf: ['html', 'txt', 'docx', 'odt'],\n}\n\n/** soffice convert-to filter token per target (md goes via txt; json via csv). */\nconst CONVERT_TOKEN: Record<string, string> = {\n md: 'txt:Text',\n json: 'csv',\n}\n\nconst validateOptions = (options: SofficeEngineOptions, name: string): void => {\n if (typeof options?.path !== 'string' || options.path.length === 0) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`])\n }\n if (!implementsBinaryExecutor(options.executor)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires an executor implementing the BinaryExecutor contract`,\n ])\n }\n if (typeof options.workspace !== 'function') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires a workspace factory (e.g. fsScratchWorkspace({ root }))`,\n ])\n }\n}\n\nconst runSoffice = async (\n options: SofficeEngineOptions,\n bytes: Uint8Array,\n inputExt: string,\n convertTo: string,\n outputExt: string,\n signal?: AbortSignal\n): Promise<Uint8Array> => {\n const workspace = await options.workspace()\n if (!implementsScratchWorkspace(workspace)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n 'the workspace factory minted a value that does not implement ScratchWorkspace',\n ])\n }\n try {\n const inputPath = await workspace.materialize(bytes, `input.${inputExt}`)\n const result = await options.executor.exec({\n cmd: options.path,\n args: [\n '--headless',\n '--nologo',\n '--nodefault',\n '--norestore',\n '--nolockcheck',\n '--convert-to',\n convertTo,\n '--outdir',\n workspace.dir(),\n inputPath,\n ],\n timeoutMs: options.timeoutMs ?? 120_000,\n signal,\n })\n if (result.failed) {\n const detail = result.stderr || result.stdout || `exit code ${result.exitCode}`\n throw new Error(`LibreOffice conversion failed: ${detail}`)\n }\n const files = await workspace.list()\n const produced = files.find(\n (f) => f.toLowerCase().endsWith(`.${outputExt}`) && f !== `input.${inputExt}`\n )\n if (!produced) {\n throw new Error(\n `LibreOffice reported success but produced no .${outputExt} output (files: ${files.join(', ')})`\n )\n }\n return workspace.read(`${workspace.dir()}/${produced}`)\n } finally {\n await workspace.dispose()\n }\n}\n\n/** The MIME types of one extension group, for capability `from` declarations. */\nconst MIMES_OF = (exts: readonly string[]): string[] =>\n Object.entries(EXT_BY_MIME)\n .filter(([, ext]) => exts.includes(ext))\n .map(([mime]) => mime)\n\n/**\n * Construct the LibreOffice engine.\n *\n * @param options - Binary path, executor, workspace factory, timeout.\n * @returns The engine, declaring one convert capability group per format silo.\n */\nexport const sofficeEngine = (options: SofficeEngineOptions): MediaEngine => {\n validateOptions(options, 'sofficeEngine')\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const mime = request.mimeType.toLowerCase().split(';')[0].trim()\n const inputExt = EXT_BY_MIME[mime]\n if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`)\n const targets = TARGETS_BY_EXT[inputExt] ?? []\n if (!targets.includes(request.to)) {\n throw new Error(\n `cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(', ')}`\n )\n }\n const convertTo = CONVERT_TOKEN[request.to] ?? request.to\n const outputExt = request.to === 'md' ? 'txt' : request.to === 'json' ? 'csv' : request.to\n let bytes = await runSoffice(\n options,\n request.bytes,\n inputExt,\n convertTo,\n outputExt,\n request.signal\n )\n let mimeType = MIME_BY_TARGET[request.to] ?? 'application/octet-stream'\n if (request.to === 'json') {\n // soffice emits CSV; lower to a JSON array-of-arrays for predictable structure.\n const text = new TextDecoder().decode(bytes)\n const rows = text\n .split(/\\r?\\n/)\n .filter((line) => line.length > 0)\n .map((line) => line.split(','))\n bytes = new TextEncoder().encode(JSON.stringify(rows))\n mimeType = MIME_BY_TARGET.json\n }\n return { outputs: [{ bytes, mimeType }] }\n }\n\n // One capability group per uniform from×to block of the matrix.\n const group = (exts: readonly string[], to: readonly string[]): ConvertCapability => ({\n from: MIMES_OF(exts),\n to,\n convert,\n })\n\n return {\n id: 'soffice',\n converts: [\n group(['docx'], TARGETS_BY_EXT.docx),\n group(['odt'], TARGETS_BY_EXT.odt),\n group(['doc'], TARGETS_BY_EXT.doc),\n group(['rtf'], TARGETS_BY_EXT.rtf),\n group(['xlsx'], TARGETS_BY_EXT.xlsx),\n group(['ods'], TARGETS_BY_EXT.ods),\n group(['xls'], TARGETS_BY_EXT.xls),\n group(['pptx'], TARGETS_BY_EXT.pptx),\n group(['odp'], TARGETS_BY_EXT.odp),\n group(['ppt'], TARGETS_BY_EXT.ppt),\n group(['pdf'], TARGETS_BY_EXT.pdf),\n ],\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"soffice.mjs","names":[],"sources":["../../../../src/batteries/media/engines/soffice.ts"],"sourcesContent":["/**\n * The LibreOffice-backed {@link @nhtio/adk/batteries/media/contracts!MediaEngine}: document,\n * spreadsheet, and presentation conversion via the `soffice` binary.\n *\n * @module @nhtio/adk/batteries/media/engines/soffice\n *\n * @remarks\n * Document conversion is the one capability in the media battery with no mature\n * cross-environment equivalent, so this engine is binary-backed by design. It composes the\n * two BYO runtime contracts: a {@link @nhtio/adk/batteries/media/contracts!BinaryExecutor}\n * runs the invocation (bundled: `execa_executor`) and a\n * {@link @nhtio/adk/batteries/media/contracts!ScratchWorkspace} exchanges bytes with it\n * (bundled: `fs_workspace`). The executor and workspace must agree on path visibility —\n * that pairing is the consumer's composition decision.\n *\n * One engine, one capability kind: the conversion matrix is declared as convert capability\n * groups (each format silo converts within itself plus to PDF/HTML; PDF converts to\n * html/txt/docx/odt). The spreadsheet group covers ODS/legacy-xls to xlsx — what used to be\n * a separate \"normalize\" engine is just an edge in the matrix.\n */\n\nimport { E_INVALID_MEDIA_PIPELINE_CONFIG } from '../exceptions'\nimport { EMPTY_MIME, implementsBinaryExecutor, implementsScratchWorkspace } from '../contracts'\nimport type {\n MediaEngine,\n ConvertCapability,\n ConvertRequest,\n ConvertResult,\n BinaryExecutor,\n ScratchWorkspaceFactory,\n} from '../contracts'\n\n/** Options for {@link sofficeEngine}. */\nexport interface SofficeEngineOptions {\n /** Path (or resolvable name) of the soffice binary. */\n path: string\n /** Runs the soffice invocation. Required — no platform default. */\n executor: BinaryExecutor\n /** Mints a scratch dir per invocation whose paths the executor can open. Required. */\n workspace: ScratchWorkspaceFactory\n /** Per-invocation timeout. Default 120000. */\n timeoutMs?: number\n}\n\nconst MIME_BY_TARGET: Record<string, string> = {\n pdf: 'application/pdf',\n html: 'text/html',\n txt: 'text/plain',\n md: 'text/markdown',\n csv: 'text/csv',\n json: 'application/json',\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n doc: 'application/msword',\n rtf: 'application/rtf',\n odt: 'application/vnd.oasis.opendocument.text',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n xls: 'application/vnd.ms-excel',\n ods: 'application/vnd.oasis.opendocument.spreadsheet',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n ppt: 'application/vnd.ms-powerpoint',\n odp: 'application/vnd.oasis.opendocument.presentation',\n}\n\nconst EXT_BY_MIME: Record<string, string> = {\n 'application/pdf': 'pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',\n 'application/msword': 'doc',\n 'application/rtf': 'rtf',\n 'text/rtf': 'rtf',\n 'application/vnd.oasis.opendocument.text': 'odt',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',\n 'application/vnd.ms-excel': 'xls',\n 'application/vnd.oasis.opendocument.spreadsheet': 'ods',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',\n 'application/vnd.ms-powerpoint': 'ppt',\n 'application/vnd.oasis.opendocument.presentation': 'odp',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/csv': 'csv',\n}\n\n/** Server-parity conversion matrix: same-silo targets plus pdf/html. */\nconst TARGETS_BY_EXT: Record<string, readonly string[]> = {\n docx: ['pdf', 'html', 'txt', 'md', 'odt', 'doc', 'rtf'],\n odt: ['pdf', 'html', 'txt', 'md', 'docx', 'doc', 'rtf'],\n doc: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'rtf'],\n rtf: ['pdf', 'html', 'txt', 'md', 'docx', 'odt', 'doc'],\n xlsx: ['pdf', 'html', 'csv', 'json', 'ods', 'xls'],\n ods: ['pdf', 'html', 'csv', 'json', 'xlsx', 'xls'],\n xls: ['pdf', 'html', 'csv', 'json', 'xlsx', 'ods'],\n pptx: ['pdf', 'html', 'odp', 'ppt'],\n odp: ['pdf', 'html', 'pptx', 'ppt'],\n ppt: ['pdf', 'html', 'pptx', 'odp'],\n pdf: ['html', 'txt', 'docx', 'odt'],\n}\n\n/** soffice convert-to filter token per target (md goes via txt; json via csv). */\nconst CONVERT_TOKEN: Record<string, string> = {\n md: 'txt:Text',\n json: 'csv',\n}\n\n/**\n * Seed extension per generation target: which silo's empty file to materialize when the\n * source is {@link EMPTY_MIME}. LibreOffice treats a zero-byte input as an empty document of\n * the extension's type (verified across all three silos, both directions, on 25.x — pinned by\n * a binary-gated spec), so generation needs no templates at all. The seed is always a\n * DIFFERENT extension than the target within the same silo, because soffice won't \"convert\"\n * docx to docx.\n */\nconst SEED_EXT_FOR_TARGET: Record<string, string> = {\n docx: 'odt',\n odt: 'docx',\n doc: 'docx',\n rtf: 'docx',\n pdf: 'docx',\n html: 'docx',\n txt: 'docx',\n md: 'docx',\n xlsx: 'ods',\n ods: 'xlsx',\n xls: 'xlsx',\n csv: 'xlsx',\n json: 'xlsx',\n pptx: 'odp',\n odp: 'pptx',\n ppt: 'pptx',\n}\n\n/** Every format this engine can generate from {@link EMPTY_MIME}. */\nconst GENERATION_TARGETS: readonly string[] = Object.keys(SEED_EXT_FOR_TARGET)\n\nconst validateOptions = (options: SofficeEngineOptions, name: string): void => {\n if (typeof options?.path !== 'string' || options.path.length === 0) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([`${name} requires the soffice binary path`])\n }\n if (!implementsBinaryExecutor(options.executor)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires an executor implementing the BinaryExecutor contract`,\n ])\n }\n if (typeof options.workspace !== 'function') {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n `${name} requires a workspace factory (e.g. fsScratchWorkspace({ root }))`,\n ])\n }\n}\n\nconst runSoffice = async (\n options: SofficeEngineOptions,\n bytes: Uint8Array,\n inputExt: string,\n convertTo: string,\n outputExt: string,\n signal?: AbortSignal\n): Promise<Uint8Array> => {\n const workspace = await options.workspace()\n if (!implementsScratchWorkspace(workspace)) {\n throw new E_INVALID_MEDIA_PIPELINE_CONFIG([\n 'the workspace factory minted a value that does not implement ScratchWorkspace',\n ])\n }\n try {\n const inputPath = await workspace.materialize(bytes, `input.${inputExt}`)\n const result = await options.executor.exec({\n cmd: options.path,\n args: [\n '--headless',\n '--nologo',\n '--nodefault',\n '--norestore',\n '--nolockcheck',\n '--convert-to',\n convertTo,\n '--outdir',\n workspace.dir(),\n inputPath,\n ],\n timeoutMs: options.timeoutMs ?? 120_000,\n signal,\n })\n if (result.failed) {\n const detail = result.stderr || result.stdout || `exit code ${result.exitCode}`\n throw new Error(`LibreOffice conversion failed: ${detail}`)\n }\n const files = await workspace.list()\n const produced = files.find(\n (f) => f.toLowerCase().endsWith(`.${outputExt}`) && f !== `input.${inputExt}`\n )\n if (!produced) {\n throw new Error(\n `LibreOffice reported success but produced no .${outputExt} output (files: ${files.join(', ')})`\n )\n }\n return workspace.read(`${workspace.dir()}/${produced}`)\n } finally {\n await workspace.dispose()\n }\n}\n\n/** The MIME types of one extension group, for capability `from` declarations. */\nconst MIMES_OF = (exts: readonly string[]): string[] =>\n Object.entries(EXT_BY_MIME)\n .filter(([, ext]) => exts.includes(ext))\n .map(([mime]) => mime)\n\n/**\n * Construct the LibreOffice engine.\n *\n * @param options - Binary path, executor, workspace factory, timeout.\n * @returns The engine, declaring one convert capability group per format silo.\n */\nexport const sofficeEngine = (options: SofficeEngineOptions): MediaEngine => {\n validateOptions(options, 'sofficeEngine')\n\n const convert = async (request: ConvertRequest): Promise<ConvertResult> => {\n const mime = request.mimeType.toLowerCase().split(';')[0].trim()\n // Generation: a zero-byte seed file in the target's silo converts as an empty document.\n const inputExt = mime === EMPTY_MIME ? SEED_EXT_FOR_TARGET[request.to] : EXT_BY_MIME[mime]\n if (!inputExt) throw new Error(`unsupported input MIME for conversion: ${request.mimeType}`)\n const targets = mime === EMPTY_MIME ? GENERATION_TARGETS : (TARGETS_BY_EXT[inputExt] ?? [])\n if (!targets.includes(request.to)) {\n throw new Error(\n `cannot convert ${inputExt} to ${request.to}; supported: ${targets.join(', ')}`\n )\n }\n const convertTo = CONVERT_TOKEN[request.to] ?? request.to\n const outputExt = request.to === 'md' ? 'txt' : request.to === 'json' ? 'csv' : request.to\n let bytes = await runSoffice(\n options,\n request.bytes,\n inputExt,\n convertTo,\n outputExt,\n request.signal\n )\n let mimeType = MIME_BY_TARGET[request.to] ?? 'application/octet-stream'\n if (request.to === 'json') {\n // soffice emits CSV; lower to a JSON array-of-arrays for predictable structure.\n const text = new TextDecoder().decode(bytes)\n const rows = text\n .split(/\\r?\\n/)\n .filter((line) => line.length > 0)\n .map((line) => line.split(','))\n bytes = new TextEncoder().encode(JSON.stringify(rows))\n mimeType = MIME_BY_TARGET.json\n }\n return { outputs: [{ bytes, mimeType }] }\n }\n\n // One capability group per uniform from×to block of the matrix.\n const group = (exts: readonly string[], to: readonly string[]): ConvertCapability => ({\n from: MIMES_OF(exts),\n to,\n convert,\n })\n\n return {\n id: 'soffice',\n converts: [\n group(['docx'], TARGETS_BY_EXT.docx),\n group(['odt'], TARGETS_BY_EXT.odt),\n group(['doc'], TARGETS_BY_EXT.doc),\n group(['rtf'], TARGETS_BY_EXT.rtf),\n group(['xlsx'], TARGETS_BY_EXT.xlsx),\n group(['ods'], TARGETS_BY_EXT.ods),\n group(['xls'], TARGETS_BY_EXT.xls),\n group(['pptx'], TARGETS_BY_EXT.pptx),\n group(['odp'], TARGETS_BY_EXT.odp),\n group(['ppt'], TARGETS_BY_EXT.ppt),\n group(['pdf'], TARGETS_BY_EXT.pdf),\n // Generation: zero-byte seed in the target's silo (see SEED_EXT_FOR_TARGET).\n { from: [EMPTY_MIME], to: GENERATION_TARGETS, convert },\n ],\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Cek0wNdY.js");
3
- const require_tool_registry = require("../../../tool_registry-BdKlPuHS.js");
3
+ const require_tool_registry = require("../../../tool_registry-DwZ6pzN4.js");
4
4
  require("../../../guards.cjs");
5
5
  const require_exceptions = require("../../../exceptions-D79HIKeX.js");
6
6
  //#region src/batteries/media/engines/tesseract_js.ts
@@ -1,4 +1,4 @@
1
- import { o as isError } from "../../../tool_registry-BaLUIzWz.mjs";
1
+ import { o as isError } from "../../../tool_registry-CZ55mIti.mjs";
2
2
  import "../../../guards.mjs";
3
3
  import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4Ofs.mjs";
4
4
  //#region src/batteries/media/engines/tesseract_js.ts
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../../chunk-Cek0wNdY.js");
3
- const require_tool_registry = require("../../../tool_registry-BdKlPuHS.js");
3
+ const require_tool_registry = require("../../../tool_registry-DwZ6pzN4.js");
4
4
  require("../../../guards.cjs");
5
5
  const require_batteries_media_contracts = require("../contracts.cjs");
6
6
  const require_exceptions = require("../../../exceptions-D79HIKeX.js");
@@ -1,4 +1,4 @@
1
- import { o as isError } from "../../../tool_registry-BaLUIzWz.mjs";
1
+ import { o as isError } from "../../../tool_registry-CZ55mIti.mjs";
2
2
  import "../../../guards.mjs";
3
3
  import { PCM_MIME, bytesToPcm } from "../contracts.mjs";
4
4
  import { t as E_INVALID_MEDIA_PIPELINE_CONFIG } from "../../../exceptions-BLTn4Ofs.mjs";
@@ -1,13 +1,14 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("../../chunk-Cek0wNdY.js");
3
- const require_tool_registry = require("../../tool_registry-BdKlPuHS.js");
4
- const require_tool = require("../../tool-B2nnkwNU.js");
5
- require("../../common-BBK2e5ro.js");
6
- const require_tool_call = require("../../tool_call-BxL3eU1k.js");
7
- const require_spooled_markdown_artifact = require("../../spooled_markdown_artifact-Dj3fNowz.js");
3
+ const require_tool_registry = require("../../tool_registry-DwZ6pzN4.js");
4
+ const require_tool = require("../../tool-Dhjca7rC.js");
5
+ require("../../common-DmDn4Gfl.js");
6
+ const require_tool_call = require("../../tool_call-Cyi0hUUc.js");
7
+ const require_spooled_markdown_artifact = require("../../spooled_markdown_artifact-CD4PrrpV.js");
8
8
  require("../../guards.cjs");
9
+ const require_batteries_media_contracts = require("./contracts.cjs");
9
10
  const require_exceptions = require("../../exceptions-D79HIKeX.js");
10
- const require_validate = require("../../validate-DgDZkUKs.js");
11
+ const require_validate = require("../../validate-gaSa9bsk.js");
11
12
  let _nhtio_validation = require("@nhtio/validation");
12
13
  //#region src/batteries/media/forge.ts
13
14
  /**
@@ -84,6 +85,44 @@ var enumerateMedia = (ctx) => {
84
85
  return out;
85
86
  };
86
87
  var failure = (code, message) => `Error (${code}): ${message}`;
88
+ /**
89
+ * Extract the format token from an `empty:<format>` sentinel id, or undefined for normal ids.
90
+ * Harness-minted ids are UUIDs, which can never start with `empty:` — the sentinel occupies
91
+ * input space that was previously a guaranteed MEDIA_NOT_FOUND.
92
+ */
93
+ var emptyFormatOf = (mediaId) => {
94
+ if (!mediaId.toLowerCase().startsWith("empty:")) return void 0;
95
+ return mediaId.slice(6).trim().toLowerCase();
96
+ };
97
+ /** The format tokens creatable in this deployment (reachable from {@link EMPTY_MIME}). */
98
+ var creatableFormats = (registry) => registry.convertTargets(require_batteries_media_contracts.EMPTY_MIME);
99
+ /** Materialize a new blank media payload for the sentinel, or a model-actionable failure. */
100
+ var materializeEmpty = async (registry, format, signal) => {
101
+ const creatable = creatableFormats(registry);
102
+ if (!creatable.includes(format)) return {
103
+ ok: false,
104
+ failure: failure("EMPTY_FORMAT_UNAVAILABLE", `cannot create "${format}" media in this deployment. Creatable formats: ${creatable.join(", ") || "(none)"}. Do not retry this format here.`)
105
+ };
106
+ const output = (await registry.convert({
107
+ bytes: new Uint8Array(0),
108
+ mimeType: require_batteries_media_contracts.EMPTY_MIME,
109
+ filename: "untitled",
110
+ to: format,
111
+ signal
112
+ })).outputs[0];
113
+ if (!output) return {
114
+ ok: false,
115
+ failure: failure("EMPTY_GENERATION_FAILED", `generating "${format}" produced no output`)
116
+ };
117
+ return {
118
+ ok: true,
119
+ payload: {
120
+ bytes: output.bytes,
121
+ mimeType: output.mimeType,
122
+ filename: `untitled.${format}`
123
+ }
124
+ };
125
+ };
87
126
  var mediaKindOf = (mimeType) => {
88
127
  const mime = mimeType.toLowerCase();
89
128
  if (mime.startsWith("image/")) return "image";
@@ -117,13 +156,17 @@ var renderResult = async (ctx, result, toolName) => {
117
156
  return typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2);
118
157
  };
119
158
  /** Resolve + acquire the input media or produce a model-actionable failure string. */
120
- var acquire = async (ctx, resolveMedia, mediaId) => {
159
+ var acquire = async (ctx, registry, resolveMedia, mediaId) => {
160
+ const format = emptyFormatOf(mediaId);
161
+ if (format !== void 0) return materializeEmpty(registry, format, ctx.abortSignal);
121
162
  const media = await resolveMedia(ctx, mediaId);
122
163
  if (!media) {
123
164
  const visible = enumerateMedia(ctx);
165
+ const creatable = creatableFormats(registry);
166
+ const createHint = creatable.length > 0 ? ` To create NEW media instead, pass media_id "empty:<format>" (available: ${creatable.join(", ")}).` : "";
124
167
  return {
125
168
  ok: false,
126
- 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."}`)
169
+ 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}`)
127
170
  };
128
171
  }
129
172
  return {
@@ -132,7 +175,13 @@ var acquire = async (ctx, resolveMedia, mediaId) => {
132
175
  };
133
176
  };
134
177
  /** Build a per-run @id ref resolver bound to this dispatch's visible media. */
135
- var refResolverFor = (ctx, resolveMedia) => async (id) => {
178
+ var refResolverFor = (ctx, registry, resolveMedia) => async (id) => {
179
+ const format = emptyFormatOf(id);
180
+ if (format !== void 0) {
181
+ const made = await materializeEmpty(registry, format, ctx.abortSignal);
182
+ if (!made.ok) throw new Error(made.failure);
183
+ return made.payload;
184
+ }
136
185
  const media = await resolveMedia(ctx, id);
137
186
  if (!media) {
138
187
  const visible = enumerateMedia(ctx).map((m) => m.id).join(", ");
@@ -203,6 +252,21 @@ var grammarText = (pipeline) => {
203
252
  replace: "[SSN]"
204
253
  }
205
254
  }] }));
255
+ const creatable = creatableFormats(pipeline.capabilities);
256
+ const creating = creatable.length > 0 ? [
257
+ "",
258
+ "Creating new media:",
259
+ `Pass media_id "empty:<format>" to create a NEW blank file instead of processing an existing one (available: ${creatable.join(", ")}).`,
260
+ `Example: media_id "empty:xlsx" with q "${require_validate.toPipe({ steps: [{
261
+ verb: "sheet.update_cells",
262
+ args: { updates: [{
263
+ address: "A1",
264
+ value: "Title"
265
+ }] }
266
+ }] })}"`,
267
+ "Blank images are 1024×1024 white — resize in the same statement (empty:png + \"image resize width=64\").",
268
+ "The same sentinel works as a ref: merge with=@empty:xlsx."
269
+ ] : [];
206
270
  return [
207
271
  "Statements are pipe expressions: verb name=value ... | verb name=value ...",
208
272
  "Named args only. Indices are 1-based. Quote values containing spaces or dashes.",
@@ -211,6 +275,7 @@ var grammarText = (pipeline) => {
211
275
  "",
212
276
  "Available verbs:",
213
277
  ...lines,
278
+ ...creating,
214
279
  "",
215
280
  "Examples (remember: inside JSON tool args, backslashes must be doubled — \\\\d):",
216
281
  ...examples.map((e) => ` ${e}`)
@@ -230,13 +295,16 @@ var buildListMediaTool = (overrides) => {
230
295
  };
231
296
  var buildCompositeTool = (pipeline, resolveMedia, gate, overrides) => {
232
297
  const name = overrides?.media_query?.name ?? "media_query";
298
+ 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).
299
+
300
+ ${grammarText(pipeline)}`;
301
+ const creatable = creatableFormats(pipeline.capabilities);
302
+ 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).";
233
303
  return new require_tool.Tool({
234
304
  name,
235
- 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).
236
-
237
- ${grammarText(pipeline)}`,
305
+ description,
238
306
  inputSchema: _nhtio_validation.validator.object({
239
- media_id: _nhtio_validation.validator.string().required().description("The id of the media to process (call list_media to discover ids)."),
307
+ media_id: _nhtio_validation.validator.string().required().description(mediaIdDescription),
240
308
  q: _nhtio_validation.validator.string().description("A pipe statement, e.g. \"select pages=2-5 | extract text\"."),
241
309
  ops: _nhtio_validation.validator.array().items(_nhtio_validation.validator.object({
242
310
  verb: _nhtio_validation.validator.string().required(),
@@ -248,12 +316,12 @@ ${grammarText(pipeline)}`,
248
316
  if (q === void 0 === (ops === void 0)) return failure("BAD_REQUEST", "provide exactly one of q (pipe statement) or ops (structured steps)");
249
317
  const dispatch = ctx;
250
318
  await runGate(gate, dispatch, name, args);
251
- const acquired = await acquire(dispatch, resolveMedia, mediaId);
319
+ const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId);
252
320
  if (!acquired.ok) return acquired.failure;
253
321
  try {
254
322
  const runOptions = {
255
323
  signal: dispatch.abortSignal,
256
- resolveRef: refResolverFor(dispatch, resolveMedia)
324
+ resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia)
257
325
  };
258
326
  return await renderResult(dispatch, q !== void 0 ? await pipeline.query(acquired.payload, q, runOptions) : await pipeline.ops(acquired.payload, ops, runOptions), name);
259
327
  } catch (err) {
@@ -271,8 +339,9 @@ var opArgsFrom = (args) => {
271
339
  }
272
340
  return out;
273
341
  };
274
- var granularSchemaFor = (spec) => {
275
- const shape = { media_id: _nhtio_validation.validator.string().required().description("The id of the media to process (call list_media to discover ids).") };
342
+ var granularSchemaFor = (spec, creatable) => {
343
+ 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).";
344
+ const shape = { media_id: _nhtio_validation.validator.string().required().description(mediaIdDescription) };
276
345
  for (const [argName, arg] of Object.entries(spec.args)) {
277
346
  let schema;
278
347
  switch (arg.type) {
@@ -337,12 +406,12 @@ var buildGranularTool = (pipeline, spec, resolveMedia, gate, overrides) => {
337
406
  return new require_tool.Tool({
338
407
  name,
339
408
  description: overrides?.[defaultName]?.description ?? `${spec.description} Processes a media file from this conversation locally. Indices are 1-based.`,
340
- inputSchema: granularSchemaFor(spec),
409
+ inputSchema: granularSchemaFor(spec, creatableFormats(pipeline.capabilities)),
341
410
  handler: async (args, ctx) => {
342
411
  const dispatch = ctx;
343
412
  const { media_id: mediaId } = args;
344
413
  await runGate(gate, dispatch, name, args);
345
- const acquired = await acquire(dispatch, resolveMedia, mediaId);
414
+ const acquired = await acquire(dispatch, pipeline.capabilities, resolveMedia, mediaId);
346
415
  if (!acquired.ok) return acquired.failure;
347
416
  const opArgs = opArgsFrom(args);
348
417
  normalizeRefArgs(spec, opArgs);
@@ -352,7 +421,7 @@ var buildGranularTool = (pipeline, spec, resolveMedia, gate, overrides) => {
352
421
  args: opArgs
353
422
  }], {
354
423
  signal: dispatch.abortSignal,
355
- resolveRef: refResolverFor(dispatch, resolveMedia)
424
+ resolveRef: refResolverFor(dispatch, pipeline.capabilities, resolveMedia)
356
425
  }), name);
357
426
  } catch (err) {
358
427
  return renderError(err);
@@ -1 +1 @@
1
- {"version":3,"file":"forge.cjs","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.cjs","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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"}