@effect-uai/core 0.12.0 → 0.13.0

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 (301) hide show
  1. package/dist/{AiError-Bf1pq4BB.d.mts → AiError-eLV9t1lU.d.mts} +12 -16
  2. package/dist/AiError-eLV9t1lU.d.mts.map +1 -0
  3. package/dist/{Audio-CR7CSljJ.d.mts → Audio-60lKKHxS.d.mts} +4 -5
  4. package/dist/Audio-60lKKHxS.d.mts.map +1 -0
  5. package/dist/{Image-C4vuBmu0.d.mts → Image-BK8-4H6s.d.mts} +12 -14
  6. package/dist/Image-BK8-4H6s.d.mts.map +1 -0
  7. package/dist/{Items-BKQgqJ8A.d.mts → Items-SfUabNuB.d.mts} +74 -76
  8. package/dist/Items-SfUabNuB.d.mts.map +1 -0
  9. package/dist/{Loop-D8nWpowc.d.mts → Loop-B54AB1ME.d.mts} +19 -21
  10. package/dist/Loop-B54AB1ME.d.mts.map +1 -0
  11. package/dist/{Media-D_CpcM1Z.d.mts → Media-DwgpifPD.d.mts} +1 -1
  12. package/dist/Media-DwgpifPD.d.mts.map +1 -0
  13. package/dist/{SandboxError-CIrVDOMV.d.mts → SandboxError-BN8b28Bh.d.mts} +11 -15
  14. package/dist/SandboxError-BN8b28Bh.d.mts.map +1 -0
  15. package/dist/{StructuredFormat-DHaUJBdd.d.mts → StructuredFormat-BlZhyOsi.d.mts} +3 -7
  16. package/dist/StructuredFormat-BlZhyOsi.d.mts.map +1 -0
  17. package/dist/{Tool-u5bag8SO.d.mts → Tool-DE-rnvkw.d.mts} +5 -8
  18. package/dist/Tool-DE-rnvkw.d.mts.map +1 -0
  19. package/dist/{ToolEvent-BBfOdhF_.d.mts → ToolEvent-D99MGQYH.d.mts} +56 -63
  20. package/dist/ToolEvent-D99MGQYH.d.mts.map +1 -0
  21. package/dist/{ToolResult-CRpmkBXR.d.mts → ToolResult-Cj8ChodL.d.mts} +49 -54
  22. package/dist/ToolResult-Cj8ChodL.d.mts.map +1 -0
  23. package/dist/{Toolkit-D4mQvhjq.d.mts → Toolkit-BEZrNPTI.d.mts} +13 -16
  24. package/dist/Toolkit-BEZrNPTI.d.mts.map +1 -0
  25. package/dist/browser/Browser.d.mts +11 -7
  26. package/dist/browser/Browser.d.mts.map +1 -1
  27. package/dist/browser/Browser.mjs +1 -1
  28. package/dist/browser/Browser.mjs.map +1 -1
  29. package/dist/browser/BrowserError.d.mts +9 -13
  30. package/dist/browser/BrowserError.d.mts.map +1 -1
  31. package/dist/browser/BrowserError.mjs +1 -1
  32. package/dist/browser/BrowserError.mjs.map +1 -1
  33. package/dist/browser/BrowserTool.d.mts +33 -36
  34. package/dist/browser/BrowserTool.d.mts.map +1 -1
  35. package/dist/browser/BrowserTool.mjs +1 -1
  36. package/dist/browser/BrowserTool.mjs.map +1 -1
  37. package/dist/capabilities/Capabilities.d.mts +8 -6
  38. package/dist/capabilities/Capabilities.d.mts.map +1 -1
  39. package/dist/capabilities/Capabilities.mjs +1 -1
  40. package/dist/capabilities/Capabilities.mjs.map +1 -1
  41. package/dist/chunker/Chunker.d.mts +32 -0
  42. package/dist/chunker/Chunker.d.mts.map +1 -0
  43. package/dist/chunker/Chunker.mjs +14 -0
  44. package/dist/chunker/Chunker.mjs.map +1 -0
  45. package/dist/dist-BaenoU1A.mjs +145 -0
  46. package/dist/dist-BaenoU1A.mjs.map +1 -0
  47. package/dist/{dist-C0m0QnUP.mjs → dist-nohcnkD6.mjs} +16 -15
  48. package/dist/dist-nohcnkD6.mjs.map +1 -0
  49. package/dist/domain/AiError.d.mts +1 -1
  50. package/dist/domain/AiError.mjs +1 -1
  51. package/dist/domain/AiError.mjs.map +1 -1
  52. package/dist/domain/Audio.d.mts +1 -1
  53. package/dist/domain/Audio.mjs +1 -1
  54. package/dist/domain/Citation.d.mts +0 -2
  55. package/dist/domain/Citation.d.mts.map +1 -1
  56. package/dist/domain/Citation.mjs +1 -1
  57. package/dist/domain/Image.d.mts +1 -1
  58. package/dist/domain/Image.mjs +1 -1
  59. package/dist/domain/Image.mjs.map +1 -1
  60. package/dist/domain/Items.d.mts +1 -1
  61. package/dist/domain/Items.mjs +1 -1
  62. package/dist/domain/Items.mjs.map +1 -1
  63. package/dist/domain/Media.d.mts +1 -1
  64. package/dist/domain/Media.mjs +1 -1
  65. package/dist/domain/Music.d.mts +18 -11
  66. package/dist/domain/Music.d.mts.map +1 -1
  67. package/dist/domain/Music.mjs +1 -1
  68. package/dist/domain/Music.mjs.map +1 -1
  69. package/dist/domain/Research.d.mts +3 -4
  70. package/dist/domain/Research.d.mts.map +1 -1
  71. package/dist/domain/Research.mjs +1 -1
  72. package/dist/domain/Transcript.d.mts +2 -3
  73. package/dist/domain/Transcript.d.mts.map +1 -1
  74. package/dist/domain/Transcript.mjs +1 -1
  75. package/dist/domain/Turn.d.mts +143 -164
  76. package/dist/domain/Turn.d.mts.map +1 -1
  77. package/dist/domain/Turn.mjs +1 -1
  78. package/dist/domain/Turn.mjs.map +1 -1
  79. package/dist/domain/Turn.test.d.mts +1 -1
  80. package/dist/domain/Turn.test.mjs +28 -18
  81. package/dist/domain/Turn.test.mjs.map +1 -1
  82. package/dist/embedding-model/Embedding.d.mts +1 -3
  83. package/dist/embedding-model/Embedding.d.mts.map +1 -1
  84. package/dist/embedding-model/Embedding.mjs +1 -1
  85. package/dist/embedding-model/EmbeddingModel.d.mts +1 -3
  86. package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
  87. package/dist/embedding-model/EmbeddingModel.mjs +1 -1
  88. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
  89. package/dist/embedding-model/EmbeddingModel.test.d.mts +1 -1
  90. package/dist/embedding-model/EmbeddingModel.test.mjs +14 -9
  91. package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
  92. package/dist/index.d.mts +16 -13
  93. package/dist/index.mjs +4 -1
  94. package/dist/job/Job.d.mts +4 -4
  95. package/dist/job/Job.d.mts.map +1 -1
  96. package/dist/job/Job.mjs +1 -1
  97. package/dist/job/Job.mjs.map +1 -1
  98. package/dist/language-model/LanguageModel.d.mts +4 -6
  99. package/dist/language-model/LanguageModel.d.mts.map +1 -1
  100. package/dist/language-model/LanguageModel.mjs +1 -1
  101. package/dist/language-model/LanguageModel.mjs.map +1 -1
  102. package/dist/language-model/LanguageModel.test.d.mts +1 -1
  103. package/dist/language-model/LanguageModel.test.mjs +6 -3
  104. package/dist/language-model/LanguageModel.test.mjs.map +1 -1
  105. package/dist/loop/Loop.d.mts +1 -1
  106. package/dist/loop/Loop.mjs +1 -1
  107. package/dist/loop/Loop.mjs.map +1 -1
  108. package/dist/loop/Loop.test.d.mts +1 -1
  109. package/dist/loop/Loop.test.mjs +60 -34
  110. package/dist/loop/Loop.test.mjs.map +1 -1
  111. package/dist/{magic-string.es-Dk-MMA0F.mjs → magic-string.es-CB2hOw9v.mjs} +14 -13
  112. package/dist/{magic-string.es-Dk-MMA0F.mjs.map → magic-string.es-CB2hOw9v.mjs.map} +1 -1
  113. package/dist/math/Vector.d.mts +0 -2
  114. package/dist/math/Vector.d.mts.map +1 -1
  115. package/dist/math/Vector.mjs +1 -1
  116. package/dist/math/Vector.mjs.map +1 -1
  117. package/dist/music-generator/MusicGenerator.d.mts +2 -4
  118. package/dist/music-generator/MusicGenerator.d.mts.map +1 -1
  119. package/dist/music-generator/MusicGenerator.mjs +1 -1
  120. package/dist/music-generator/MusicGenerator.mjs.map +1 -1
  121. package/dist/music-generator/MusicGenerator.test.d.mts +1 -1
  122. package/dist/music-generator/MusicGenerator.test.mjs +19 -10
  123. package/dist/music-generator/MusicGenerator.test.mjs.map +1 -1
  124. package/dist/observability/Metrics.d.mts +10 -7
  125. package/dist/observability/Metrics.d.mts.map +1 -1
  126. package/dist/observability/Metrics.mjs +1 -1
  127. package/dist/observability/Metrics.mjs.map +1 -1
  128. package/dist/observability/Metrics.test.d.mts +1 -1
  129. package/dist/observability/Metrics.test.mjs +50 -28
  130. package/dist/observability/Metrics.test.mjs.map +1 -1
  131. package/dist/observability/Telemetry.d.mts +2 -3
  132. package/dist/observability/Telemetry.d.mts.map +1 -1
  133. package/dist/observability/Telemetry.mjs +1 -1
  134. package/dist/observability/Telemetry.mjs.map +1 -1
  135. package/dist/observability/Telemetry.test.d.mts +1 -1
  136. package/dist/observability/Telemetry.test.mjs +15 -7
  137. package/dist/observability/Telemetry.test.mjs.map +1 -1
  138. package/dist/reranker/Reranker.d.mts +46 -0
  139. package/dist/reranker/Reranker.d.mts.map +1 -0
  140. package/dist/reranker/Reranker.mjs +14 -0
  141. package/dist/reranker/Reranker.mjs.map +1 -0
  142. package/dist/research/DeepResearch.d.mts +1 -3
  143. package/dist/research/DeepResearch.d.mts.map +1 -1
  144. package/dist/research/DeepResearch.mjs +1 -1
  145. package/dist/research/DeepResearch.mjs.map +1 -1
  146. package/dist/retry/Retry.d.mts +2 -6
  147. package/dist/retry/Retry.d.mts.map +1 -1
  148. package/dist/retry/Retry.mjs +1 -1
  149. package/dist/retry/Retry.mjs.map +1 -1
  150. package/dist/retry/Retry.test.d.mts +1 -1
  151. package/dist/retry/Retry.test.mjs +6 -3
  152. package/dist/retry/Retry.test.mjs.map +1 -1
  153. package/dist/sandbox/Memory.d.mts +0 -2
  154. package/dist/sandbox/Memory.d.mts.map +1 -1
  155. package/dist/sandbox/Memory.mjs +4 -4
  156. package/dist/sandbox/Memory.mjs.map +1 -1
  157. package/dist/sandbox/Sandbox.d.mts +144 -156
  158. package/dist/sandbox/Sandbox.d.mts.map +1 -1
  159. package/dist/sandbox/Sandbox.mjs +1 -1
  160. package/dist/sandbox/Sandbox.mjs.map +1 -1
  161. package/dist/sandbox/SandboxError.d.mts +1 -1
  162. package/dist/sandbox/SandboxError.mjs +1 -1
  163. package/dist/sandbox/SandboxError.mjs.map +1 -1
  164. package/dist/sandbox/SandboxImage.d.mts +0 -2
  165. package/dist/sandbox/SandboxImage.d.mts.map +1 -1
  166. package/dist/sandbox/SandboxImage.mjs +1 -1
  167. package/dist/sandbox/SandboxImage.mjs.map +1 -1
  168. package/dist/sandbox/SandboxNetwork.d.mts +0 -2
  169. package/dist/sandbox/SandboxNetwork.d.mts.map +1 -1
  170. package/dist/sandbox/SandboxNetwork.mjs +1 -1
  171. package/dist/sandbox/SandboxNetwork.mjs.map +1 -1
  172. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +8 -7
  173. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
  174. package/dist/speech-synthesizer/SpeechSynthesizer.mjs +1 -1
  175. package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
  176. package/dist/speech-synthesizer/SpeechSynthesizer.test.d.mts +1 -1
  177. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +31 -16
  178. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
  179. package/dist/streaming/JSONL.d.mts +1 -5
  180. package/dist/streaming/JSONL.d.mts.map +1 -1
  181. package/dist/streaming/JSONL.mjs +1 -1
  182. package/dist/streaming/JSONL.mjs.map +1 -1
  183. package/dist/streaming/JSONL.test.d.mts +1 -1
  184. package/dist/streaming/JSONL.test.mjs +16 -8
  185. package/dist/streaming/JSONL.test.mjs.map +1 -1
  186. package/dist/streaming/Lines.d.mts +0 -2
  187. package/dist/streaming/Lines.d.mts.map +1 -1
  188. package/dist/streaming/Lines.mjs +1 -1
  189. package/dist/streaming/Lines.mjs.map +1 -1
  190. package/dist/streaming/SSE.d.mts +0 -2
  191. package/dist/streaming/SSE.d.mts.map +1 -1
  192. package/dist/streaming/SSE.mjs +1 -1
  193. package/dist/streaming/SSE.mjs.map +1 -1
  194. package/dist/streaming/SSE.test.d.mts +1 -1
  195. package/dist/streaming/SSE.test.mjs +34 -17
  196. package/dist/streaming/SSE.test.mjs.map +1 -1
  197. package/dist/structured-format/StructuredFormat.d.mts +1 -1
  198. package/dist/structured-format/StructuredFormat.mjs +1 -1
  199. package/dist/structured-format/StructuredFormat.mjs.map +1 -1
  200. package/dist/structured-format/StructuredFormat.test.d.mts +1 -1
  201. package/dist/structured-format/StructuredFormat.test.mjs +7 -4
  202. package/dist/structured-format/StructuredFormat.test.mjs.map +1 -1
  203. package/dist/{dist-CcRV3-A5.mjs → test.DNmyFkvJ-YPzDR0Co.mjs} +1644 -5172
  204. package/dist/test.DNmyFkvJ-YPzDR0Co.mjs.map +1 -0
  205. package/dist/testing/MockMusicGenerator.d.mts +4 -3
  206. package/dist/testing/MockMusicGenerator.d.mts.map +1 -1
  207. package/dist/testing/MockMusicGenerator.mjs.map +1 -1
  208. package/dist/testing/MockProvider.d.mts +1 -2
  209. package/dist/testing/MockProvider.d.mts.map +1 -1
  210. package/dist/testing/MockProvider.mjs.map +1 -1
  211. package/dist/testing/MockSandbox.d.mts +256 -290
  212. package/dist/testing/MockSandbox.d.mts.map +1 -1
  213. package/dist/testing/MockSandbox.mjs.map +1 -1
  214. package/dist/testing/MockSpeechSynthesizer.d.mts +10 -6
  215. package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -1
  216. package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -1
  217. package/dist/testing/MockTranscriber.d.mts +3 -2
  218. package/dist/testing/MockTranscriber.d.mts.map +1 -1
  219. package/dist/testing/MockTranscriber.mjs.map +1 -1
  220. package/dist/tokenizer/Tokenizer.d.mts +22 -0
  221. package/dist/tokenizer/Tokenizer.d.mts.map +1 -0
  222. package/dist/tokenizer/Tokenizer.mjs +9 -0
  223. package/dist/tokenizer/Tokenizer.mjs.map +1 -0
  224. package/dist/tool/Approval.d.mts +31 -36
  225. package/dist/tool/Approval.d.mts.map +1 -1
  226. package/dist/tool/Approval.mjs +1 -1
  227. package/dist/tool/Approval.mjs.map +1 -1
  228. package/dist/tool/Approval.test.d.mts +1 -1
  229. package/dist/tool/Approval.test.mjs +37 -24
  230. package/dist/tool/Approval.test.mjs.map +1 -1
  231. package/dist/tool/HistoryCheck.d.mts +2 -4
  232. package/dist/tool/HistoryCheck.d.mts.map +1 -1
  233. package/dist/tool/HistoryCheck.mjs +1 -1
  234. package/dist/tool/HistoryCheck.mjs.map +1 -1
  235. package/dist/tool/Tool.d.mts +1 -1
  236. package/dist/tool/Tool.mjs +4 -4
  237. package/dist/tool/Tool.mjs.map +1 -1
  238. package/dist/tool/Tool.test.d.mts +1 -1
  239. package/dist/tool/Tool.test.mjs +23 -11
  240. package/dist/tool/Tool.test.mjs.map +1 -1
  241. package/dist/tool/ToolEvent.d.mts +1 -1
  242. package/dist/tool/ToolEvent.mjs +1 -1
  243. package/dist/tool/ToolEvent.mjs.map +1 -1
  244. package/dist/tool/ToolResult.d.mts +1 -1
  245. package/dist/tool/ToolResult.mjs +1 -1
  246. package/dist/tool/ToolResult.mjs.map +1 -1
  247. package/dist/tool/Toolkit.d.mts +1 -1
  248. package/dist/tool/Toolkit.mjs +4 -3
  249. package/dist/tool/Toolkit.mjs.map +1 -1
  250. package/dist/tool/Toolkit.test.d.mts +1 -1
  251. package/dist/tool/Toolkit.test.mjs +40 -21
  252. package/dist/tool/Toolkit.test.mjs.map +1 -1
  253. package/dist/transcriber/Transcriber.d.mts +6 -6
  254. package/dist/transcriber/Transcriber.d.mts.map +1 -1
  255. package/dist/transcriber/Transcriber.mjs +1 -1
  256. package/dist/transcriber/Transcriber.mjs.map +1 -1
  257. package/dist/transcriber/Transcriber.test.d.mts +1 -1
  258. package/dist/transcriber/Transcriber.test.mjs +20 -10
  259. package/dist/transcriber/Transcriber.test.mjs.map +1 -1
  260. package/dist/utils-CiH_DjFW.mjs +3918 -0
  261. package/dist/utils-CiH_DjFW.mjs.map +1 -0
  262. package/dist/web-read/WebRead.d.mts +15 -9
  263. package/dist/web-read/WebRead.d.mts.map +1 -1
  264. package/dist/web-read/WebRead.mjs +1 -1
  265. package/dist/web-read/WebRead.mjs.map +1 -1
  266. package/dist/web-read/WebReadTool.d.mts +4 -5
  267. package/dist/web-read/WebReadTool.d.mts.map +1 -1
  268. package/dist/web-read/WebReadTool.mjs +1 -1
  269. package/dist/web-read/WebReadTool.mjs.map +1 -1
  270. package/dist/web-search/WebSearch.d.mts +22 -13
  271. package/dist/web-search/WebSearch.d.mts.map +1 -1
  272. package/dist/web-search/WebSearch.mjs +1 -1
  273. package/dist/web-search/WebSearch.mjs.map +1 -1
  274. package/dist/web-search/WebSearchTool.d.mts +6 -6
  275. package/dist/web-search/WebSearchTool.d.mts.map +1 -1
  276. package/dist/web-search/WebSearchTool.mjs +1 -1
  277. package/dist/web-search/WebSearchTool.mjs.map +1 -1
  278. package/package.json +16 -4
  279. package/src/chunker/Chunker.ts +34 -0
  280. package/src/index.ts +3 -0
  281. package/src/loop/Loop.test.ts +13 -14
  282. package/src/loop/Loop.ts +74 -76
  283. package/src/reranker/Reranker.ts +51 -0
  284. package/src/testing/MockProvider.ts +8 -9
  285. package/src/tokenizer/Tokenizer.ts +19 -0
  286. package/src/tool/Tool.ts +13 -13
  287. package/dist/AiError-Bf1pq4BB.d.mts.map +0 -1
  288. package/dist/Audio-CR7CSljJ.d.mts.map +0 -1
  289. package/dist/Image-C4vuBmu0.d.mts.map +0 -1
  290. package/dist/Items-BKQgqJ8A.d.mts.map +0 -1
  291. package/dist/Loop-D8nWpowc.d.mts.map +0 -1
  292. package/dist/Media-D_CpcM1Z.d.mts.map +0 -1
  293. package/dist/SandboxError-CIrVDOMV.d.mts.map +0 -1
  294. package/dist/StructuredFormat-DHaUJBdd.d.mts.map +0 -1
  295. package/dist/Tool-u5bag8SO.d.mts.map +0 -1
  296. package/dist/ToolEvent-BBfOdhF_.d.mts.map +0 -1
  297. package/dist/ToolResult-CRpmkBXR.d.mts.map +0 -1
  298. package/dist/Toolkit-D4mQvhjq.d.mts.map +0 -1
  299. package/dist/dist-C0m0QnUP.mjs.map +0 -1
  300. package/dist/dist-CcRV3-A5.mjs.map +0 -1
  301. /package/dist/{chunk-uyGKjUfl.mjs → rolldown-runtime-EAsCxrDo.mjs} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.test.mjs","names":["Tool.fromStandardSchema","Tool.make","Tool.withRun","Tool.toDescriptors","Tool.decodeArgs","Tool.fromEffectSchema","Tool.signal","Tool.provider","Tool.noInput","Tool.descriptorsOf","Tool.providerToolsOf","Tool.isProviderTool"],"sources":["../../src/tool/Tool.test.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from \"@standard-schema/spec\"\nimport { Context, Effect, Schema } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as Items from \"../domain/Items.js\"\nimport * as Tool from \"./Tool.js\"\n\n// ---------------------------------------------------------------------------\n// Minimal dual-standard schema for testing — no need to pull Zod / Valibot /\n// ArkType as devDeps. This is the smallest possible object satisfying both\n// `StandardSchemaV1` and `StandardJSONSchemaV1` per their published specs.\n// ---------------------------------------------------------------------------\n\ntype EmailRecipient = { readonly to: string }\n\nconst emailRecipientSchema: StandardSchemaV1<unknown, EmailRecipient> &\n StandardJSONSchemaV1<unknown, EmailRecipient> = {\n \"~standard\": {\n version: 1,\n vendor: \"test-fixture\",\n validate: (value) => {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"to\" in value &&\n typeof (value as { to: unknown }).to === \"string\"\n ) {\n return { value: value as EmailRecipient }\n }\n return { issues: [{ message: \"expected { to: string }\" }] }\n },\n jsonSchema: {\n input: () => ({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n }),\n output: () => ({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n }),\n },\n },\n}\n\n// A schema that satisfies StandardSchemaV1 only (no JSON Schema). Used to\n// verify the helper's compile-time guard.\nconst standardOnly: StandardSchemaV1<unknown, EmailRecipient> = {\n \"~standard\": {\n version: 1,\n vendor: \"test-fixture\",\n validate: () => ({ value: { to: \"\" } }),\n },\n}\n\ndescribe(\"Tool.fromStandardSchema\", () => {\n it(\"returns the schema (structurally) typed as ToolInputSchema<Output>\", () => {\n const adapted = Tool.fromStandardSchema(emailRecipientSchema)\n\n // Same object — helper is a type-narrowing identity at runtime.\n expect(adapted).toBe(emailRecipientSchema)\n\n // Both interfaces accessible through the same `~standard` key.\n const valid = adapted[\"~standard\"].validate({ to: \"hi@example.com\" })\n expect(valid).toEqual({ value: { to: \"hi@example.com\" } })\n\n const json = adapted[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" })\n expect(json).toEqual({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n })\n })\n\n it(\"composes with Tool.make so Input is inferred from the schema's Output\", async () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n // `run`'s parameter is typed as { to: string } via the schema's Output —\n // this property access compiles without annotation.\n const result = await Effect.runPromise(sendEmail.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(result).toBe(\"queued: x@y.z\")\n })\n\n it(\"type: rejects schemas missing the Standard JSON Schema half at compile time\", () => {\n // @ts-expect-error — `standardOnly` lacks `jsonSchema`; helper's\n // intersection constraint refuses it.\n Tool.fromStandardSchema(standardOnly)\n })\n\n it(\"type: Output type flows through fromStandardSchema into Tool.make\", () => {\n const tool = Tool.make({\n name: \"send_email\",\n description: \"send\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: (input) => Effect.succeed(input),\n })\n\n type InputOf<T> = T extends Tool.Tool<string, infer I, any, any, any> ? I : never\n expectTypeOf<InputOf<typeof tool>>().toEqualTypeOf<EmailRecipient>()\n })\n})\n\ndescribe(\"Tool.withRun\", () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`sent: ${to}`),\n })\n\n it(\"swaps run while keeping the model-facing descriptor identical\", async () => {\n const dryRun = Tool.withRun(sendEmail, ({ to }) => Effect.succeed(`dry-run: ${to}`))\n\n // Definition (name/description/schema) is byte-identical -> same descriptor.\n expect(Tool.toDescriptors([dryRun])).toEqual(Tool.toDescriptors([sendEmail]))\n // input is typed from the original tool, no annotation needed.\n const out = await Effect.runPromise(dryRun.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(out).toBe(\"dry-run: x@y.z\")\n })\n})\n\ndescribe(\"Tool.decodeArgs\", () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n const callWith = (args: string): Items.ToolCall => ({\n type: \"function_call\",\n call_id: \"call_1\",\n name: \"send_email\",\n arguments: args,\n })\n\n it(\"decodes valid JSON arguments to the typed input\", async () => {\n const input = await Effect.runPromise(Tool.decodeArgs(sendEmail, callWith('{\"to\":\"x@y.z\"}')))\n expect(input).toEqual({ to: \"x@y.z\" })\n // The return type is the tool's Input, no annotation needed.\n expectTypeOf(input).toEqualTypeOf<EmailRecipient>()\n })\n\n it(\"fails with ToolError when the arguments aren't valid JSON\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith(\"not json\")))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"fails with ToolError when the arguments don't satisfy the schema\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith('{\"to\":42}')))\n expect(exit._tag).toBe(\"Failure\")\n })\n})\n\ndescribe(\"Effect Schema inputSchema\", () => {\n const Recipient = Schema.Struct({ to: Schema.String })\n\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Recipient,\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n const callWith = (args: string): Items.ToolCall => ({\n type: \"function_call\",\n call_id: \"call_1\",\n name: \"send_email\",\n arguments: args,\n })\n\n it(\"runs with the input inferred from the schema's Type\", async () => {\n const result = await Effect.runPromise(sendEmail.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(result).toBe(\"queued: x@y.z\")\n })\n\n it(\"type: Input is the schema's Type\", () => {\n type InputOf<T> = T extends Tool.Tool<string, infer I, any, any, any> ? I : never\n expectTypeOf<InputOf<typeof sendEmail>>().toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"type: E and R are inferred from run through the Effect Schema overload\", () => {\n class Mailer extends Context.Service<\n Mailer,\n { readonly send: (to: string) => Effect.Effect<string> }\n >()(\"test/Mailer\") {}\n\n const tool = Tool.make({\n name: \"send_email\",\n description: \"send\",\n inputSchema: Recipient,\n run: ({ to }) =>\n Effect.gen(function* () {\n const mailer = yield* Mailer\n if (to === \"\") return yield* Effect.fail(\"empty\" as const)\n return yield* mailer.send(to)\n }),\n })\n\n expectTypeOf<Tool.ToolE<typeof tool>>().toEqualTypeOf<\"empty\">()\n expectTypeOf<Tool.ToolR<typeof tool>>().toEqualTypeOf<Mailer>()\n })\n\n it(\"decodes valid JSON arguments to the typed input\", async () => {\n const input = await Effect.runPromise(Tool.decodeArgs(sendEmail, callWith('{\"to\":\"x@y.z\"}')))\n expect(input).toEqual({ to: \"x@y.z\" })\n expectTypeOf(input).toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"fails with ToolValidationError when the arguments don't satisfy the schema\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith('{\"to\":42}')))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"renders a JSON Schema descriptor from the wrapped schema\", () => {\n const [descriptor] = Tool.toDescriptors([sendEmail])\n expect(descriptor?.name).toBe(\"send_email\")\n expect(descriptor?.inputSchema).toMatchObject({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n })\n })\n\n it(\"accepts an already-wrapped fromEffectSchema value unchanged\", async () => {\n const wrapped = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromEffectSchema(Recipient),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n expect(Tool.toDescriptors([wrapped])).toEqual(Tool.toDescriptors([sendEmail]))\n const input = await Effect.runPromise(Tool.decodeArgs(wrapped, callWith('{\"to\":\"x@y.z\"}')))\n expectTypeOf(input).toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"signal: decode-only kinds accept an Effect Schema directly\", async () => {\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"Escalate to a human.\",\n inputSchema: Schema.Struct({ reason: Schema.String }),\n })\n const input = await Effect.runPromise(\n Tool.decodeArgs(escalate, {\n type: \"function_call\",\n call_id: \"call_2\",\n name: \"escalate\",\n arguments: '{\"reason\":\"stuck\"}',\n }),\n )\n expect(input).toEqual({ reason: \"stuck\" })\n expectTypeOf(input).toEqualTypeOf<{ readonly reason: string }>()\n })\n})\n\ndescribe(\"provider-hosted tools\", () => {\n const localTool = Tool.make({\n name: \"send_email\",\n description: \"Send an email.\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ to: Schema.String })),\n run: ({ to }) => Effect.succeed(`sent: ${to}`),\n })\n const signalTool = Tool.signal({\n name: \"escalate\",\n description: \"Escalate to a human.\",\n inputSchema: Schema.Struct({ reason: Schema.String }),\n })\n const hostedTool = Tool.provider({\n name: \"web_search\",\n description: \"Search the web.\",\n inputSchema: Tool.noInput,\n provider: \"gemini\",\n config: { kind: \"google_search\" },\n })\n const toolkit = { send_email: localTool, escalate: signalTool, web_search: hostedTool }\n\n it(\"descriptorsOf excludes provider-hosted tools\", () => {\n const descriptors = Tool.descriptorsOf(toolkit)\n expect(descriptors.map((d) => d.name)).toEqual([\"send_email\", \"escalate\"])\n })\n\n it(\"toDescriptors drops a ProviderTool from a mixed array\", () => {\n expect(Tool.toDescriptors([localTool, hostedTool]).map((d) => d.name)).toEqual([\"send_email\"])\n })\n\n it(\"providerToolsOf returns only the provider-hosted tools\", () => {\n const provider = Tool.providerToolsOf(toolkit)\n expect(provider).toHaveLength(1)\n expect(provider[0]?.name).toBe(\"web_search\")\n expect(provider[0]?.provider).toBe(\"gemini\")\n expect(provider[0]?.config).toEqual({ kind: \"google_search\" })\n })\n\n it(\"providerToolsOf on an absent toolkit is empty\", () => {\n expect(Tool.providerToolsOf(undefined)).toEqual([])\n })\n\n it(\"isProviderTool narrows the tool kind\", () => {\n expect(Tool.isProviderTool(hostedTool)).toBe(true)\n expect(Tool.isProviderTool(localTool)).toBe(false)\n expect(Tool.isProviderTool(signalTool)).toBe(false)\n })\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"Tool.test.mjs","names":["Tool.fromStandardSchema","Tool.make","Tool.withRun","Tool.toDescriptors","Tool.decodeArgs","Tool.fromEffectSchema","Tool.signal","Tool.provider","Tool.noInput","Tool.descriptorsOf","Tool.providerToolsOf","Tool.isProviderTool"],"sources":["../../src/tool/Tool.test.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from \"@standard-schema/spec\"\nimport { Context, Effect, Schema } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as Items from \"../domain/Items.js\"\nimport * as Tool from \"./Tool.js\"\n\n// ---------------------------------------------------------------------------\n// Minimal dual-standard schema for testing — no need to pull Zod / Valibot /\n// ArkType as devDeps. This is the smallest possible object satisfying both\n// `StandardSchemaV1` and `StandardJSONSchemaV1` per their published specs.\n// ---------------------------------------------------------------------------\n\ntype EmailRecipient = { readonly to: string }\n\nconst emailRecipientSchema: StandardSchemaV1<unknown, EmailRecipient> &\n StandardJSONSchemaV1<unknown, EmailRecipient> = {\n \"~standard\": {\n version: 1,\n vendor: \"test-fixture\",\n validate: (value) => {\n if (\n typeof value === \"object\" &&\n value !== null &&\n \"to\" in value &&\n typeof (value as { to: unknown }).to === \"string\"\n ) {\n return { value: value as EmailRecipient }\n }\n return { issues: [{ message: \"expected { to: string }\" }] }\n },\n jsonSchema: {\n input: () => ({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n }),\n output: () => ({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n }),\n },\n },\n}\n\n// A schema that satisfies StandardSchemaV1 only (no JSON Schema). Used to\n// verify the helper's compile-time guard.\nconst standardOnly: StandardSchemaV1<unknown, EmailRecipient> = {\n \"~standard\": {\n version: 1,\n vendor: \"test-fixture\",\n validate: () => ({ value: { to: \"\" } }),\n },\n}\n\ndescribe(\"Tool.fromStandardSchema\", () => {\n it(\"returns the schema (structurally) typed as ToolInputSchema<Output>\", () => {\n const adapted = Tool.fromStandardSchema(emailRecipientSchema)\n\n // Same object — helper is a type-narrowing identity at runtime.\n expect(adapted).toBe(emailRecipientSchema)\n\n // Both interfaces accessible through the same `~standard` key.\n const valid = adapted[\"~standard\"].validate({ to: \"hi@example.com\" })\n expect(valid).toEqual({ value: { to: \"hi@example.com\" } })\n\n const json = adapted[\"~standard\"].jsonSchema.input({ target: \"draft-2020-12\" })\n expect(json).toEqual({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n })\n })\n\n it(\"composes with Tool.make so Input is inferred from the schema's Output\", async () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n // `run`'s parameter is typed as { to: string } via the schema's Output —\n // this property access compiles without annotation.\n const result = await Effect.runPromise(sendEmail.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(result).toBe(\"queued: x@y.z\")\n })\n\n it(\"type: rejects schemas missing the Standard JSON Schema half at compile time\", () => {\n // @ts-expect-error — `standardOnly` lacks `jsonSchema`; helper's\n // intersection constraint refuses it.\n Tool.fromStandardSchema(standardOnly)\n })\n\n it(\"type: Output type flows through fromStandardSchema into Tool.make\", () => {\n const tool = Tool.make({\n name: \"send_email\",\n description: \"send\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: (input) => Effect.succeed(input),\n })\n\n type InputOf<T> = T extends Tool.Tool<string, infer I, any, any, any> ? I : never\n expectTypeOf<InputOf<typeof tool>>().toEqualTypeOf<EmailRecipient>()\n })\n})\n\ndescribe(\"Tool.withRun\", () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`sent: ${to}`),\n })\n\n it(\"swaps run while keeping the model-facing descriptor identical\", async () => {\n const dryRun = Tool.withRun(sendEmail, ({ to }) => Effect.succeed(`dry-run: ${to}`))\n\n // Definition (name/description/schema) is byte-identical -> same descriptor.\n expect(Tool.toDescriptors([dryRun])).toEqual(Tool.toDescriptors([sendEmail]))\n // input is typed from the original tool, no annotation needed.\n const out = await Effect.runPromise(dryRun.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(out).toBe(\"dry-run: x@y.z\")\n })\n})\n\ndescribe(\"Tool.decodeArgs\", () => {\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromStandardSchema(emailRecipientSchema),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n const callWith = (args: string): Items.ToolCall => ({\n type: \"function_call\",\n call_id: \"call_1\",\n name: \"send_email\",\n arguments: args,\n })\n\n it(\"decodes valid JSON arguments to the typed input\", async () => {\n const input = await Effect.runPromise(Tool.decodeArgs(sendEmail, callWith('{\"to\":\"x@y.z\"}')))\n expect(input).toEqual({ to: \"x@y.z\" })\n // The return type is the tool's Input, no annotation needed.\n expectTypeOf(input).toEqualTypeOf<EmailRecipient>()\n })\n\n it(\"fails with ToolError when the arguments aren't valid JSON\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith(\"not json\")))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"fails with ToolError when the arguments don't satisfy the schema\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith('{\"to\":42}')))\n expect(exit._tag).toBe(\"Failure\")\n })\n})\n\ndescribe(\"Effect Schema inputSchema\", () => {\n const Recipient = Schema.Struct({ to: Schema.String })\n\n const sendEmail = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Recipient,\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n\n const callWith = (args: string): Items.ToolCall => ({\n type: \"function_call\",\n call_id: \"call_1\",\n name: \"send_email\",\n arguments: args,\n })\n\n it(\"runs with the input inferred from the schema's Type\", async () => {\n const result = await Effect.runPromise(sendEmail.run({ to: \"x@y.z\" }, () => Effect.void))\n expect(result).toBe(\"queued: x@y.z\")\n })\n\n it(\"type: Input is the schema's Type\", () => {\n type InputOf<T> = T extends Tool.Tool<string, infer I, any, any, any> ? I : never\n expectTypeOf<InputOf<typeof sendEmail>>().toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"type: E and R are inferred from run through the Effect Schema overload\", () => {\n class Mailer extends Context.Service<\n Mailer,\n { readonly send: (to: string) => Effect.Effect<string> }\n >()(\"test/Mailer\") {}\n\n const tool = Tool.make({\n name: \"send_email\",\n description: \"send\",\n inputSchema: Recipient,\n run: ({ to }) =>\n Effect.gen(function* () {\n const mailer = yield* Mailer\n if (to === \"\") return yield* Effect.fail(\"empty\" as const)\n return yield* mailer.send(to)\n }),\n })\n\n expectTypeOf<Tool.ToolE<typeof tool>>().toEqualTypeOf<\"empty\">()\n expectTypeOf<Tool.ToolR<typeof tool>>().toEqualTypeOf<Mailer>()\n })\n\n it(\"decodes valid JSON arguments to the typed input\", async () => {\n const input = await Effect.runPromise(Tool.decodeArgs(sendEmail, callWith('{\"to\":\"x@y.z\"}')))\n expect(input).toEqual({ to: \"x@y.z\" })\n expectTypeOf(input).toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"fails with ToolValidationError when the arguments don't satisfy the schema\", async () => {\n const exit = await Effect.runPromiseExit(Tool.decodeArgs(sendEmail, callWith('{\"to\":42}')))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"renders a JSON Schema descriptor from the wrapped schema\", () => {\n const [descriptor] = Tool.toDescriptors([sendEmail])\n expect(descriptor?.name).toBe(\"send_email\")\n expect(descriptor?.inputSchema).toMatchObject({\n type: \"object\",\n properties: { to: { type: \"string\" } },\n required: [\"to\"],\n })\n })\n\n it(\"accepts an already-wrapped fromEffectSchema value unchanged\", async () => {\n const wrapped = Tool.make({\n name: \"send_email\",\n description: \"Send an email to a single recipient.\",\n inputSchema: Tool.fromEffectSchema(Recipient),\n run: ({ to }) => Effect.succeed(`queued: ${to}`),\n })\n expect(Tool.toDescriptors([wrapped])).toEqual(Tool.toDescriptors([sendEmail]))\n const input = await Effect.runPromise(Tool.decodeArgs(wrapped, callWith('{\"to\":\"x@y.z\"}')))\n expectTypeOf(input).toEqualTypeOf<{ readonly to: string }>()\n })\n\n it(\"signal: decode-only kinds accept an Effect Schema directly\", async () => {\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"Escalate to a human.\",\n inputSchema: Schema.Struct({ reason: Schema.String }),\n })\n const input = await Effect.runPromise(\n Tool.decodeArgs(escalate, {\n type: \"function_call\",\n call_id: \"call_2\",\n name: \"escalate\",\n arguments: '{\"reason\":\"stuck\"}',\n }),\n )\n expect(input).toEqual({ reason: \"stuck\" })\n expectTypeOf(input).toEqualTypeOf<{ readonly reason: string }>()\n })\n})\n\ndescribe(\"provider-hosted tools\", () => {\n const localTool = Tool.make({\n name: \"send_email\",\n description: \"Send an email.\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ to: Schema.String })),\n run: ({ to }) => Effect.succeed(`sent: ${to}`),\n })\n const signalTool = Tool.signal({\n name: \"escalate\",\n description: \"Escalate to a human.\",\n inputSchema: Schema.Struct({ reason: Schema.String }),\n })\n const hostedTool = Tool.provider({\n name: \"web_search\",\n description: \"Search the web.\",\n inputSchema: Tool.noInput,\n provider: \"gemini\",\n config: { kind: \"google_search\" },\n })\n const toolkit = { send_email: localTool, escalate: signalTool, web_search: hostedTool }\n\n it(\"descriptorsOf excludes provider-hosted tools\", () => {\n const descriptors = Tool.descriptorsOf(toolkit)\n expect(descriptors.map((d) => d.name)).toEqual([\"send_email\", \"escalate\"])\n })\n\n it(\"toDescriptors drops a ProviderTool from a mixed array\", () => {\n expect(Tool.toDescriptors([localTool, hostedTool]).map((d) => d.name)).toEqual([\"send_email\"])\n })\n\n it(\"providerToolsOf returns only the provider-hosted tools\", () => {\n const provider = Tool.providerToolsOf(toolkit)\n expect(provider).toHaveLength(1)\n expect(provider[0]?.name).toBe(\"web_search\")\n expect(provider[0]?.provider).toBe(\"gemini\")\n expect(provider[0]?.config).toEqual({ kind: \"google_search\" })\n })\n\n it(\"providerToolsOf on an absent toolkit is empty\", () => {\n expect(Tool.providerToolsOf(undefined)).toEqual([])\n })\n\n it(\"isProviderTool narrows the tool kind\", () => {\n expect(Tool.isProviderTool(hostedTool)).toBe(true)\n expect(Tool.isProviderTool(localTool)).toBe(false)\n expect(Tool.isProviderTool(signalTool)).toBe(false)\n })\n})\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { a as isProgress, i as isOutput, r as isApprovalRequested, t as ToolEvent } from "../ToolEvent-BBfOdhF_.mjs";
1
+ import { a as isProgress, i as isOutput, r as isApprovalRequested, t as ToolEvent } from "../ToolEvent-D99MGQYH.mjs";
2
2
  export { ToolEvent, isApprovalRequested, isOutput, isProgress };
@@ -1,4 +1,4 @@
1
- import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
1
+ import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
2
  import { Data } from "effect";
3
3
  //#region src/tool/ToolEvent.ts
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ToolEvent.mjs","names":[],"sources":["../../src/tool/ToolEvent.ts"],"sourcesContent":["/**\n * The event type emitted while handling tool calls.\n *\n * - ApprovalRequested : gated calls waiting for approval\n * - Progress : per-element passthrough from a streaming tool's run\n * - Output : terminal result (carries a structured ToolResult)\n *\n * Recipes thread `ToolEvent.Output.result` through `continueWithResults` and\n * apply `toToolCallOutput` when appending to history.\n *\n * Demux note: `Progress` carries `call_id` / `tool` at the top level, while\n * `Output` nests them inside `result` (the `ToolResult` already carries both).\n * A consumer grouping events by call reads `e.call_id` for `Progress` and\n * `e.result.call_id` for `Output`.\n */\nimport { Data } from \"effect\"\nimport type { ToolResult } from \"./ToolResult.js\"\n\nexport type ToolEvent = Data.TaggedEnum<{\n ApprovalRequested: {\n readonly call_id: string\n readonly tool: string\n readonly arguments: string\n }\n Progress: {\n readonly call_id: string\n readonly tool: string\n readonly data: unknown\n }\n Output: {\n readonly result: ToolResult\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolEvent`,\n * provided by `Data.taggedEnum`. Use `ToolEvent.Output({ result })` to build\n * an event, `ToolEvent.$is(\"Output\")` for type narrowing,\n * `ToolEvent.$match({ ApprovalRequested, Progress, Output })` for\n * exhaustive pattern matching.\n */\nexport const ToolEvent = Data.taggedEnum<ToolEvent>()\n\nexport const isApprovalRequested = ToolEvent.$is(\"ApprovalRequested\")\nexport const isProgress = ToolEvent.$is(\"Progress\")\nexport const isOutput = ToolEvent.$is(\"Output\")\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,YAAY,KAAK,YAAuB;AAErD,MAAa,sBAAsB,UAAU,IAAI,oBAAoB;AACrE,MAAa,aAAa,UAAU,IAAI,WAAW;AACnD,MAAa,WAAW,UAAU,IAAI,SAAS"}
1
+ {"version":3,"file":"ToolEvent.mjs","names":[],"sources":["../../src/tool/ToolEvent.ts"],"sourcesContent":["/**\n * The event type emitted while handling tool calls.\n *\n * - ApprovalRequested : gated calls waiting for approval\n * - Progress : per-element passthrough from a streaming tool's run\n * - Output : terminal result (carries a structured ToolResult)\n *\n * Recipes thread `ToolEvent.Output.result` through `continueWithResults` and\n * apply `toToolCallOutput` when appending to history.\n *\n * Demux note: `Progress` carries `call_id` / `tool` at the top level, while\n * `Output` nests them inside `result` (the `ToolResult` already carries both).\n * A consumer grouping events by call reads `e.call_id` for `Progress` and\n * `e.result.call_id` for `Output`.\n */\nimport { Data } from \"effect\"\nimport type { ToolResult } from \"./ToolResult.js\"\n\nexport type ToolEvent = Data.TaggedEnum<{\n ApprovalRequested: {\n readonly call_id: string\n readonly tool: string\n readonly arguments: string\n }\n Progress: {\n readonly call_id: string\n readonly tool: string\n readonly data: unknown\n }\n Output: {\n readonly result: ToolResult\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolEvent`,\n * provided by `Data.taggedEnum`. Use `ToolEvent.Output({ result })` to build\n * an event, `ToolEvent.$is(\"Output\")` for type narrowing,\n * `ToolEvent.$match({ ApprovalRequested, Progress, Output })` for\n * exhaustive pattern matching.\n */\nexport const ToolEvent = Data.taggedEnum<ToolEvent>()\n\nexport const isApprovalRequested = ToolEvent.$is(\"ApprovalRequested\")\nexport const isProgress = ToolEvent.$is(\"Progress\")\nexport const isOutput = ToolEvent.$is(\"Output\")\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,YAAY,KAAK,WAAsB;AAEpD,MAAa,sBAAsB,UAAU,IAAI,mBAAmB;AACpE,MAAa,aAAa,UAAU,IAAI,UAAU;AAClD,MAAa,WAAW,UAAU,IAAI,QAAQ"}
@@ -1,2 +1,2 @@
1
- import { a as executionError, c as isOk, d as toToolCallOutput, f as validationError, i as denied, l as nonLocalTool, o as failed, r as cancelled, s as isFailure, t as ToolResult, u as serializeValue } from "../ToolResult-CRpmkBXR.mjs";
1
+ import { a as executionError, c as isOk, d as toToolCallOutput, f as validationError, i as denied, l as nonLocalTool, o as failed, r as cancelled, s as isFailure, t as ToolResult, u as serializeValue } from "../ToolResult-Cj8ChodL.mjs";
2
2
  export { ToolResult, cancelled, denied, executionError, failed, isFailure, isOk, nonLocalTool, serializeValue, toToolCallOutput, validationError };
@@ -1,4 +1,4 @@
1
- import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
1
+ import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
2
  import { toolCallOutput } from "../domain/Items.mjs";
3
3
  import { Data } from "effect";
4
4
  //#region src/tool/ToolResult.ts
@@ -1 +1 @@
1
- {"version":3,"file":"ToolResult.mjs","names":[],"sources":["../../src/tool/ToolResult.ts"],"sourcesContent":["/**\n * Post-execution and synthetic tool results.\n *\n * - Executed tools emit ToolResult.Ok.\n * - Approval/cancellation policy emits synthetic ToolResult.Failure.\n *\n * Wire conversion stays at the recipe boundary via `toToolCallOutput`\n * so recipes can inspect, redact, or audit values before serialization.\n *\n * `output` and `reason` are `string`, not `unknown`: the wire wants strings,\n * and `unknown` would invite non-serializable values (Date, Map, BigInt,\n * fn). Recipes that want structured detail JSON.stringify themselves.\n */\nimport { Data } from \"effect\"\nimport type { ToolCall, ToolCallOutput } from \"../domain/Items.js\"\nimport { toolCallOutput } from \"../domain/Items.js\"\n\n// ---------------------------------------------------------------------------\n// ToolResult\n// ---------------------------------------------------------------------------\n\nexport type ToolResult = Data.TaggedEnum<{\n Ok: {\n readonly call_id: string\n readonly tool: string\n readonly value: unknown\n }\n Failure: {\n readonly call_id: string\n readonly tool: string\n readonly kind: string\n readonly reason?: string\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolResult`,\n * provided by `Data.taggedEnum`. Use `ToolResult.$is(\"Ok\")` for type\n * narrowing and `ToolResult.$match({ Ok, Failure })` for exhaustive\n * pattern matching. Synthetic-result helpers (`denied`, `cancelled`,\n * `executionError`, `failed`) below are kinder constructors than the\n * raw `ToolResult.Failure(...)`.\n */\nexport const ToolResult = Data.taggedEnum<ToolResult>()\n\nexport const isOk = ToolResult.$is(\"Ok\")\nexport const isFailure = ToolResult.$is(\"Failure\")\n\n// Synthesizers. `denied` and `cancelled` are operationally distinct;\n// anything else is just a recipe-chosen `kind` via `failed`.\n// ---------------------------------------------------------------------------\n\nexport const failed = (call: ToolCall, kind: string, reason?: string): ToolResult =>\n ToolResult.Failure({\n call_id: call.call_id,\n tool: call.name,\n kind,\n ...(reason !== undefined ? { reason } : {}),\n })\n\n/** Explicit user/policy rejection. */\nexport const denied = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"denied\", reason)\n\n/** Implicit non-answer (follow-up, inactivity, abort). */\nexport const cancelled = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"cancelled\", reason)\n\n/** Tool's own execution failed (JSON parse error, runtime crash). */\nexport const executionError = (call: ToolCall, reason: string): ToolResult =>\n failed(call, \"execution_error\", reason)\n\n/**\n * The model called a tool that is visible to it but has no local executor — a\n * provider-hosted, signal, or interaction tool routed through `Toolkit.run`.\n * Distinct from `unknown_tool` (no such tool at all): the loop is expected to\n * intercept these kinds before execution, so this kind flags a missing handler.\n */\nexport const nonLocalTool = (call: ToolCall, reason?: string): ToolResult =>\n failed(\n call,\n \"non_local_tool\",\n reason ?? `Tool \"${call.name}\" is visible to the model but has no local executor`,\n )\n\n/**\n * The model's `arguments` failed the tool's input schema. Distinct kind from\n * `execution_error` so recipes can tell a contract violation apart from a\n * runtime crash.\n */\nexport const validationError = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"input_validation_error\", reason ?? \"Tool input failed schema validation\")\n\n// ---------------------------------------------------------------------------\n// Wire conversion - the one place structured → string happens.\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a success value for the wire. A `string` passes through raw\n * (models read plain text best; JSON.stringify would quote and escape it);\n * `undefined` becomes `\"null\"`; anything else is JSON.\n */\nexport const serializeValue = (value: unknown): string =>\n typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n\n/** The one failure shape the model sees: `{\"error\":{\"kind\",\"message\"}}`. */\nconst failureBody = (kind: string, message?: string): string =>\n JSON.stringify({ error: message !== undefined ? { kind, message } : { kind } })\n\nexport const toToolCallOutput = (r: ToolResult): ToolCallOutput =>\n ToolResult.$match(r, {\n Ok: (v) => toolCallOutput(v.call_id, serializeValue(v.value)),\n Failure: (f) => toolCallOutput(f.call_id, failureBody(f.kind, f.reason)),\n })\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,aAAa,KAAK,YAAwB;AAEvD,MAAa,OAAO,WAAW,IAAI,KAAK;AACxC,MAAa,YAAY,WAAW,IAAI,UAAU;AAMlD,MAAa,UAAU,MAAgB,MAAc,WACnD,WAAW,QAAQ;CACjB,SAAS,KAAK;CACd,MAAM,KAAK;CACX;CACA,GAAI,WAAW,KAAA,IAAY,EAAE,QAAQ,GAAG,EAAE;CAC3C,CAAC;;AAGJ,MAAa,UAAU,MAAgB,WACrC,OAAO,MAAM,UAAU,OAAO;;AAGhC,MAAa,aAAa,MAAgB,WACxC,OAAO,MAAM,aAAa,OAAO;;AAGnC,MAAa,kBAAkB,MAAgB,WAC7C,OAAO,MAAM,mBAAmB,OAAO;;;;;;;AAQzC,MAAa,gBAAgB,MAAgB,WAC3C,OACE,MACA,kBACA,UAAU,SAAS,KAAK,KAAK,qDAC9B;;;;;;AAOH,MAAa,mBAAmB,MAAgB,WAC9C,OAAO,MAAM,0BAA0B,UAAU,sCAAsC;;;;;;AAWzF,MAAa,kBAAkB,UAC7B,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,KAAK;;AAGnE,MAAM,eAAe,MAAc,YACjC,KAAK,UAAU,EAAE,OAAO,YAAY,KAAA,IAAY;CAAE;CAAM;CAAS,GAAG,EAAE,MAAM,EAAE,CAAC;AAEjF,MAAa,oBAAoB,MAC/B,WAAW,OAAO,GAAG;CACnB,KAAK,MAAM,eAAe,EAAE,SAAS,eAAe,EAAE,MAAM,CAAC;CAC7D,UAAU,MAAM,eAAe,EAAE,SAAS,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC;CACzE,CAAC"}
1
+ {"version":3,"file":"ToolResult.mjs","names":[],"sources":["../../src/tool/ToolResult.ts"],"sourcesContent":["/**\n * Post-execution and synthetic tool results.\n *\n * - Executed tools emit ToolResult.Ok.\n * - Approval/cancellation policy emits synthetic ToolResult.Failure.\n *\n * Wire conversion stays at the recipe boundary via `toToolCallOutput`\n * so recipes can inspect, redact, or audit values before serialization.\n *\n * `output` and `reason` are `string`, not `unknown`: the wire wants strings,\n * and `unknown` would invite non-serializable values (Date, Map, BigInt,\n * fn). Recipes that want structured detail JSON.stringify themselves.\n */\nimport { Data } from \"effect\"\nimport type { ToolCall, ToolCallOutput } from \"../domain/Items.js\"\nimport { toolCallOutput } from \"../domain/Items.js\"\n\n// ---------------------------------------------------------------------------\n// ToolResult\n// ---------------------------------------------------------------------------\n\nexport type ToolResult = Data.TaggedEnum<{\n Ok: {\n readonly call_id: string\n readonly tool: string\n readonly value: unknown\n }\n Failure: {\n readonly call_id: string\n readonly tool: string\n readonly kind: string\n readonly reason?: string\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolResult`,\n * provided by `Data.taggedEnum`. Use `ToolResult.$is(\"Ok\")` for type\n * narrowing and `ToolResult.$match({ Ok, Failure })` for exhaustive\n * pattern matching. Synthetic-result helpers (`denied`, `cancelled`,\n * `executionError`, `failed`) below are kinder constructors than the\n * raw `ToolResult.Failure(...)`.\n */\nexport const ToolResult = Data.taggedEnum<ToolResult>()\n\nexport const isOk = ToolResult.$is(\"Ok\")\nexport const isFailure = ToolResult.$is(\"Failure\")\n\n// Synthesizers. `denied` and `cancelled` are operationally distinct;\n// anything else is just a recipe-chosen `kind` via `failed`.\n// ---------------------------------------------------------------------------\n\nexport const failed = (call: ToolCall, kind: string, reason?: string): ToolResult =>\n ToolResult.Failure({\n call_id: call.call_id,\n tool: call.name,\n kind,\n ...(reason !== undefined ? { reason } : {}),\n })\n\n/** Explicit user/policy rejection. */\nexport const denied = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"denied\", reason)\n\n/** Implicit non-answer (follow-up, inactivity, abort). */\nexport const cancelled = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"cancelled\", reason)\n\n/** Tool's own execution failed (JSON parse error, runtime crash). */\nexport const executionError = (call: ToolCall, reason: string): ToolResult =>\n failed(call, \"execution_error\", reason)\n\n/**\n * The model called a tool that is visible to it but has no local executor — a\n * provider-hosted, signal, or interaction tool routed through `Toolkit.run`.\n * Distinct from `unknown_tool` (no such tool at all): the loop is expected to\n * intercept these kinds before execution, so this kind flags a missing handler.\n */\nexport const nonLocalTool = (call: ToolCall, reason?: string): ToolResult =>\n failed(\n call,\n \"non_local_tool\",\n reason ?? `Tool \"${call.name}\" is visible to the model but has no local executor`,\n )\n\n/**\n * The model's `arguments` failed the tool's input schema. Distinct kind from\n * `execution_error` so recipes can tell a contract violation apart from a\n * runtime crash.\n */\nexport const validationError = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"input_validation_error\", reason ?? \"Tool input failed schema validation\")\n\n// ---------------------------------------------------------------------------\n// Wire conversion - the one place structured → string happens.\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a success value for the wire. A `string` passes through raw\n * (models read plain text best; JSON.stringify would quote and escape it);\n * `undefined` becomes `\"null\"`; anything else is JSON.\n */\nexport const serializeValue = (value: unknown): string =>\n typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n\n/** The one failure shape the model sees: `{\"error\":{\"kind\",\"message\"}}`. */\nconst failureBody = (kind: string, message?: string): string =>\n JSON.stringify({ error: message !== undefined ? { kind, message } : { kind } })\n\nexport const toToolCallOutput = (r: ToolResult): ToolCallOutput =>\n ToolResult.$match(r, {\n Ok: (v) => toolCallOutput(v.call_id, serializeValue(v.value)),\n Failure: (f) => toolCallOutput(f.call_id, failureBody(f.kind, f.reason)),\n })\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,aAAa,KAAK,WAAuB;AAEtD,MAAa,OAAO,WAAW,IAAI,IAAI;AACvC,MAAa,YAAY,WAAW,IAAI,SAAS;AAMjD,MAAa,UAAU,MAAgB,MAAc,WACnD,WAAW,QAAQ;CACjB,SAAS,KAAK;CACd,MAAM,KAAK;CACX;CACA,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;AAC3C,CAAC;;AAGH,MAAa,UAAU,MAAgB,WACrC,OAAO,MAAM,UAAU,MAAM;;AAG/B,MAAa,aAAa,MAAgB,WACxC,OAAO,MAAM,aAAa,MAAM;;AAGlC,MAAa,kBAAkB,MAAgB,WAC7C,OAAO,MAAM,mBAAmB,MAAM;;;;;;;AAQxC,MAAa,gBAAgB,MAAgB,WAC3C,OACE,MACA,kBACA,UAAU,SAAS,KAAK,KAAK,oDAC/B;;;;;;AAOF,MAAa,mBAAmB,MAAgB,WAC9C,OAAO,MAAM,0BAA0B,UAAU,qCAAqC;;;;;;AAWxF,MAAa,kBAAkB,UAC7B,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,IAAI;;AAGlE,MAAM,eAAe,MAAc,YACjC,KAAK,UAAU,EAAE,OAAO,YAAY,KAAA,IAAY;CAAE;CAAM;AAAQ,IAAI,EAAE,KAAK,EAAE,CAAC;AAEhF,MAAa,oBAAoB,MAC/B,WAAW,OAAO,GAAG;CACnB,KAAK,MAAM,eAAe,EAAE,SAAS,eAAe,EAAE,KAAK,CAAC;CAC5D,UAAU,MAAM,eAAe,EAAE,SAAS,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC;AACzE,CAAC"}
@@ -1,2 +1,2 @@
1
- import { S as wrap, _ as fromArray, a as ToolMap, b as namespace, c as ToolkitR, d as appendToolResults, f as collectResults, g as descriptors, h as describeFailures, i as NoDuplicateNames, m as continueWithResults, n as ExecuteOptions, o as Toolkit, p as compose, r as Middleware, s as ToolkitE, t as DuplicateToolName, u as UniqueTools, v as make, x as run, y as makeNamespaced } from "../Toolkit-D4mQvhjq.mjs";
1
+ import { S as wrap, _ as fromArray, a as ToolMap, b as namespace, c as ToolkitR, d as appendToolResults, f as collectResults, g as descriptors, h as describeFailures, i as NoDuplicateNames, m as continueWithResults, n as ExecuteOptions, o as Toolkit, p as compose, r as Middleware, s as ToolkitE, t as DuplicateToolName, u as UniqueTools, v as make, x as run, y as makeNamespaced } from "../Toolkit-BEZrNPTI.mjs";
2
2
  export { DuplicateToolName, ExecuteOptions, Middleware, NoDuplicateNames, ToolMap, Toolkit, ToolkitE, ToolkitR, UniqueTools, appendToolResults, collectResults, compose, continueWithResults, describeFailures, descriptors, fromArray, make, makeNamespaced, namespace, run, wrap };
@@ -1,4 +1,4 @@
1
- import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
1
+ import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
2
  import { ToolResult, failed, nonLocalTool, toToolCallOutput, validationError } from "./ToolResult.mjs";
3
3
  import { ToolFailed, decodeCallInput, toDescriptors } from "./Tool.mjs";
4
4
  import { appendToHistory } from "../domain/Turn.mjs";
@@ -53,10 +53,11 @@ var DuplicateToolName = class extends Data.TaggedError("DuplicateToolName") {};
53
53
  * surfaces from `compose` as a `DuplicateToolName`.
54
54
  */
55
55
  const namespace = (prefix, toolkit) => {
56
- return indexByName(Array.map(Record.values(toolkit), (tool) => ({
56
+ const renamed = Array.map(Record.values(toolkit), (tool) => ({
57
57
  ...tool,
58
58
  name: `${prefix}__${tool.name}`
59
- })));
59
+ }));
60
+ return indexByName(renamed);
60
61
  };
61
62
  /** `namespace(prefix, make(...tools))` in one step, for static namespaced tools. */
62
63
  const makeNamespaced = (prefix, ...tools) => namespace(prefix, buildStatic(tools));
@@ -1 +1 @@
1
- {"version":3,"file":"Toolkit.mjs","names":["Arr","Loop.value","Loop.next"],"sources":["../../src/tool/Toolkit.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport {\n Array as Arr,\n Cause,\n Data,\n Effect,\n Fiber,\n Function,\n Match,\n Option,\n Queue,\n Record,\n Ref,\n Stream,\n} from \"effect\"\nimport * as Loop from \"../loop/Loop.js\"\nimport type { HistoryItem, ToolCall } from \"../domain/Items.js\"\nimport { appendToHistory, type Turn } from \"../domain/Turn.js\"\nimport {\n type AnyLocalTool,\n type AnyTool,\n type DecodeResult,\n decodeCallInput,\n type Emit,\n type InteractionTool,\n type ProviderTool,\n type SignalTool,\n type Tool,\n type ToolDescriptor,\n type ToolE,\n ToolFailed,\n type ToolR,\n toDescriptors,\n} from \"./Tool.js\"\nimport {\n ToolResult,\n failed,\n nonLocalTool,\n toToolCallOutput,\n validationError,\n} from \"./ToolResult.js\"\nimport { ToolEvent } from \"./ToolEvent.js\"\nimport { isOutput } from \"./ToolEvent.js\"\n\n// ---------------------------------------------------------------------------\n// Toolkit: a name-indexed record of tools — \"what the model sees\". The record\n// gives O(1) dispatch and within-toolkit name-uniqueness for free; descriptors\n// are derived, not stored. Build a static one with `make` (compile-time\n// duplicate-name check), a dynamic one with `fromArray` (trusted single source,\n// last-wins), and combine independent ones with `compose` (the application\n// boundary where cross-source names can clash).\n// ---------------------------------------------------------------------------\n\nexport type ToolMap = Record<string, AnyTool>\n\nexport type Toolkit<Tools extends ToolMap = ToolMap> = Tools\n\nconst indexByName = (tools: ReadonlyArray<AnyTool>): ToolMap =>\n Object.fromEntries(tools.map((tool) => [tool.name, tool]))\n\n/**\n * Index literal tools by their `name`. Duplicate names are a compile error\n * (`UniqueTools`). Names are not otherwise checked: a malformed name is a\n * developer typo in a literal that the provider rejects clearly on first use,\n * and there is no single cross-provider name rule to enforce here anyway.\n */\nconst buildStatic = <Tools extends ReadonlyArray<AnyTool>>(\n tools: Tools,\n): Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }> =>\n indexByName(tools) as Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }>\n\nexport const make = <const Tools extends ReadonlyArray<AnyTool>>(\n ...tools: UniqueTools<Tools>\n): Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }> => buildStatic(tools as unknown as Tools)\n\n/**\n * Build a toolkit from a dynamically-sourced array (MCP server, plugin). Names\n * are not validated (MCP legitimately uses dots / >64 chars — sanitize at the\n * provider adapter); same-named tools resolve last-wins. Cross-source clashes\n * are caught by `compose`.\n */\nexport const fromArray = (tools: ReadonlyArray<AnyTool>): Toolkit => indexByName(tools)\n\n/** Render the provider-facing descriptors for a toolkit on demand. */\nexport const descriptors = (toolkit: Toolkit): ReadonlyArray<ToolDescriptor> =>\n toDescriptors(Object.values(toolkit))\n\n/**\n * Union of every tool's `R` requirements in a toolkit. Surfaced by `run` so the\n * loop's stream type carries the services tools need through to `Effect.provide`.\n *\n * The `ToolMap extends T` guard yields `never` for the wide `Toolkit` type (a\n * toolkit passed through an untyped boundary, where the requirements aren't\n * statically known) instead of `any`, which would otherwise poison the\n * resulting stream's `R`. Concrete toolkits from `make` keep their precise R.\n */\nexport type ToolkitR<T extends Toolkit> = ToolMap extends T ? never : ToolR<T[keyof T]>\n\n/**\n * Union of every tool's `E` error type in a toolkit. `Toolkit.run` reports\n * `Exclude<ToolkitE<T>, string | ToolFailed>` on its stream: the sentinel\n * failures are absorbed into results, everything else propagates typed. The\n * same `ToolMap extends T` guard as `ToolkitR` keeps a wide toolkit at\n * `never` rather than `any`.\n */\nexport type ToolkitE<T extends Toolkit> = ToolMap extends T ? never : ToolE<T[keyof T]>\n\n// ---------------------------------------------------------------------------\n// Uniqueness & composition. `make` rejects duplicate literal names at compile\n// time; `compose` rejects cross-source collisions — at compile time when the\n// names are statically known, and at runtime (`DuplicateToolName`) for dynamic\n// sources whose keys are only `string`. No silent last-wins overwrite, no late\n// provider 400.\n// ---------------------------------------------------------------------------\n\n// --- compile-time duplicate detection --------------------------------------\n\ntype DuplicateName<\n Tools extends ReadonlyArray<AnyTool>,\n Seen extends string = never,\n> = Tools extends readonly [\n infer Head extends AnyTool,\n ...infer Tail extends ReadonlyArray<AnyTool>,\n]\n ? Head[\"name\"] extends Seen\n ? Head[\"name\"]\n : DuplicateName<Tail, Seen | Head[\"name\"]>\n : never\n\ntype DuplicateNameError<Name extends string> = {\n readonly __error: `Duplicate tool name: ${Name}`\n}\n\nexport type NoDuplicateNames<Tools extends ReadonlyArray<AnyTool>> = [\n DuplicateName<Tools>,\n] extends [never]\n ? unknown\n : DuplicateNameError<DuplicateName<Tools>>\n\n/** `Tools` constrained so a repeated literal name is a compile error in `make`. */\nexport type UniqueTools<Tools extends ReadonlyArray<AnyTool>> = Tools & NoDuplicateNames<Tools>\n\n// Cross-toolkit literal-key collision, skipping wide (`string`-keyed, dynamic)\n// toolkits so the compile-time check never false-positives on `fromArray`.\ntype ComposeDuplicateKey<\n Kits extends ReadonlyArray<Toolkit>,\n Seen extends string = never,\n> = Kits extends readonly [infer Head extends Toolkit, ...infer Tail extends ReadonlyArray<Toolkit>]\n ? string extends keyof Head\n ? ComposeDuplicateKey<Tail, Seen>\n : Extract<keyof Head & string, Seen> extends never\n ? ComposeDuplicateKey<Tail, Seen | (keyof Head & string)>\n : Extract<keyof Head & string, Seen>\n : never\n\ntype NoDuplicateComposedKeys<Kits extends ReadonlyArray<Toolkit>> = [\n ComposeDuplicateKey<Kits>,\n] extends [never]\n ? unknown\n : DuplicateNameError<ComposeDuplicateKey<Kits>>\n\ntype UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (\n x: infer I,\n) => void\n ? I\n : never\n\ntype Composed<Kits extends ReadonlyArray<Toolkit>> =\n UnionToIntersection<Kits[number]> extends infer M ? (M extends ToolMap ? M : ToolMap) : ToolMap\n\n// --- uniqueness error ------------------------------------------------------\n\n/**\n * Two composed toolkits resolved to the same final name. Carries the colliding\n * `sources` (the positions of the clashing toolkits) so the error says *which*\n * inputs collided. Raised at runtime by `compose` for dynamic sources; static\n * collisions are caught earlier at compile time.\n */\nexport class DuplicateToolName extends Data.TaggedError(\"DuplicateToolName\")<{\n readonly name: string\n readonly sources: ReadonlyArray<string>\n}> {}\n\n// --- namespacing -----------------------------------------------------------\n\ntype PrefixName<Prefix extends string, Name extends string> = `${Prefix}__${Name}`\n\ntype PrefixTool<Prefix extends string, T extends AnyTool> =\n T extends Tool<infer N, infer I, infer Ev, infer O, infer E, infer R>\n ? Tool<PrefixName<Prefix, N>, I, Ev, O, E, R>\n : T extends ProviderTool<infer N, infer I, infer P, infer C>\n ? ProviderTool<PrefixName<Prefix, N>, I, P, C>\n : T extends SignalTool<infer N, infer I>\n ? SignalTool<PrefixName<Prefix, N>, I>\n : T extends InteractionTool<infer N, infer I>\n ? InteractionTool<PrefixName<Prefix, N>, I>\n : T\n\ntype Namespaced<Prefix extends string, T extends ToolMap> = {\n readonly [K in keyof T & string as PrefixName<Prefix, K>]: T[K] extends AnyTool\n ? PrefixTool<Prefix, T[K]>\n : never\n}\n\n/**\n * Prefix every tool name with `<namespace>__`, returning a new toolkit. Use it\n * to keep generic names (`search`) from two sources distinct before `compose`.\n * If two namespaces collide at the `__` separator, the duplicate final name\n * surfaces from `compose` as a `DuplicateToolName`.\n */\nexport const namespace = <const Prefix extends string, const T extends ToolMap>(\n prefix: Prefix,\n toolkit: Toolkit<T>,\n): Toolkit<Namespaced<Prefix, T>> => {\n const renamed = Arr.map(Record.values(toolkit), (tool) => ({\n ...tool,\n name: `${prefix}__${tool.name}`,\n }))\n return indexByName(renamed) as Toolkit<Namespaced<Prefix, T>>\n}\n\n/** `namespace(prefix, make(...tools))` in one step, for static namespaced tools. */\nexport const makeNamespaced = <\n const Prefix extends string,\n const Tools extends ReadonlyArray<AnyTool>,\n>(\n prefix: Prefix,\n ...tools: UniqueTools<Tools>\n): Toolkit<Namespaced<Prefix, { [T in Tools[number] as T[\"name\"]]: T }>> =>\n namespace(prefix, buildStatic(tools as unknown as Tools))\n\n// --- composition (the application boundary) --------------------------------\n\n/**\n * Combine independently-built toolkits into one. This is where names from\n * separate sources can collide, so it is the one effectful constructor: a\n * duplicate final name fails with `DuplicateToolName` (naming the colliding\n * input positions) instead of silently overwriting or 400-ing later at the\n * provider. Statically-known collisions are additionally a compile error.\n */\nexport const compose = <const Kits extends ReadonlyArray<Toolkit>>(\n ...toolkits: Kits & NoDuplicateComposedKeys<Kits>\n): Effect.Effect<Toolkit<Composed<Kits>>, DuplicateToolName> =>\n Effect.gen(function* () {\n const kits = toolkits as unknown as ReadonlyArray<Toolkit>\n const labelled = Arr.flatMap(kits, (kit, i) =>\n Arr.map(Record.keys(kit), (name) => ({ name, source: `toolkit-${i}` })),\n )\n const collision = Arr.findFirst(\n Record.toEntries(Arr.groupBy(labelled, (entry) => entry.name)),\n ([, group]) => group.length > 1,\n )\n if (Option.isSome(collision)) {\n const [name, group] = collision.value\n return yield* new DuplicateToolName({ name, sources: Arr.map(group, (e) => e.source) })\n }\n return Arr.reduce(kits, {} as ToolMap, (acc, kit) => ({ ...acc, ...kit })) as Toolkit<\n Composed<Kits>\n >\n })\n\n// ---------------------------------------------------------------------------\n// Middleware: a `Toolkit -> Toolkit` transform applied up front, then the\n// wrapped toolkit is run. A middleware transforms one local tool's `run` given\n// its name (logging, retry, auth, metrics); `wrap` lifts it over the whole\n// toolkit, leaving provider/signal/interaction kinds untouched. The one reason\n// this is a combinator rather than native `Record.map`: it tracks the\n// middleware's added requirement `R2`, unioning it into `ToolkitR` instead of\n// widening every tool's type.\n// ---------------------------------------------------------------------------\n\ntype Handler<Input, Event, Output, E, R> = (\n input: Input,\n emit: Emit<Event>,\n) => Effect.Effect<Output, E, R>\n\n/**\n * A `run` transform, tracking an added requirement `R2` and optional added\n * error `E2`. The tool's own `E` is preserved; `describeFailures` is the\n * combinator that *replaces* it (mapping every error to `string`).\n */\nexport type Middleware<R2 = never, E2 = never> = <Input, Event, Output, E, R>(\n run: Handler<Input, Event, Output, E, R>,\n name: string,\n) => Handler<Input, Event, Output, E | E2, R | R2>\n\ntype WrapTool<T extends AnyTool, R2, E2> =\n T extends Tool<infer N, infer I, infer Ev, infer O, infer E, infer R>\n ? Tool<N, I, Ev, O, E | E2, R | R2>\n : T\n\ntype WrapToolkit<T extends ToolMap, R2, E2> = {\n readonly [K in keyof T]: T[K] extends AnyTool ? WrapTool<T[K], R2, E2> : T[K]\n}\n\n/**\n * Wrap every local tool's `run` with a middleware, preserving each tool's\n * `Input`/`Output` and unioning the middleware's requirement `R2` into the\n * toolkit's `ToolkitR`. Non-local kinds pass through unchanged. Stack with\n * `pipe` - inner-first, so the last `wrap` applied is the outermost and runs\n * first:\n *\n * const observed = pipe(toolkit, Toolkit.wrap(logging), Toolkit.wrap(authz))\n * Toolkit.run(observed, calls) // authz runs, then logging, then the tool\n *\n * A middleware that fails with `string` / `ToolFailed` is absorbed by the\n * executor into a `ToolResult.Failure`; any other error propagates typed. Use\n * `describeFailures` for the total \"everything to the model\" case, or the\n * `Approval` flow for a distinct \"denied\" verdict.\n */\nexport const wrap: {\n <R2, E2>(\n middleware: Middleware<R2, E2>,\n ): <T extends Toolkit>(toolkit: T) => WrapToolkit<T, R2, E2>\n <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2>\n} = Function.dual(\n 2,\n <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2> =>\n Record.map(toolkit, (tool) =>\n tool._tag === \"LocalTool\" ? { ...tool, run: middleware(tool.run, tool.name) } : tool,\n ) as WrapToolkit<T, R2, E2>,\n)\n\n// ---------------------------------------------------------------------------\n// describeFailures: map every local tool's error to a model-visible string,\n// opting its failures into recovery. Group tools by recovery policy, wrap\n// each group, then compose.\n// ---------------------------------------------------------------------------\n\ntype DescribeTool<T extends AnyTool> =\n T extends Tool<infer N, infer I, infer Ev, infer O, any, infer R>\n ? Tool<N, I, Ev, O, string, R>\n : T\n\ntype DescribeToolkit<T extends ToolMap> = {\n readonly [K in keyof T]: T[K] extends AnyTool ? DescribeTool<T[K]> : T[K]\n}\n\n/**\n * Map every local tool's error to a `string`, so `Toolkit.run` absorbs those\n * failures into `ToolResult.Failure` the model reads and adapts to. Pairs\n * with the domain `describe` helpers (`AiError.describe`,\n * `BrowserError.describe`). Non-local kinds pass through.\n *\n * const kit = yield* Toolkit.compose(\n * Toolkit.describeFailures(searchKit, AiError.describe),\n * browserToolkit(session), // failures stay typed\n * )\n */\nexport const describeFailures: {\n <E>(describe: (e: E) => string): <T extends Toolkit>(toolkit: T) => DescribeToolkit<T>\n <T extends Toolkit>(toolkit: T, describe: (e: ToolkitE<T>) => string): DescribeToolkit<T>\n} = Function.dual(\n 2,\n <T extends Toolkit>(toolkit: T, describe: (e: any) => string): DescribeToolkit<T> =>\n Record.map(toolkit, (tool) =>\n tool._tag === \"LocalTool\"\n ? {\n ...tool,\n run: (input: unknown, emit: Emit<unknown>) =>\n (tool as AnyLocalTool).run(input, emit).pipe(Effect.mapError(describe)),\n }\n : tool,\n ) as DescribeToolkit<T>,\n)\n\n// ---------------------------------------------------------------------------\n// Tool executor. Streams `ToolEvent`s in real time. Policy stays outside this\n// module: callers pass only the calls they have already decided should run.\n// ---------------------------------------------------------------------------\n\nexport type ExecuteOptions = {\n readonly concurrency?: number | \"unbounded\"\n}\n\n/**\n * Execute every provided call. Approval/rejection policy belongs upstream.\n *\n * String and `ToolFailed` failures are absorbed into `ToolResult.Failure`\n * (the model reads and adapts to them); every other tool error propagates\n * typed on the stream, so `Exclude<ToolkitE<T>, string | ToolFailed>` is\n * what a caller catches or lets end the run. If one call in a concurrent\n * batch fails the stream, siblings may go unanswered: a loop that catches\n * and continues must reconcile history first (`HistoryCheck.cancelAllPending`).\n */\nexport const run = <T extends Toolkit>(\n toolkit: T,\n calls: ReadonlyArray<ToolCall>,\n options?: ExecuteOptions,\n): Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>> =>\n Stream.fromIterable(calls).pipe(\n Stream.flatMap((call) => runOne(toolkit, call), {\n concurrency: options?.concurrency ?? \"unbounded\",\n }),\n ) as Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>>\n\nconst okResult = (call: ToolCall, tool: string, value: unknown): ToolResult =>\n ToolResult.Ok({ call_id: call.call_id, tool, value })\n\nconst outputEvent = (result: ToolResult): Stream.Stream<ToolEvent, never, any> =>\n Stream.succeed(ToolEvent.Output({ result }))\n\nconst runOne = (toolkit: ToolMap, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> => {\n // Own-property lookup: a hallucinated call named `constructor` / `toString`\n // must not resolve to a prototype method.\n const tool = Object.hasOwn(toolkit, call.name) ? toolkit[call.name] : undefined\n return Match.value(tool).pipe(\n // No such tool. Graceful: emit a synthetic Failure so OTHER calls in this\n // turn still execute. LLMs hallucinate tool names; MCP tools come and go.\n Match.when(Match.undefined, () =>\n outputEvent(failed(call, \"unknown_tool\", `No tool registered with name \"${call.name}\"`)),\n ),\n Match.tag(\"LocalTool\", (local) => runTool(local, call)),\n // Provider/signal/interaction tools are model-visible but not locally\n // executable. The loop is expected to intercept these (decode + act) before\n // execution; reaching here means it didn't, so report it rather than crash.\n Match.orElse(() => outputEvent(nonLocalTool(call))),\n )\n}\n\nconst describeIssues = (issues: ReadonlyArray<StandardSchemaV1.Issue>): string =>\n issues\n .map((issue) => {\n const path = (issue.path ?? [])\n .map((seg) => (typeof seg === \"object\" && seg !== null ? String(seg.key) : String(seg)))\n .join(\".\")\n return path === \"\" ? issue.message : `${path}: ${issue.message}`\n })\n .join(\"; \")\n .slice(0, 500)\n\n// A decode failure is the model's contract violation: report it as a result\n// (never a stream failure) so the model can correct its next call.\nconst runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> =>\n Stream.unwrap(\n decodeCallInput(tool, call).pipe(\n Effect.map(\n Match.type<DecodeResult<unknown>>().pipe(\n Match.tag(\"parseError\", () =>\n outputEvent(validationError(call, \"arguments are not valid JSON\")),\n ),\n Match.tag(\"invalid\", ({ issues }) =>\n outputEvent(validationError(call, describeIssues(issues))),\n ),\n Match.tag(\"ok\", ({ input }) => runValidated(tool, call, input)),\n Match.exhaustive,\n ),\n ),\n ),\n )\n\nconst runValidated = (\n tool: AnyLocalTool,\n call: ToolCall,\n input: unknown,\n): Stream.Stream<ToolEvent, unknown, any> =>\n Stream.unwrap(\n Effect.gen(function* () {\n // Per-call queue: `emit` offers events as `run` produces them; the\n // progress stream drains them in real time; `Queue.end` (in the\n // `ensuring`) flushes pending events then signals a clean end, which\n // `Stream.fromQueue` treats as completion. `Queue.shutdown` would clear\n // queued items and interrupt pending takes — wrong for graceful teardown.\n const queue = yield* Queue.make<unknown, Cause.Done>({ capacity: tool.emitBufferSize })\n const emit = (event: unknown) => Effect.asVoid(Queue.offer(queue, event))\n const fiber = yield* tool\n .run(input, emit)\n .pipe(Effect.ensuring(Queue.end(queue)), Effect.forkScoped)\n\n const progress = Stream.fromQueue(queue).pipe(\n Stream.map((data) => ToolEvent.Progress({ call_id: call.call_id, tool: tool.name, data })),\n )\n const output = Stream.fromEffect(\n Fiber.join(fiber).pipe(\n Effect.matchCauseEffect({\n onSuccess: (value: unknown) =>\n Effect.succeed(ToolEvent.Output({ result: okResult(call, tool.name, value) })),\n onFailure: (cause: Cause.Cause<unknown>) => routeCause(call, cause),\n }),\n ),\n )\n return progress.pipe(Stream.concat(output))\n }),\n )\n\n// A clean single string / ToolFailed failure is absorbed as a model-visible\n// result; interruption, defects, and typed non-sentinel errors propagate.\nconst routeCause = (\n call: ToolCall,\n cause: Cause.Cause<unknown>,\n): Effect.Effect<ToolEvent, unknown> => {\n const only = cause.reasons.length === 1 ? cause.reasons[0] : undefined\n if (only !== undefined && Cause.isFailReason(only)) {\n const error = only.error\n if (typeof error === \"string\") {\n return Effect.succeed(ToolEvent.Output({ result: failed(call, \"tool_failed\", error) }))\n }\n if (error instanceof ToolFailed) {\n return Effect.succeed(\n ToolEvent.Output({ result: failed(call, error.kind ?? \"tool_failed\", error.message) }),\n )\n }\n }\n return Effect.failCause(cause)\n}\n\n// ---------------------------------------------------------------------------\n// `continueWithResults` - bridge from a `Stream<ToolEvent>` to the loop's\n// emit shape. Drains the stream to the consumer in real-time, taps every\n// `Output` into an internal Ref, and at end-of-stream emits\n// `Loop.next(build(results))`. Recipe never sees the Ref.\n//\n// Dual: data-first `continueWithResults(stream, build)` and data-last\n// `stream.pipe(continueWithResults(build))` both work.\n// ---------------------------------------------------------------------------\n\n/**\n * Append a completed turn plus tool results to a state's history, converting\n * the results to wire-format `ToolCallOutput`s. Curried so it slots directly\n * into `continueWithResults`:\n *\n * Toolkit.run(toolkit, calls).pipe(\n * Toolkit.continueWithResults(Toolkit.appendToolResults(state, turn)),\n * )\n *\n * Equivalent to `appendToHistory(state, turn, results.map(toToolCallOutput))`\n * — the helper just hides the wire-conversion step.\n */\nexport const appendToolResults =\n <S extends { readonly history: ReadonlyArray<HistoryItem> }>(state: S, turn: Turn) =>\n (results: ReadonlyArray<ToolResult>): S =>\n appendToHistory(state, turn, results.map(toToolCallOutput))\n\n/**\n * Drain a `Stream<ToolEvent>` and return the accumulated `ToolResult`s\n * from every `Output` event. One-shot — the type is `Effect<results>`,\n * not a one-element stream — so it composes naturally with `Effect.map`\n * to build state.\n *\n * Counterpart to `continueWithResults`: `continueWithResults` bundles\n * drain + emit Next into one bridge; this one is the right arm of a\n * broadcast/fork-and-merge for callers that want to vary the left arm\n * (e.g., filter/tap ToolEvents before forwarding).\n */\nexport const collectResults = <E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n): Effect.Effect<ReadonlyArray<ToolResult>, E, R> =>\n stream.pipe(\n Stream.filter(isOutput),\n Stream.map((e) => e.result),\n Stream.runCollect,\n )\n\n/**\n * Bridge from a `Stream<ToolEvent>` to the loop's emit shape. Forwards\n * every ToolEvent downstream as a `Loop.value` and at end-of-stream emits\n * one `Loop.next(build(results))` carrying the accumulated `ToolResult`s\n * from terminal `Output` events.\n *\n * Conceptually a broadcast fork (one arm passes events through, the other\n * drains them into a state). Implemented as a single-pass Ref tap + concat\n * for zero buffering — the broadcast version is observationally equivalent\n * but holds events in a PubSub until both arms drain. The public primitives\n * (`Loop.value`, `collectResults`, `Loop.next`) compose the same pattern\n * when you need to vary an arm.\n *\n * Dual: data-first `continueWithResults(stream, build)` and data-last\n * `stream.pipe(continueWithResults(build))` both work.\n */\nexport const continueWithResults: {\n <S>(\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): <E, R>(stream: Stream.Stream<ToolEvent, E, R>) => Stream.Stream<Loop.Step<ToolEvent, S>, E, R>\n <S, E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R>\n} = Function.dual(\n 2,\n <S, E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const ref = yield* Ref.make<ReadonlyArray<ToolResult>>([])\n const tapped = stream.pipe(\n Stream.tap((e) =>\n isOutput(e) ? Ref.update(ref, (acc) => Arr.append(acc, e.result)) : Effect.void,\n ),\n Stream.map(Loop.value),\n )\n const continuation = Stream.unwrap(\n Ref.get(ref).pipe(Effect.map((acc) => Loop.next(build(acc)))),\n )\n return tapped.pipe(Stream.concat(continuation))\n }),\n ),\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"Toolkit.mjs","names":["Arr","Loop.value","Loop.next"],"sources":["../../src/tool/Toolkit.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport {\n Array as Arr,\n Cause,\n Data,\n Effect,\n Fiber,\n Function,\n Match,\n Option,\n Queue,\n Record,\n Ref,\n Stream,\n} from \"effect\"\nimport * as Loop from \"../loop/Loop.js\"\nimport type { HistoryItem, ToolCall } from \"../domain/Items.js\"\nimport { appendToHistory, type Turn } from \"../domain/Turn.js\"\nimport {\n type AnyLocalTool,\n type AnyTool,\n type DecodeResult,\n decodeCallInput,\n type Emit,\n type InteractionTool,\n type ProviderTool,\n type SignalTool,\n type Tool,\n type ToolDescriptor,\n type ToolE,\n ToolFailed,\n type ToolR,\n toDescriptors,\n} from \"./Tool.js\"\nimport {\n ToolResult,\n failed,\n nonLocalTool,\n toToolCallOutput,\n validationError,\n} from \"./ToolResult.js\"\nimport { ToolEvent } from \"./ToolEvent.js\"\nimport { isOutput } from \"./ToolEvent.js\"\n\n// ---------------------------------------------------------------------------\n// Toolkit: a name-indexed record of tools — \"what the model sees\". The record\n// gives O(1) dispatch and within-toolkit name-uniqueness for free; descriptors\n// are derived, not stored. Build a static one with `make` (compile-time\n// duplicate-name check), a dynamic one with `fromArray` (trusted single source,\n// last-wins), and combine independent ones with `compose` (the application\n// boundary where cross-source names can clash).\n// ---------------------------------------------------------------------------\n\nexport type ToolMap = Record<string, AnyTool>\n\nexport type Toolkit<Tools extends ToolMap = ToolMap> = Tools\n\nconst indexByName = (tools: ReadonlyArray<AnyTool>): ToolMap =>\n Object.fromEntries(tools.map((tool) => [tool.name, tool]))\n\n/**\n * Index literal tools by their `name`. Duplicate names are a compile error\n * (`UniqueTools`). Names are not otherwise checked: a malformed name is a\n * developer typo in a literal that the provider rejects clearly on first use,\n * and there is no single cross-provider name rule to enforce here anyway.\n */\nconst buildStatic = <Tools extends ReadonlyArray<AnyTool>>(\n tools: Tools,\n): Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }> =>\n indexByName(tools) as Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }>\n\nexport const make = <const Tools extends ReadonlyArray<AnyTool>>(\n ...tools: UniqueTools<Tools>\n): Toolkit<{ [T in Tools[number] as T[\"name\"]]: T }> => buildStatic(tools as unknown as Tools)\n\n/**\n * Build a toolkit from a dynamically-sourced array (MCP server, plugin). Names\n * are not validated (MCP legitimately uses dots / >64 chars — sanitize at the\n * provider adapter); same-named tools resolve last-wins. Cross-source clashes\n * are caught by `compose`.\n */\nexport const fromArray = (tools: ReadonlyArray<AnyTool>): Toolkit => indexByName(tools)\n\n/** Render the provider-facing descriptors for a toolkit on demand. */\nexport const descriptors = (toolkit: Toolkit): ReadonlyArray<ToolDescriptor> =>\n toDescriptors(Object.values(toolkit))\n\n/**\n * Union of every tool's `R` requirements in a toolkit. Surfaced by `run` so the\n * loop's stream type carries the services tools need through to `Effect.provide`.\n *\n * The `ToolMap extends T` guard yields `never` for the wide `Toolkit` type (a\n * toolkit passed through an untyped boundary, where the requirements aren't\n * statically known) instead of `any`, which would otherwise poison the\n * resulting stream's `R`. Concrete toolkits from `make` keep their precise R.\n */\nexport type ToolkitR<T extends Toolkit> = ToolMap extends T ? never : ToolR<T[keyof T]>\n\n/**\n * Union of every tool's `E` error type in a toolkit. `Toolkit.run` reports\n * `Exclude<ToolkitE<T>, string | ToolFailed>` on its stream: the sentinel\n * failures are absorbed into results, everything else propagates typed. The\n * same `ToolMap extends T` guard as `ToolkitR` keeps a wide toolkit at\n * `never` rather than `any`.\n */\nexport type ToolkitE<T extends Toolkit> = ToolMap extends T ? never : ToolE<T[keyof T]>\n\n// ---------------------------------------------------------------------------\n// Uniqueness & composition. `make` rejects duplicate literal names at compile\n// time; `compose` rejects cross-source collisions — at compile time when the\n// names are statically known, and at runtime (`DuplicateToolName`) for dynamic\n// sources whose keys are only `string`. No silent last-wins overwrite, no late\n// provider 400.\n// ---------------------------------------------------------------------------\n\n// --- compile-time duplicate detection --------------------------------------\n\ntype DuplicateName<\n Tools extends ReadonlyArray<AnyTool>,\n Seen extends string = never,\n> = Tools extends readonly [\n infer Head extends AnyTool,\n ...infer Tail extends ReadonlyArray<AnyTool>,\n]\n ? Head[\"name\"] extends Seen\n ? Head[\"name\"]\n : DuplicateName<Tail, Seen | Head[\"name\"]>\n : never\n\ntype DuplicateNameError<Name extends string> = {\n readonly __error: `Duplicate tool name: ${Name}`\n}\n\nexport type NoDuplicateNames<Tools extends ReadonlyArray<AnyTool>> = [\n DuplicateName<Tools>,\n] extends [never]\n ? unknown\n : DuplicateNameError<DuplicateName<Tools>>\n\n/** `Tools` constrained so a repeated literal name is a compile error in `make`. */\nexport type UniqueTools<Tools extends ReadonlyArray<AnyTool>> = Tools & NoDuplicateNames<Tools>\n\n// Cross-toolkit literal-key collision, skipping wide (`string`-keyed, dynamic)\n// toolkits so the compile-time check never false-positives on `fromArray`.\ntype ComposeDuplicateKey<\n Kits extends ReadonlyArray<Toolkit>,\n Seen extends string = never,\n> = Kits extends readonly [infer Head extends Toolkit, ...infer Tail extends ReadonlyArray<Toolkit>]\n ? string extends keyof Head\n ? ComposeDuplicateKey<Tail, Seen>\n : Extract<keyof Head & string, Seen> extends never\n ? ComposeDuplicateKey<Tail, Seen | (keyof Head & string)>\n : Extract<keyof Head & string, Seen>\n : never\n\ntype NoDuplicateComposedKeys<Kits extends ReadonlyArray<Toolkit>> = [\n ComposeDuplicateKey<Kits>,\n] extends [never]\n ? unknown\n : DuplicateNameError<ComposeDuplicateKey<Kits>>\n\ntype UnionToIntersection<U> = (U extends unknown ? (x: U) => void : never) extends (\n x: infer I,\n) => void\n ? I\n : never\n\ntype Composed<Kits extends ReadonlyArray<Toolkit>> =\n UnionToIntersection<Kits[number]> extends infer M ? (M extends ToolMap ? M : ToolMap) : ToolMap\n\n// --- uniqueness error ------------------------------------------------------\n\n/**\n * Two composed toolkits resolved to the same final name. Carries the colliding\n * `sources` (the positions of the clashing toolkits) so the error says *which*\n * inputs collided. Raised at runtime by `compose` for dynamic sources; static\n * collisions are caught earlier at compile time.\n */\nexport class DuplicateToolName extends Data.TaggedError(\"DuplicateToolName\")<{\n readonly name: string\n readonly sources: ReadonlyArray<string>\n}> {}\n\n// --- namespacing -----------------------------------------------------------\n\ntype PrefixName<Prefix extends string, Name extends string> = `${Prefix}__${Name}`\n\ntype PrefixTool<Prefix extends string, T extends AnyTool> =\n T extends Tool<infer N, infer I, infer Ev, infer O, infer E, infer R>\n ? Tool<PrefixName<Prefix, N>, I, Ev, O, E, R>\n : T extends ProviderTool<infer N, infer I, infer P, infer C>\n ? ProviderTool<PrefixName<Prefix, N>, I, P, C>\n : T extends SignalTool<infer N, infer I>\n ? SignalTool<PrefixName<Prefix, N>, I>\n : T extends InteractionTool<infer N, infer I>\n ? InteractionTool<PrefixName<Prefix, N>, I>\n : T\n\ntype Namespaced<Prefix extends string, T extends ToolMap> = {\n readonly [K in keyof T & string as PrefixName<Prefix, K>]: T[K] extends AnyTool\n ? PrefixTool<Prefix, T[K]>\n : never\n}\n\n/**\n * Prefix every tool name with `<namespace>__`, returning a new toolkit. Use it\n * to keep generic names (`search`) from two sources distinct before `compose`.\n * If two namespaces collide at the `__` separator, the duplicate final name\n * surfaces from `compose` as a `DuplicateToolName`.\n */\nexport const namespace = <const Prefix extends string, const T extends ToolMap>(\n prefix: Prefix,\n toolkit: Toolkit<T>,\n): Toolkit<Namespaced<Prefix, T>> => {\n const renamed = Arr.map(Record.values(toolkit), (tool) => ({\n ...tool,\n name: `${prefix}__${tool.name}`,\n }))\n return indexByName(renamed) as Toolkit<Namespaced<Prefix, T>>\n}\n\n/** `namespace(prefix, make(...tools))` in one step, for static namespaced tools. */\nexport const makeNamespaced = <\n const Prefix extends string,\n const Tools extends ReadonlyArray<AnyTool>,\n>(\n prefix: Prefix,\n ...tools: UniqueTools<Tools>\n): Toolkit<Namespaced<Prefix, { [T in Tools[number] as T[\"name\"]]: T }>> =>\n namespace(prefix, buildStatic(tools as unknown as Tools))\n\n// --- composition (the application boundary) --------------------------------\n\n/**\n * Combine independently-built toolkits into one. This is where names from\n * separate sources can collide, so it is the one effectful constructor: a\n * duplicate final name fails with `DuplicateToolName` (naming the colliding\n * input positions) instead of silently overwriting or 400-ing later at the\n * provider. Statically-known collisions are additionally a compile error.\n */\nexport const compose = <const Kits extends ReadonlyArray<Toolkit>>(\n ...toolkits: Kits & NoDuplicateComposedKeys<Kits>\n): Effect.Effect<Toolkit<Composed<Kits>>, DuplicateToolName> =>\n Effect.gen(function* () {\n const kits = toolkits as unknown as ReadonlyArray<Toolkit>\n const labelled = Arr.flatMap(kits, (kit, i) =>\n Arr.map(Record.keys(kit), (name) => ({ name, source: `toolkit-${i}` })),\n )\n const collision = Arr.findFirst(\n Record.toEntries(Arr.groupBy(labelled, (entry) => entry.name)),\n ([, group]) => group.length > 1,\n )\n if (Option.isSome(collision)) {\n const [name, group] = collision.value\n return yield* new DuplicateToolName({ name, sources: Arr.map(group, (e) => e.source) })\n }\n return Arr.reduce(kits, {} as ToolMap, (acc, kit) => ({ ...acc, ...kit })) as Toolkit<\n Composed<Kits>\n >\n })\n\n// ---------------------------------------------------------------------------\n// Middleware: a `Toolkit -> Toolkit` transform applied up front, then the\n// wrapped toolkit is run. A middleware transforms one local tool's `run` given\n// its name (logging, retry, auth, metrics); `wrap` lifts it over the whole\n// toolkit, leaving provider/signal/interaction kinds untouched. The one reason\n// this is a combinator rather than native `Record.map`: it tracks the\n// middleware's added requirement `R2`, unioning it into `ToolkitR` instead of\n// widening every tool's type.\n// ---------------------------------------------------------------------------\n\ntype Handler<Input, Event, Output, E, R> = (\n input: Input,\n emit: Emit<Event>,\n) => Effect.Effect<Output, E, R>\n\n/**\n * A `run` transform, tracking an added requirement `R2` and optional added\n * error `E2`. The tool's own `E` is preserved; `describeFailures` is the\n * combinator that *replaces* it (mapping every error to `string`).\n */\nexport type Middleware<R2 = never, E2 = never> = <Input, Event, Output, E, R>(\n run: Handler<Input, Event, Output, E, R>,\n name: string,\n) => Handler<Input, Event, Output, E | E2, R | R2>\n\ntype WrapTool<T extends AnyTool, R2, E2> =\n T extends Tool<infer N, infer I, infer Ev, infer O, infer E, infer R>\n ? Tool<N, I, Ev, O, E | E2, R | R2>\n : T\n\ntype WrapToolkit<T extends ToolMap, R2, E2> = {\n readonly [K in keyof T]: T[K] extends AnyTool ? WrapTool<T[K], R2, E2> : T[K]\n}\n\n/**\n * Wrap every local tool's `run` with a middleware, preserving each tool's\n * `Input`/`Output` and unioning the middleware's requirement `R2` into the\n * toolkit's `ToolkitR`. Non-local kinds pass through unchanged. Stack with\n * `pipe` - inner-first, so the last `wrap` applied is the outermost and runs\n * first:\n *\n * const observed = pipe(toolkit, Toolkit.wrap(logging), Toolkit.wrap(authz))\n * Toolkit.run(observed, calls) // authz runs, then logging, then the tool\n *\n * A middleware that fails with `string` / `ToolFailed` is absorbed by the\n * executor into a `ToolResult.Failure`; any other error propagates typed. Use\n * `describeFailures` for the total \"everything to the model\" case, or the\n * `Approval` flow for a distinct \"denied\" verdict.\n */\nexport const wrap: {\n <R2, E2>(\n middleware: Middleware<R2, E2>,\n ): <T extends Toolkit>(toolkit: T) => WrapToolkit<T, R2, E2>\n <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2>\n} = Function.dual(\n 2,\n <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2> =>\n Record.map(toolkit, (tool) =>\n tool._tag === \"LocalTool\" ? { ...tool, run: middleware(tool.run, tool.name) } : tool,\n ) as WrapToolkit<T, R2, E2>,\n)\n\n// ---------------------------------------------------------------------------\n// describeFailures: map every local tool's error to a model-visible string,\n// opting its failures into recovery. Group tools by recovery policy, wrap\n// each group, then compose.\n// ---------------------------------------------------------------------------\n\ntype DescribeTool<T extends AnyTool> =\n T extends Tool<infer N, infer I, infer Ev, infer O, any, infer R>\n ? Tool<N, I, Ev, O, string, R>\n : T\n\ntype DescribeToolkit<T extends ToolMap> = {\n readonly [K in keyof T]: T[K] extends AnyTool ? DescribeTool<T[K]> : T[K]\n}\n\n/**\n * Map every local tool's error to a `string`, so `Toolkit.run` absorbs those\n * failures into `ToolResult.Failure` the model reads and adapts to. Pairs\n * with the domain `describe` helpers (`AiError.describe`,\n * `BrowserError.describe`). Non-local kinds pass through.\n *\n * const kit = yield* Toolkit.compose(\n * Toolkit.describeFailures(searchKit, AiError.describe),\n * browserToolkit(session), // failures stay typed\n * )\n */\nexport const describeFailures: {\n <E>(describe: (e: E) => string): <T extends Toolkit>(toolkit: T) => DescribeToolkit<T>\n <T extends Toolkit>(toolkit: T, describe: (e: ToolkitE<T>) => string): DescribeToolkit<T>\n} = Function.dual(\n 2,\n <T extends Toolkit>(toolkit: T, describe: (e: any) => string): DescribeToolkit<T> =>\n Record.map(toolkit, (tool) =>\n tool._tag === \"LocalTool\"\n ? {\n ...tool,\n run: (input: unknown, emit: Emit<unknown>) =>\n (tool as AnyLocalTool).run(input, emit).pipe(Effect.mapError(describe)),\n }\n : tool,\n ) as DescribeToolkit<T>,\n)\n\n// ---------------------------------------------------------------------------\n// Tool executor. Streams `ToolEvent`s in real time. Policy stays outside this\n// module: callers pass only the calls they have already decided should run.\n// ---------------------------------------------------------------------------\n\nexport type ExecuteOptions = {\n readonly concurrency?: number | \"unbounded\"\n}\n\n/**\n * Execute every provided call. Approval/rejection policy belongs upstream.\n *\n * String and `ToolFailed` failures are absorbed into `ToolResult.Failure`\n * (the model reads and adapts to them); every other tool error propagates\n * typed on the stream, so `Exclude<ToolkitE<T>, string | ToolFailed>` is\n * what a caller catches or lets end the run. If one call in a concurrent\n * batch fails the stream, siblings may go unanswered: a loop that catches\n * and continues must reconcile history first (`HistoryCheck.cancelAllPending`).\n */\nexport const run = <T extends Toolkit>(\n toolkit: T,\n calls: ReadonlyArray<ToolCall>,\n options?: ExecuteOptions,\n): Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>> =>\n Stream.fromIterable(calls).pipe(\n Stream.flatMap((call) => runOne(toolkit, call), {\n concurrency: options?.concurrency ?? \"unbounded\",\n }),\n ) as Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>>\n\nconst okResult = (call: ToolCall, tool: string, value: unknown): ToolResult =>\n ToolResult.Ok({ call_id: call.call_id, tool, value })\n\nconst outputEvent = (result: ToolResult): Stream.Stream<ToolEvent, never, any> =>\n Stream.succeed(ToolEvent.Output({ result }))\n\nconst runOne = (toolkit: ToolMap, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> => {\n // Own-property lookup: a hallucinated call named `constructor` / `toString`\n // must not resolve to a prototype method.\n const tool = Object.hasOwn(toolkit, call.name) ? toolkit[call.name] : undefined\n return Match.value(tool).pipe(\n // No such tool. Graceful: emit a synthetic Failure so OTHER calls in this\n // turn still execute. LLMs hallucinate tool names; MCP tools come and go.\n Match.when(Match.undefined, () =>\n outputEvent(failed(call, \"unknown_tool\", `No tool registered with name \"${call.name}\"`)),\n ),\n Match.tag(\"LocalTool\", (local) => runTool(local, call)),\n // Provider/signal/interaction tools are model-visible but not locally\n // executable. The loop is expected to intercept these (decode + act) before\n // execution; reaching here means it didn't, so report it rather than crash.\n Match.orElse(() => outputEvent(nonLocalTool(call))),\n )\n}\n\nconst describeIssues = (issues: ReadonlyArray<StandardSchemaV1.Issue>): string =>\n issues\n .map((issue) => {\n const path = (issue.path ?? [])\n .map((seg) => (typeof seg === \"object\" && seg !== null ? String(seg.key) : String(seg)))\n .join(\".\")\n return path === \"\" ? issue.message : `${path}: ${issue.message}`\n })\n .join(\"; \")\n .slice(0, 500)\n\n// A decode failure is the model's contract violation: report it as a result\n// (never a stream failure) so the model can correct its next call.\nconst runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> =>\n Stream.unwrap(\n decodeCallInput(tool, call).pipe(\n Effect.map(\n Match.type<DecodeResult<unknown>>().pipe(\n Match.tag(\"parseError\", () =>\n outputEvent(validationError(call, \"arguments are not valid JSON\")),\n ),\n Match.tag(\"invalid\", ({ issues }) =>\n outputEvent(validationError(call, describeIssues(issues))),\n ),\n Match.tag(\"ok\", ({ input }) => runValidated(tool, call, input)),\n Match.exhaustive,\n ),\n ),\n ),\n )\n\nconst runValidated = (\n tool: AnyLocalTool,\n call: ToolCall,\n input: unknown,\n): Stream.Stream<ToolEvent, unknown, any> =>\n Stream.unwrap(\n Effect.gen(function* () {\n // Per-call queue: `emit` offers events as `run` produces them; the\n // progress stream drains them in real time; `Queue.end` (in the\n // `ensuring`) flushes pending events then signals a clean end, which\n // `Stream.fromQueue` treats as completion. `Queue.shutdown` would clear\n // queued items and interrupt pending takes — wrong for graceful teardown.\n const queue = yield* Queue.make<unknown, Cause.Done>({ capacity: tool.emitBufferSize })\n const emit = (event: unknown) => Effect.asVoid(Queue.offer(queue, event))\n const fiber = yield* tool\n .run(input, emit)\n .pipe(Effect.ensuring(Queue.end(queue)), Effect.forkScoped)\n\n const progress = Stream.fromQueue(queue).pipe(\n Stream.map((data) => ToolEvent.Progress({ call_id: call.call_id, tool: tool.name, data })),\n )\n const output = Stream.fromEffect(\n Fiber.join(fiber).pipe(\n Effect.matchCauseEffect({\n onSuccess: (value: unknown) =>\n Effect.succeed(ToolEvent.Output({ result: okResult(call, tool.name, value) })),\n onFailure: (cause: Cause.Cause<unknown>) => routeCause(call, cause),\n }),\n ),\n )\n return progress.pipe(Stream.concat(output))\n }),\n )\n\n// A clean single string / ToolFailed failure is absorbed as a model-visible\n// result; interruption, defects, and typed non-sentinel errors propagate.\nconst routeCause = (\n call: ToolCall,\n cause: Cause.Cause<unknown>,\n): Effect.Effect<ToolEvent, unknown> => {\n const only = cause.reasons.length === 1 ? cause.reasons[0] : undefined\n if (only !== undefined && Cause.isFailReason(only)) {\n const error = only.error\n if (typeof error === \"string\") {\n return Effect.succeed(ToolEvent.Output({ result: failed(call, \"tool_failed\", error) }))\n }\n if (error instanceof ToolFailed) {\n return Effect.succeed(\n ToolEvent.Output({ result: failed(call, error.kind ?? \"tool_failed\", error.message) }),\n )\n }\n }\n return Effect.failCause(cause)\n}\n\n// ---------------------------------------------------------------------------\n// `continueWithResults` - bridge from a `Stream<ToolEvent>` to the loop's\n// emit shape. Drains the stream to the consumer in real-time, taps every\n// `Output` into an internal Ref, and at end-of-stream emits\n// `Loop.next(build(results))`. Recipe never sees the Ref.\n//\n// Dual: data-first `continueWithResults(stream, build)` and data-last\n// `stream.pipe(continueWithResults(build))` both work.\n// ---------------------------------------------------------------------------\n\n/**\n * Append a completed turn plus tool results to a state's history, converting\n * the results to wire-format `ToolCallOutput`s. Curried so it slots directly\n * into `continueWithResults`:\n *\n * Toolkit.run(toolkit, calls).pipe(\n * Toolkit.continueWithResults(Toolkit.appendToolResults(state, turn)),\n * )\n *\n * Equivalent to `appendToHistory(state, turn, results.map(toToolCallOutput))`\n * — the helper just hides the wire-conversion step.\n */\nexport const appendToolResults =\n <S extends { readonly history: ReadonlyArray<HistoryItem> }>(state: S, turn: Turn) =>\n (results: ReadonlyArray<ToolResult>): S =>\n appendToHistory(state, turn, results.map(toToolCallOutput))\n\n/**\n * Drain a `Stream<ToolEvent>` and return the accumulated `ToolResult`s\n * from every `Output` event. One-shot — the type is `Effect<results>`,\n * not a one-element stream — so it composes naturally with `Effect.map`\n * to build state.\n *\n * Counterpart to `continueWithResults`: `continueWithResults` bundles\n * drain + emit Next into one bridge; this one is the right arm of a\n * broadcast/fork-and-merge for callers that want to vary the left arm\n * (e.g., filter/tap ToolEvents before forwarding).\n */\nexport const collectResults = <E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n): Effect.Effect<ReadonlyArray<ToolResult>, E, R> =>\n stream.pipe(\n Stream.filter(isOutput),\n Stream.map((e) => e.result),\n Stream.runCollect,\n )\n\n/**\n * Bridge from a `Stream<ToolEvent>` to the loop's emit shape. Forwards\n * every ToolEvent downstream as a `Loop.value` and at end-of-stream emits\n * one `Loop.next(build(results))` carrying the accumulated `ToolResult`s\n * from terminal `Output` events.\n *\n * Conceptually a broadcast fork (one arm passes events through, the other\n * drains them into a state). Implemented as a single-pass Ref tap + concat\n * for zero buffering — the broadcast version is observationally equivalent\n * but holds events in a PubSub until both arms drain. The public primitives\n * (`Loop.value`, `collectResults`, `Loop.next`) compose the same pattern\n * when you need to vary an arm.\n *\n * Dual: data-first `continueWithResults(stream, build)` and data-last\n * `stream.pipe(continueWithResults(build))` both work.\n */\nexport const continueWithResults: {\n <S>(\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): <E, R>(stream: Stream.Stream<ToolEvent, E, R>) => Stream.Stream<Loop.Step<ToolEvent, S>, E, R>\n <S, E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R>\n} = Function.dual(\n 2,\n <S, E, R>(\n stream: Stream.Stream<ToolEvent, E, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const ref = yield* Ref.make<ReadonlyArray<ToolResult>>([])\n const tapped = stream.pipe(\n Stream.tap((e) =>\n isOutput(e) ? Ref.update(ref, (acc) => Arr.append(acc, e.result)) : Effect.void,\n ),\n Stream.map(Loop.value),\n )\n const continuation = Stream.unwrap(\n Ref.get(ref).pipe(Effect.map((acc) => Loop.next(build(acc)))),\n )\n return tapped.pipe(Stream.concat(continuation))\n }),\n ),\n)\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -3,7 +3,9 @@ import { isFailure, isOk, toToolCallOutput } from "./ToolResult.mjs";
3
3
  import { fail, fromEffectSchema, make, provider, signal, toDescriptors } from "./Tool.mjs";
4
4
  import { isOutput, isProgress } from "./ToolEvent.mjs";
5
5
  import { compose, describeFailures, fromArray, make as make$1, makeNamespaced, namespace, run, wrap } from "./Toolkit.mjs";
6
- import { c as it, n as globalExpect, o as describe$1, t as import_dist } from "../dist-CcRV3-A5.mjs";
6
+ import { m as it, o as describe$1 } from "../utils-CiH_DjFW.mjs";
7
+ import { n as globalExpect } from "../test.DNmyFkvJ-YPzDR0Co.mjs";
8
+ import { t as import_dist } from "../dist-BaenoU1A.mjs";
7
9
  import { Context, Effect, Layer, Schema, Stream, pipe } from "effect";
8
10
  //#region src/tool/Toolkit.test.ts
9
11
  const Empty = Schema.Struct({});
@@ -36,18 +38,20 @@ describe$1("Tool.toDescriptors", () => {
36
38
  });
37
39
  });
38
40
  it("includes strict flag only when set on the tool", () => {
39
- const [s, l] = toDescriptors([make({
41
+ const strictTool = make({
40
42
  name: "strict_one",
41
43
  description: "",
42
44
  inputSchema: fromEffectSchema(GetWeatherInput),
43
45
  run: () => Effect.succeed({}),
44
46
  strict: true
45
- }), make({
47
+ });
48
+ const looseTool = make({
46
49
  name: "loose_one",
47
50
  description: "",
48
51
  inputSchema: fromEffectSchema(GetWeatherInput),
49
52
  run: () => Effect.succeed({})
50
- })]);
53
+ });
54
+ const [s, l] = toDescriptors([strictTool, looseTool]);
51
55
  globalExpect(s?.strict).toBe(true);
52
56
  globalExpect(l).not.toHaveProperty("strict");
53
57
  });
@@ -87,7 +91,8 @@ describe$1("Toolkit.run - tools with R requirements", () => {
87
91
  arguments: "{}"
88
92
  });
89
93
  it("propagates each tool's R into the resulting Stream's requirements", () => {
90
- (0, import_dist.expectTypeOf)(run(make$1(getWeather, getCoords), [])).toEqualTypeOf();
94
+ const stream = run(make$1(getWeather, getCoords), []);
95
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
91
96
  });
92
97
  it("runs each tool with its own service injected", async () => {
93
98
  const layer = Layer.mergeAll(Layer.succeed(WeatherApiKey, { key: "weather-123" }), Layer.succeed(GeoApiKey, { key: "geo-456" }));
@@ -138,7 +143,8 @@ describe$1("Toolkit.run - tools with R requirements", () => {
138
143
  inputSchema: fromEffectSchema(Empty),
139
144
  run: () => Effect.succeed(0)
140
145
  });
141
- (0, import_dist.expectTypeOf)(run(make$1(plain), [])).toEqualTypeOf();
146
+ const stream = run(make$1(plain), []);
147
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
142
148
  });
143
149
  });
144
150
  describe$1("Toolkit.make / compose / namespace - uniqueness", () => {
@@ -150,13 +156,15 @@ describe$1("Toolkit.make / compose / namespace - uniqueness", () => {
150
156
  run: () => Effect.succeed(0)
151
157
  });
152
158
  it("rejects a duplicate literal name at compile time", () => {
153
- make$1(tool("search"), provider({
159
+ const a = tool("search");
160
+ const b = provider({
154
161
  name: "search",
155
162
  description: "",
156
163
  inputSchema: fromEffectSchema(Empty),
157
164
  provider: "demo",
158
165
  config: {}
159
- }));
166
+ });
167
+ make$1(a, b);
160
168
  });
161
169
  it("fails compose with DuplicateToolName naming the colliding sources (dynamic)", async () => {
162
170
  const github = fromArray([tool("search")]);
@@ -177,9 +185,11 @@ describe$1("Toolkit.make / compose / namespace - uniqueness", () => {
177
185
  globalExpect(Object.keys(composed).sort()).toEqual(["github__search", "linear__search"]);
178
186
  });
179
187
  it("preserves each tool's R through compose", () => {
180
- (0, import_dist.expectTypeOf)(Effect.gen(function* () {
181
- return run(yield* compose(make$1(tool("a")), make$1(tool("b"))), []);
182
- })).toEqualTypeOf();
188
+ const stream = Effect.gen(function* () {
189
+ const composed = yield* compose(make$1(tool("a")), make$1(tool("b")));
190
+ return run(composed, []);
191
+ });
192
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
183
193
  });
184
194
  });
185
195
  describe$1("Toolkit.namespace - typed toolkits", () => {
@@ -204,12 +214,14 @@ describe$1("Toolkit.namespace - typed toolkits", () => {
204
214
  (0, import_dist.expectTypeOf)().toEqualTypeOf();
205
215
  });
206
216
  it("type: preserves E and R through makeNamespaced", () => {
207
- (0, import_dist.expectTypeOf)(makeNamespaced("geo", search).geo__search.name).toEqualTypeOf();
217
+ const namespaced = makeNamespaced("geo", search);
218
+ (0, import_dist.expectTypeOf)(namespaced.geo__search.name).toEqualTypeOf();
208
219
  (0, import_dist.expectTypeOf)().toEqualTypeOf();
209
220
  (0, import_dist.expectTypeOf)().toEqualTypeOf();
210
221
  });
211
222
  it("type: Toolkit.run surfaces the namespaced tool's R (string E is absorbed)", () => {
212
- (0, import_dist.expectTypeOf)(run(kit, [])).toEqualTypeOf();
223
+ const stream = run(kit, []);
224
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
213
225
  });
214
226
  it("runtime: renames the tool and executes it under the prefixed name", async () => {
215
227
  const outputs = (await Effect.runPromise(Stream.runCollect(run(kit, [{
@@ -252,7 +264,8 @@ describe$1("Toolkit.wrap - middleware", () => {
252
264
  });
253
265
  it("unions the middleware's R2 into the toolkit's requirements", () => {
254
266
  const wrapped = wrap(make$1(getWeather), audited);
255
- (0, import_dist.expectTypeOf)(run(wrapped, [])).toEqualTypeOf();
267
+ const stream = run(wrapped, []);
268
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
256
269
  });
257
270
  it("runs the wrapped handler and leaves non-local kinds untouched", async () => {
258
271
  const recorded = [];
@@ -311,7 +324,8 @@ describe$1("Toolkit.run - executor outcomes", () => {
311
324
  raw: null
312
325
  }))
313
326
  });
314
- globalExpect((await Effect.runPromiseExit(runToArray(make$1(t), [toolCall("t", "1")])))._tag).toBe("Failure");
327
+ const exit = await Effect.runPromiseExit(runToArray(make$1(t), [toolCall("t", "1")]));
328
+ globalExpect(exit._tag).toBe("Failure");
315
329
  });
316
330
  it("dies on a defect instead of masking it as a result", async () => {
317
331
  const t = make({
@@ -320,7 +334,8 @@ describe$1("Toolkit.run - executor outcomes", () => {
320
334
  inputSchema: fromEffectSchema(Empty),
321
335
  run: () => Effect.die(/* @__PURE__ */ new Error("boom"))
322
336
  });
323
- globalExpect((await Effect.runPromiseExit(runToArray(make$1(t), [toolCall("t", "1")])))._tag).toBe("Failure");
337
+ const exit = await Effect.runPromiseExit(runToArray(make$1(t), [toolCall("t", "1")]));
338
+ globalExpect(exit._tag).toBe("Failure");
324
339
  });
325
340
  it("streams progress events strictly before the terminal output", async () => {
326
341
  const t = make({
@@ -384,10 +399,11 @@ describe$1("Toolkit.run - executor outcomes", () => {
384
399
  });
385
400
  it("preserves call order with concurrency 1", async () => {
386
401
  const kit = make$1(plain("a", "A"), plain("b", "B"));
387
- globalExpect((await Effect.runPromise(runToArray(kit, [toolCall("a", "a1"), toolCall("b", "b1")], { concurrency: 1 }))).filter(isOutput).map((e) => e.result.call_id)).toEqual(["a1", "b1"]);
402
+ const events = await Effect.runPromise(runToArray(kit, [toolCall("a", "a1"), toolCall("b", "b1")], { concurrency: 1 }));
403
+ globalExpect(events.filter(isOutput).map((e) => e.result.call_id)).toEqual(["a1", "b1"]);
388
404
  });
389
405
  it("type: run of a describeFailures toolkit has no stream error", () => {
390
- const kit = describeFailures(make$1(make({
406
+ const t = make({
391
407
  name: "t",
392
408
  description: "",
393
409
  inputSchema: fromEffectSchema(Empty),
@@ -395,8 +411,10 @@ describe$1("Toolkit.run - executor outcomes", () => {
395
411
  provider: "x",
396
412
  raw: null
397
413
  }))
398
- })), describe);
399
- (0, import_dist.expectTypeOf)(run(kit, [])).toEqualTypeOf();
414
+ });
415
+ const kit = describeFailures(make$1(t), describe);
416
+ const stream = run(kit, []);
417
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
400
418
  });
401
419
  it("type: run of a typed toolkit propagates the tool's E", () => {
402
420
  const t = make({
@@ -408,7 +426,8 @@ describe$1("Toolkit.run - executor outcomes", () => {
408
426
  raw: null
409
427
  }))
410
428
  });
411
- (0, import_dist.expectTypeOf)(run(make$1(t), [])).toEqualTypeOf();
429
+ const stream = run(make$1(t), []);
430
+ (0, import_dist.expectTypeOf)(stream).toEqualTypeOf();
412
431
  });
413
432
  });
414
433
  //#endregion