@effect-uai/core 0.10.0 → 0.12.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 (146) hide show
  1. package/dist/{Image-BZmKfIdq.d.mts → Image-C4vuBmu0.d.mts} +11 -11
  2. package/dist/{Image-BZmKfIdq.d.mts.map → Image-C4vuBmu0.d.mts.map} +1 -1
  3. package/dist/{Items-BnIduEV2.d.mts → Items-BKQgqJ8A.d.mts} +136 -97
  4. package/dist/Items-BKQgqJ8A.d.mts.map +1 -0
  5. package/dist/{Tool-2lC_XMth.d.mts → Tool-u5bag8SO.d.mts} +42 -9
  6. package/dist/Tool-u5bag8SO.d.mts.map +1 -0
  7. package/dist/{ToolEvent-cWZ2BnbS.d.mts → ToolEvent-BBfOdhF_.d.mts} +2 -2
  8. package/dist/{ToolEvent-cWZ2BnbS.d.mts.map → ToolEvent-BBfOdhF_.d.mts.map} +1 -1
  9. package/dist/{ToolResult-CL4hK_9z.d.mts → ToolResult-CRpmkBXR.d.mts} +2 -2
  10. package/dist/{ToolResult-CL4hK_9z.d.mts.map → ToolResult-CRpmkBXR.d.mts.map} +1 -1
  11. package/dist/{Toolkit-Dr9l_hNy.d.mts → Toolkit-D4mQvhjq.d.mts} +6 -6
  12. package/dist/Toolkit-D4mQvhjq.d.mts.map +1 -0
  13. package/dist/browser/Browser.mjs +4 -4
  14. package/dist/browser/Browser.mjs.map +1 -1
  15. package/dist/browser/BrowserTool.d.mts +34 -33
  16. package/dist/browser/BrowserTool.d.mts.map +1 -1
  17. package/dist/{dist-DWgRbDN3.mjs → dist-C0m0QnUP.mjs} +71 -30
  18. package/dist/dist-C0m0QnUP.mjs.map +1 -0
  19. package/dist/{dist-B04dlFHg.mjs → dist-CcRV3-A5.mjs} +8 -8
  20. package/dist/{dist-B04dlFHg.mjs.map → dist-CcRV3-A5.mjs.map} +1 -1
  21. package/dist/domain/Citation.d.mts +60 -0
  22. package/dist/domain/Citation.d.mts.map +1 -0
  23. package/dist/domain/Citation.mjs +7 -0
  24. package/dist/domain/Citation.mjs.map +1 -0
  25. package/dist/domain/Image.d.mts +1 -1
  26. package/dist/domain/Items.d.mts +1 -1
  27. package/dist/domain/Items.mjs +28 -4
  28. package/dist/domain/Items.mjs.map +1 -1
  29. package/dist/domain/Research.d.mts +37 -0
  30. package/dist/domain/Research.d.mts.map +1 -0
  31. package/dist/domain/Research.mjs +7 -0
  32. package/dist/domain/Research.mjs.map +1 -0
  33. package/dist/domain/Turn.d.mts +92 -9
  34. package/dist/domain/Turn.d.mts.map +1 -1
  35. package/dist/domain/Turn.mjs +8 -1
  36. package/dist/domain/Turn.mjs.map +1 -1
  37. package/dist/domain/Turn.test.mjs +1 -1
  38. package/dist/embedding-model/Embedding.d.mts +1 -1
  39. package/dist/embedding-model/EmbeddingModel.mjs +2 -2
  40. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
  41. package/dist/embedding-model/EmbeddingModel.test.mjs +1 -1
  42. package/dist/index.d.mts +12 -8
  43. package/dist/index.mjs +5 -1
  44. package/dist/job/Job.d.mts +80 -0
  45. package/dist/job/Job.d.mts.map +1 -0
  46. package/dist/job/Job.mjs +49 -0
  47. package/dist/job/Job.mjs.map +1 -0
  48. package/dist/language-model/LanguageModel.d.mts +2 -2
  49. package/dist/language-model/LanguageModel.mjs +2 -2
  50. package/dist/language-model/LanguageModel.mjs.map +1 -1
  51. package/dist/language-model/LanguageModel.test.mjs +1 -1
  52. package/dist/loop/Loop.test.mjs +1 -1
  53. package/dist/music-generator/MusicGenerator.mjs +4 -4
  54. package/dist/music-generator/MusicGenerator.mjs.map +1 -1
  55. package/dist/music-generator/MusicGenerator.test.mjs +1 -1
  56. package/dist/observability/Metrics.d.mts +13 -5
  57. package/dist/observability/Metrics.d.mts.map +1 -1
  58. package/dist/observability/Metrics.mjs +30 -7
  59. package/dist/observability/Metrics.mjs.map +1 -1
  60. package/dist/observability/Metrics.test.mjs +44 -2
  61. package/dist/observability/Metrics.test.mjs.map +1 -1
  62. package/dist/observability/Telemetry.test.mjs +2 -2
  63. package/dist/research/DeepResearch.d.mts +71 -0
  64. package/dist/research/DeepResearch.d.mts.map +1 -0
  65. package/dist/research/DeepResearch.mjs +61 -0
  66. package/dist/research/DeepResearch.mjs.map +1 -0
  67. package/dist/retry/Retry.d.mts +3 -3
  68. package/dist/retry/Retry.d.mts.map +1 -1
  69. package/dist/retry/Retry.mjs +30 -1
  70. package/dist/retry/Retry.mjs.map +1 -1
  71. package/dist/retry/Retry.test.mjs +1 -1
  72. package/dist/sandbox/Sandbox.mjs +18 -18
  73. package/dist/sandbox/Sandbox.mjs.map +1 -1
  74. package/dist/speech-synthesizer/SpeechSynthesizer.mjs +8 -8
  75. package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
  76. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +1 -1
  77. package/dist/streaming/JSONL.d.mts +3 -1
  78. package/dist/streaming/JSONL.d.mts.map +1 -1
  79. package/dist/streaming/JSONL.mjs +5 -11
  80. package/dist/streaming/JSONL.mjs.map +1 -1
  81. package/dist/streaming/JSONL.test.mjs +10 -1
  82. package/dist/streaming/JSONL.test.mjs.map +1 -1
  83. package/dist/streaming/SSE.d.mts +5 -3
  84. package/dist/streaming/SSE.d.mts.map +1 -1
  85. package/dist/streaming/SSE.mjs +28 -38
  86. package/dist/streaming/SSE.mjs.map +1 -1
  87. package/dist/streaming/SSE.test.mjs +54 -4
  88. package/dist/streaming/SSE.test.mjs.map +1 -1
  89. package/dist/structured-format/StructuredFormat.test.mjs +1 -1
  90. package/dist/testing/MockProvider.d.mts +1 -1
  91. package/dist/testing/MockSandbox.d.mts +1 -1
  92. package/dist/tool/Approval.d.mts +3 -3
  93. package/dist/tool/Approval.test.mjs +1 -1
  94. package/dist/tool/HistoryCheck.d.mts +2 -2
  95. package/dist/tool/Tool.d.mts +2 -2
  96. package/dist/tool/Tool.mjs +27 -7
  97. package/dist/tool/Tool.mjs.map +1 -1
  98. package/dist/tool/Tool.test.mjs +127 -3
  99. package/dist/tool/Tool.test.mjs.map +1 -1
  100. package/dist/tool/ToolEvent.d.mts +1 -1
  101. package/dist/tool/ToolResult.d.mts +1 -1
  102. package/dist/tool/Toolkit.d.mts +1 -1
  103. package/dist/tool/Toolkit.mjs.map +1 -1
  104. package/dist/tool/Toolkit.test.mjs +46 -2
  105. package/dist/tool/Toolkit.test.mjs.map +1 -1
  106. package/dist/transcriber/Transcriber.mjs +3 -3
  107. package/dist/transcriber/Transcriber.mjs.map +1 -1
  108. package/dist/transcriber/Transcriber.test.mjs +1 -1
  109. package/dist/web-read/WebRead.mjs +1 -1
  110. package/dist/web-read/WebRead.mjs.map +1 -1
  111. package/dist/web-read/WebReadTool.d.mts +1 -1
  112. package/dist/web-search/WebSearch.mjs +1 -1
  113. package/dist/web-search/WebSearch.mjs.map +1 -1
  114. package/dist/web-search/WebSearchTool.d.mts +1 -1
  115. package/package.json +19 -3
  116. package/src/browser/Browser.ts +4 -4
  117. package/src/domain/Citation.ts +64 -0
  118. package/src/domain/Items.ts +32 -4
  119. package/src/domain/Research.ts +42 -0
  120. package/src/domain/Turn.ts +26 -0
  121. package/src/embedding-model/EmbeddingModel.ts +2 -2
  122. package/src/index.ts +4 -0
  123. package/src/job/Job.ts +125 -0
  124. package/src/language-model/LanguageModel.ts +2 -2
  125. package/src/music-generator/MusicGenerator.ts +4 -4
  126. package/src/observability/Metrics.test.ts +65 -0
  127. package/src/observability/Metrics.ts +53 -11
  128. package/src/research/DeepResearch.ts +137 -0
  129. package/src/retry/Retry.ts +44 -3
  130. package/src/sandbox/Sandbox.ts +18 -18
  131. package/src/speech-synthesizer/SpeechSynthesizer.ts +8 -8
  132. package/src/streaming/JSONL.test.ts +19 -0
  133. package/src/streaming/JSONL.ts +6 -38
  134. package/src/streaming/SSE.test.ts +86 -3
  135. package/src/streaming/SSE.ts +36 -80
  136. package/src/tool/Tool.test.ts +151 -1
  137. package/src/tool/Tool.ts +120 -22
  138. package/src/tool/Toolkit.test.ts +60 -0
  139. package/src/tool/Toolkit.ts +2 -2
  140. package/src/transcriber/Transcriber.ts +3 -3
  141. package/src/web-read/WebRead.ts +1 -1
  142. package/src/web-search/WebSearch.ts +1 -1
  143. package/dist/Items-BnIduEV2.d.mts.map +0 -1
  144. package/dist/Tool-2lC_XMth.d.mts.map +0 -1
  145. package/dist/Toolkit-Dr9l_hNy.d.mts.map +0 -1
  146. package/dist/dist-DWgRbDN3.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.test.mjs","names":["Tool.fromStandardSchema","Tool.make","Tool.withRun","Tool.toDescriptors","Tool.decodeArgs"],"sources":["../../src/tool/Tool.test.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from \"@standard-schema/spec\"\nimport { Effect } 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"],"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-cWZ2BnbS.mjs";
1
+ import { a as isProgress, i as isOutput, r as isApprovalRequested, t as ToolEvent } from "../ToolEvent-BBfOdhF_.mjs";
2
2
  export { ToolEvent, isApprovalRequested, isOutput, isProgress };
@@ -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-CL4hK_9z.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-CRpmkBXR.mjs";
2
2
  export { ToolResult, cancelled, denied, executionError, failed, isFailure, isOk, nonLocalTool, serializeValue, toToolCallOutput, validationError };
@@ -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-Dr9l_hNy.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-D4mQvhjq.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 +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 R>\n ? Tool<PrefixName<Prefix, N>, I, Ev, O, 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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"}
@@ -2,8 +2,8 @@ import { RateLimited, describe } from "../domain/AiError.mjs";
2
2
  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
- import { compose, describeFailures, fromArray, make as make$1, namespace, run, wrap } from "./Toolkit.mjs";
6
- import { a as describe$1, n as globalExpect, o as it, t as import_dist } from "../dist-B04dlFHg.mjs";
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";
7
7
  import { Context, Effect, Layer, Schema, Stream, pipe } from "effect";
8
8
  //#region src/tool/Toolkit.test.ts
9
9
  const Empty = Schema.Struct({});
@@ -182,6 +182,50 @@ describe$1("Toolkit.make / compose / namespace - uniqueness", () => {
182
182
  })).toEqualTypeOf();
183
183
  });
184
184
  });
185
+ describe$1("Toolkit.namespace - typed toolkits", () => {
186
+ const Query = Schema.Struct({ q: Schema.String });
187
+ class Geo extends Context.Service()("test/Geo") {}
188
+ const search = make({
189
+ name: "search",
190
+ description: "",
191
+ inputSchema: fromEffectSchema(Query),
192
+ run: ({ q }) => Effect.gen(function* () {
193
+ const geo = yield* Geo;
194
+ if (q === "") return yield* Effect.fail("empty_query");
195
+ return yield* geo.lookup(q);
196
+ })
197
+ });
198
+ const kit = namespace("geo", make$1(search));
199
+ it("type: prefixes the name literal of a service-requiring tool", () => {
200
+ (0, import_dist.expectTypeOf)(kit.geo__search.name).toEqualTypeOf();
201
+ });
202
+ it("type: preserves E and R through namespace", () => {
203
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
204
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
205
+ });
206
+ it("type: preserves E and R through makeNamespaced", () => {
207
+ (0, import_dist.expectTypeOf)(makeNamespaced("geo", search).geo__search.name).toEqualTypeOf();
208
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
209
+ (0, import_dist.expectTypeOf)().toEqualTypeOf();
210
+ });
211
+ it("type: Toolkit.run surfaces the namespaced tool's R (string E is absorbed)", () => {
212
+ (0, import_dist.expectTypeOf)(run(kit, [])).toEqualTypeOf();
213
+ });
214
+ it("runtime: renames the tool and executes it under the prefixed name", async () => {
215
+ const outputs = (await Effect.runPromise(Stream.runCollect(run(kit, [{
216
+ type: "function_call",
217
+ call_id: "call_1",
218
+ name: "geo__search",
219
+ arguments: "{\"q\":\"lisbon\"}"
220
+ }])).pipe(Effect.provide(Layer.succeed(Geo, { lookup: (q) => Effect.succeed(`geo:${q}`) }))))).filter(isOutput).map((e) => e.result);
221
+ globalExpect(outputs).toHaveLength(1);
222
+ globalExpect(outputs[0]).toMatchObject({
223
+ _tag: "Ok",
224
+ tool: "geo__search",
225
+ value: "geo:lisbon"
226
+ });
227
+ });
228
+ });
185
229
  describe$1("Toolkit.wrap - middleware", () => {
186
230
  const Empty = Schema.Struct({});
187
231
  class Audit extends Context.Service()("test/Audit") {}
@@ -1 +1 @@
1
- {"version":3,"file":"Toolkit.test.mjs","names":["Toolkit.run","describe","Tool.make","Tool.fromEffectSchema","Tool.toDescriptors","Toolkit.make","Tool.signal","Tool.provider","Toolkit.fromArray","Toolkit.compose","Toolkit.namespace","Toolkit.wrap","Tool.fail","AiError.RateLimited","Toolkit.describeFailures","AiError.describe"],"sources":["../../src/tool/Toolkit.test.ts"],"sourcesContent":["import { Context, Effect, Layer, pipe, Schema, Stream } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { ToolCall } from \"../domain/Items.js\"\nimport { type ToolEvent, isOutput, isProgress } from \"./ToolEvent.js\"\nimport { isFailure, isOk, toToolCallOutput } from \"./ToolResult.js\"\nimport * as Tool from \"./Tool.js\"\nimport * as Toolkit from \"./Toolkit.js\"\n\nconst Empty = Schema.Struct({})\nconst toolCall = (name: string, id: string, args = \"{}\"): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: args,\n})\nconst runToArray = <T extends Toolkit.Toolkit>(\n toolkit: T,\n calls: ReadonlyArray<ToolCall>,\n options?: Toolkit.ExecuteOptions,\n) =>\n Toolkit.run(toolkit, calls, options).pipe(\n Stream.runCollect,\n Effect.map((events): ReadonlyArray<ToolEvent> => Array.from(events)),\n )\n\ndescribe(\"Tool.toDescriptors\", () => {\n const GetWeatherInput = Schema.Struct({ city: Schema.String })\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"Look up the current temperature for a city.\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: ({ city }) => Effect.succeed({ city, tempC: 18 }),\n })\n\n it(\"renders the input schema as a JSON Schema document\", () => {\n const [desc] = Tool.toDescriptors([getWeather])\n expect(desc?.name).toBe(\"get_weather\")\n expect(desc?.description).toBe(\"Look up the current temperature for a city.\")\n expect(desc?.inputSchema).toMatchObject({\n type: \"object\",\n properties: { city: { type: \"string\" } },\n required: [\"city\"],\n })\n })\n\n it(\"includes strict flag only when set on the tool\", () => {\n const strictTool = Tool.make({\n name: \"strict_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n strict: true,\n })\n const looseTool = Tool.make({\n name: \"loose_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n })\n const [s, l] = Tool.toDescriptors([strictTool, looseTool])\n expect(s?.strict).toBe(true)\n expect(l).not.toHaveProperty(\"strict\")\n })\n})\n\ndescribe(\"Toolkit.run - tools with R requirements\", () => {\n // Two distinct services, modelling the \"typed per-tool context\" use case\n // (cf. AI SDK 7's `toolsContext`). In Effect each tool declares its R, the\n // compiler enforces it, and `run` surfaces the union for the caller\n // to provide via Layer.\n type WeatherApiKeyShape = { readonly key: string }\n class WeatherApiKey extends Context.Service<WeatherApiKey, WeatherApiKeyShape>()(\n \"test/WeatherApiKey\",\n ) {}\n\n type GeoApiKeyShape = { readonly key: string }\n class GeoApiKey extends Context.Service<GeoApiKey, GeoApiKeyShape>()(\"test/GeoApiKey\") {}\n\n const Empty = Schema.Struct({})\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* WeatherApiKey\n return { source: \"weather\", key }\n }),\n })\n\n const getCoords = Tool.make({\n name: \"get_coords\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* GeoApiKey\n return { source: \"geo\", key }\n }),\n })\n\n const call = (name: string, id: string): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: \"{}\",\n })\n\n it(\"propagates each tool's R into the resulting Stream's requirements\", () => {\n const stream = Toolkit.run(Toolkit.make(getWeather, getCoords), [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, WeatherApiKey | GeoApiKey>\n >()\n })\n\n it(\"runs each tool with its own service injected\", async () => {\n const layer = Layer.mergeAll(\n Layer.succeed(WeatherApiKey, { key: \"weather-123\" }),\n Layer.succeed(GeoApiKey, { key: \"geo-456\" }),\n )\n\n const program = Toolkit.run(Toolkit.make(getWeather, getCoords), [\n call(\"get_weather\", \"c1\"),\n call(\"get_coords\", \"c2\"),\n ]).pipe(Stream.runCollect, Effect.provide(layer))\n\n const events = await Effect.runPromise(program)\n const outputs = Array.from(events).filter(isOutput)\n const byCall = new Map(outputs.map((e) => [e.result.call_id, e.result]))\n\n const w = byCall.get(\"c1\")\n const g = byCall.get(\"c2\")\n expect(w !== undefined && isOk(w) && w.value).toEqual({\n source: \"weather\",\n key: \"weather-123\",\n })\n expect(g !== undefined && isOk(g) && g.value).toEqual({\n source: \"geo\",\n key: \"geo-456\",\n })\n })\n\n it(\"reports non-local kinds as non_local_tool while local calls in the same turn still run\", async () => {\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n })\n const localPlain = Tool.make({\n name: \"plain\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed({ ok: true }),\n })\n\n const events = await Effect.runPromise(\n Toolkit.run(Toolkit.make(localPlain, escalate), [\n call(\"escalate\", \"s1\"),\n call(\"plain\", \"p1\"),\n call(\"ghost\", \"g1\"),\n ]).pipe(Stream.runCollect),\n )\n const byCall = new Map(\n Array.from(events)\n .filter(isOutput)\n .map((e) => [e.result.call_id, e.result] as const),\n )\n\n // Signal tool: model-visible but no local executor.\n const s = byCall.get(\"s1\")\n expect(s?._tag === \"Failure\" && s.kind).toBe(\"non_local_tool\")\n // Unknown tool stays distinct from non-local.\n const g = byCall.get(\"g1\")\n expect(g?._tag === \"Failure\" && g.kind).toBe(\"unknown_tool\")\n // The local call still executed despite the non-local sibling.\n const p = byCall.get(\"p1\")\n expect(p !== undefined && isOk(p) && p.value).toEqual({ ok: true })\n })\n\n it(\"with no service-needing tools, R is never\", () => {\n const plain = Tool.make({\n name: \"plain\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(0),\n })\n const stream = Toolkit.run(Toolkit.make(plain), [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, never>\n >()\n })\n})\n\ndescribe(\"Toolkit.make / compose / namespace - uniqueness\", () => {\n const Empty = Schema.Struct({})\n const tool = (name: string) =>\n Tool.make({\n name,\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(0),\n })\n\n it(\"rejects a duplicate literal name at compile time\", () => {\n const a = tool(\"search\")\n const b = Tool.provider({\n name: \"search\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n provider: \"demo\",\n config: {},\n })\n // @ts-expect-error Duplicate tool name: search\n Toolkit.make(a, b)\n })\n\n it(\"fails compose with DuplicateToolName naming the colliding sources (dynamic)\", async () => {\n const github = Toolkit.fromArray([tool(\"search\")])\n const linear = Toolkit.fromArray([tool(\"search\")])\n const exit = await Effect.runPromiseExit(Toolkit.compose(github, linear))\n expect(exit._tag).toBe(\"Failure\")\n if (exit._tag === \"Failure\") {\n const json = JSON.stringify(exit.cause)\n expect(json).toContain(\"DuplicateToolName\")\n expect(json).toContain(\"toolkit-0\")\n expect(json).toContain(\"toolkit-1\")\n }\n })\n\n it(\"namespaces names so generic names from two sources compose cleanly\", async () => {\n const github = Toolkit.namespace(\"github\", Toolkit.fromArray([tool(\"search\")]))\n const linear = Toolkit.namespace(\"linear\", Toolkit.fromArray([tool(\"search\")]))\n const composed = await Effect.runPromise(Toolkit.compose(github, linear))\n expect(Object.keys(composed).sort()).toEqual([\"github__search\", \"linear__search\"])\n })\n\n it(\"preserves each tool's R through compose\", () => {\n const stream = Effect.gen(function* () {\n const composed = yield* Toolkit.compose(Toolkit.make(tool(\"a\")), Toolkit.make(tool(\"b\")))\n return Toolkit.run(composed, [])\n })\n // No service-needing tools here -> R stays never (compose didn't poison it).\n expectTypeOf(stream).toEqualTypeOf<\n Effect.Effect<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, never>,\n Toolkit.DuplicateToolName\n >\n >()\n })\n})\n\ndescribe(\"Toolkit.wrap - middleware\", () => {\n const Empty = Schema.Struct({})\n type AuditShape = { readonly record: (name: string) => Effect.Effect<void> }\n class Audit extends Context.Service<Audit, AuditShape>()(\"test/Audit\") {}\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed({ tempC: 18 }),\n })\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n })\n\n // A middleware that needs the Audit service -> R2 = Audit.\n const audited: Toolkit.Middleware<Audit> = (run, name) => (input, emit) =>\n Effect.gen(function* () {\n const a = yield* Audit\n yield* a.record(name)\n return yield* run(input, emit)\n })\n\n const call = (name: string, id: string): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: \"{}\",\n })\n\n it(\"unions the middleware's R2 into the toolkit's requirements\", () => {\n const wrapped = Toolkit.wrap(Toolkit.make(getWeather), audited)\n const stream = Toolkit.run(wrapped, [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, Audit>\n >()\n })\n\n it(\"runs the wrapped handler and leaves non-local kinds untouched\", async () => {\n const recorded: Array<string> = []\n const wrapped = pipe(Toolkit.make(getWeather, escalate), Toolkit.wrap(audited))\n\n const events = await Effect.runPromise(\n Toolkit.run(wrapped, [call(\"get_weather\", \"w1\"), call(\"escalate\", \"s1\")]).pipe(\n Stream.runCollect,\n Effect.provideService(Audit, { record: (n) => Effect.sync(() => void recorded.push(n)) }),\n ),\n )\n const byCall = new Map(\n Array.from(events)\n .filter(isOutput)\n .map((e) => [e.result.call_id, e.result] as const),\n )\n\n // Local tool ran through the middleware (audit fired).\n expect(recorded).toEqual([\"get_weather\"])\n const w = byCall.get(\"w1\")\n expect(w !== undefined && isOk(w) && w.value).toEqual({ tempC: 18 })\n // Signal kind is left alone by wrap -> still reported non-local.\n const s = byCall.get(\"s1\")\n expect(s?._tag === \"Failure\" && s.kind).toBe(\"non_local_tool\")\n })\n})\n\ndescribe(\"Toolkit.run - executor outcomes\", () => {\n const plain = <N extends string>(name: N, out: unknown) =>\n Tool.make({\n name,\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(out),\n })\n\n it(\"absorbs a bare string failure as a tool_failed result\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(\"nope\"),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"tool_failed\", reason: \"nope\" })\n })\n\n it(\"absorbs Tool.fail with a custom kind\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Tool.fail(\"missing\", { kind: \"not_found\" }),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"not_found\", reason: \"missing\" })\n })\n\n it(\"propagates a typed non-sentinel failure on the stream\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const exit = await Effect.runPromiseExit(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"dies on a defect instead of masking it as a result\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.die(new Error(\"boom\")),\n })\n const exit = await Effect.runPromiseExit(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"streams progress events strictly before the terminal output\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: (_input, emit) =>\n emit(\"a\").pipe(Effect.andThen(emit(\"b\")), Effect.andThen(Effect.succeed(\"done\"))),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(events.map((e) => e._tag)).toEqual([\"Progress\", \"Progress\", \"Output\"])\n expect(events.filter(isProgress).map((p) => p.data)).toEqual([\"a\", \"b\"])\n })\n\n it(\"reports input validation failures as a result carrying the issue detail\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ x: Schema.Number })),\n run: () => Effect.succeed(1),\n })\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\", '{\"x\":\"nope\"}')]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"input_validation_error\" })\n expect(result !== undefined && isFailure(result) && result.reason).toBeTruthy()\n })\n\n it(\"normalizes empty arguments to {} for a zero-arg tool\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"t\", \"1\", \"\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Ok\", value: \"ok\" })\n })\n\n it(\"reports unparseable arguments as input_validation_error\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"t\", \"1\", \"not json\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"input_validation_error\" })\n })\n\n it(\"a call named constructor is unknown_tool, not non_local_tool\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"constructor\", \"1\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"unknown_tool\" })\n })\n\n it('serializes an undefined output as \"null\" on the wire', async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.void,\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result !== undefined && toToolCallOutput(result).output).toBe(\"null\")\n })\n\n it(\"preserves call order with concurrency 1\", async () => {\n const kit = Toolkit.make(plain(\"a\", \"A\"), plain(\"b\", \"B\"))\n const events = await Effect.runPromise(\n runToArray(kit, [toolCall(\"a\", \"a1\"), toolCall(\"b\", \"b1\")], { concurrency: 1 }),\n )\n expect(events.filter(isOutput).map((e) => e.result.call_id)).toEqual([\"a1\", \"b1\"])\n })\n\n it(\"type: run of a describeFailures toolkit has no stream error\", () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const kit = Toolkit.describeFailures(Toolkit.make(t), AiError.describe)\n const stream = Toolkit.run(kit, [])\n expectTypeOf(stream).toEqualTypeOf<Stream.Stream<ToolEvent, never, never>>()\n })\n\n it(\"type: run of a typed toolkit propagates the tool's E\", () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const stream = Toolkit.run(Toolkit.make(t), [])\n expectTypeOf(stream).toEqualTypeOf<Stream.Stream<ToolEvent, AiError.RateLimited, never>>()\n })\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"Toolkit.test.mjs","names":["Toolkit.run","describe","Tool.make","Tool.fromEffectSchema","Tool.toDescriptors","Toolkit.make","Tool.signal","Tool.provider","Toolkit.fromArray","Toolkit.compose","Toolkit.namespace","Toolkit.makeNamespaced","Toolkit.wrap","Tool.fail","AiError.RateLimited","Toolkit.describeFailures","AiError.describe"],"sources":["../../src/tool/Toolkit.test.ts"],"sourcesContent":["import { Context, Effect, Layer, pipe, Schema, Stream } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { ToolCall } from \"../domain/Items.js\"\nimport { type ToolEvent, isOutput, isProgress } from \"./ToolEvent.js\"\nimport { isFailure, isOk, toToolCallOutput } from \"./ToolResult.js\"\nimport * as Tool from \"./Tool.js\"\nimport * as Toolkit from \"./Toolkit.js\"\n\nconst Empty = Schema.Struct({})\nconst toolCall = (name: string, id: string, args = \"{}\"): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: args,\n})\nconst runToArray = <T extends Toolkit.Toolkit>(\n toolkit: T,\n calls: ReadonlyArray<ToolCall>,\n options?: Toolkit.ExecuteOptions,\n) =>\n Toolkit.run(toolkit, calls, options).pipe(\n Stream.runCollect,\n Effect.map((events): ReadonlyArray<ToolEvent> => Array.from(events)),\n )\n\ndescribe(\"Tool.toDescriptors\", () => {\n const GetWeatherInput = Schema.Struct({ city: Schema.String })\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"Look up the current temperature for a city.\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: ({ city }) => Effect.succeed({ city, tempC: 18 }),\n })\n\n it(\"renders the input schema as a JSON Schema document\", () => {\n const [desc] = Tool.toDescriptors([getWeather])\n expect(desc?.name).toBe(\"get_weather\")\n expect(desc?.description).toBe(\"Look up the current temperature for a city.\")\n expect(desc?.inputSchema).toMatchObject({\n type: \"object\",\n properties: { city: { type: \"string\" } },\n required: [\"city\"],\n })\n })\n\n it(\"includes strict flag only when set on the tool\", () => {\n const strictTool = Tool.make({\n name: \"strict_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n strict: true,\n })\n const looseTool = Tool.make({\n name: \"loose_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n })\n const [s, l] = Tool.toDescriptors([strictTool, looseTool])\n expect(s?.strict).toBe(true)\n expect(l).not.toHaveProperty(\"strict\")\n })\n})\n\ndescribe(\"Toolkit.run - tools with R requirements\", () => {\n // Two distinct services, modelling the \"typed per-tool context\" use case\n // (cf. AI SDK 7's `toolsContext`). In Effect each tool declares its R, the\n // compiler enforces it, and `run` surfaces the union for the caller\n // to provide via Layer.\n type WeatherApiKeyShape = { readonly key: string }\n class WeatherApiKey extends Context.Service<WeatherApiKey, WeatherApiKeyShape>()(\n \"test/WeatherApiKey\",\n ) {}\n\n type GeoApiKeyShape = { readonly key: string }\n class GeoApiKey extends Context.Service<GeoApiKey, GeoApiKeyShape>()(\"test/GeoApiKey\") {}\n\n const Empty = Schema.Struct({})\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* WeatherApiKey\n return { source: \"weather\", key }\n }),\n })\n\n const getCoords = Tool.make({\n name: \"get_coords\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* GeoApiKey\n return { source: \"geo\", key }\n }),\n })\n\n const call = (name: string, id: string): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: \"{}\",\n })\n\n it(\"propagates each tool's R into the resulting Stream's requirements\", () => {\n const stream = Toolkit.run(Toolkit.make(getWeather, getCoords), [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, WeatherApiKey | GeoApiKey>\n >()\n })\n\n it(\"runs each tool with its own service injected\", async () => {\n const layer = Layer.mergeAll(\n Layer.succeed(WeatherApiKey, { key: \"weather-123\" }),\n Layer.succeed(GeoApiKey, { key: \"geo-456\" }),\n )\n\n const program = Toolkit.run(Toolkit.make(getWeather, getCoords), [\n call(\"get_weather\", \"c1\"),\n call(\"get_coords\", \"c2\"),\n ]).pipe(Stream.runCollect, Effect.provide(layer))\n\n const events = await Effect.runPromise(program)\n const outputs = Array.from(events).filter(isOutput)\n const byCall = new Map(outputs.map((e) => [e.result.call_id, e.result]))\n\n const w = byCall.get(\"c1\")\n const g = byCall.get(\"c2\")\n expect(w !== undefined && isOk(w) && w.value).toEqual({\n source: \"weather\",\n key: \"weather-123\",\n })\n expect(g !== undefined && isOk(g) && g.value).toEqual({\n source: \"geo\",\n key: \"geo-456\",\n })\n })\n\n it(\"reports non-local kinds as non_local_tool while local calls in the same turn still run\", async () => {\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n })\n const localPlain = Tool.make({\n name: \"plain\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed({ ok: true }),\n })\n\n const events = await Effect.runPromise(\n Toolkit.run(Toolkit.make(localPlain, escalate), [\n call(\"escalate\", \"s1\"),\n call(\"plain\", \"p1\"),\n call(\"ghost\", \"g1\"),\n ]).pipe(Stream.runCollect),\n )\n const byCall = new Map(\n Array.from(events)\n .filter(isOutput)\n .map((e) => [e.result.call_id, e.result] as const),\n )\n\n // Signal tool: model-visible but no local executor.\n const s = byCall.get(\"s1\")\n expect(s?._tag === \"Failure\" && s.kind).toBe(\"non_local_tool\")\n // Unknown tool stays distinct from non-local.\n const g = byCall.get(\"g1\")\n expect(g?._tag === \"Failure\" && g.kind).toBe(\"unknown_tool\")\n // The local call still executed despite the non-local sibling.\n const p = byCall.get(\"p1\")\n expect(p !== undefined && isOk(p) && p.value).toEqual({ ok: true })\n })\n\n it(\"with no service-needing tools, R is never\", () => {\n const plain = Tool.make({\n name: \"plain\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(0),\n })\n const stream = Toolkit.run(Toolkit.make(plain), [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, never>\n >()\n })\n})\n\ndescribe(\"Toolkit.make / compose / namespace - uniqueness\", () => {\n const Empty = Schema.Struct({})\n const tool = (name: string) =>\n Tool.make({\n name,\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(0),\n })\n\n it(\"rejects a duplicate literal name at compile time\", () => {\n const a = tool(\"search\")\n const b = Tool.provider({\n name: \"search\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n provider: \"demo\",\n config: {},\n })\n // @ts-expect-error Duplicate tool name: search\n Toolkit.make(a, b)\n })\n\n it(\"fails compose with DuplicateToolName naming the colliding sources (dynamic)\", async () => {\n const github = Toolkit.fromArray([tool(\"search\")])\n const linear = Toolkit.fromArray([tool(\"search\")])\n const exit = await Effect.runPromiseExit(Toolkit.compose(github, linear))\n expect(exit._tag).toBe(\"Failure\")\n if (exit._tag === \"Failure\") {\n const json = JSON.stringify(exit.cause)\n expect(json).toContain(\"DuplicateToolName\")\n expect(json).toContain(\"toolkit-0\")\n expect(json).toContain(\"toolkit-1\")\n }\n })\n\n it(\"namespaces names so generic names from two sources compose cleanly\", async () => {\n const github = Toolkit.namespace(\"github\", Toolkit.fromArray([tool(\"search\")]))\n const linear = Toolkit.namespace(\"linear\", Toolkit.fromArray([tool(\"search\")]))\n const composed = await Effect.runPromise(Toolkit.compose(github, linear))\n expect(Object.keys(composed).sort()).toEqual([\"github__search\", \"linear__search\"])\n })\n\n it(\"preserves each tool's R through compose\", () => {\n const stream = Effect.gen(function* () {\n const composed = yield* Toolkit.compose(Toolkit.make(tool(\"a\")), Toolkit.make(tool(\"b\")))\n return Toolkit.run(composed, [])\n })\n // No service-needing tools here -> R stays never (compose didn't poison it).\n expectTypeOf(stream).toEqualTypeOf<\n Effect.Effect<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, never>,\n Toolkit.DuplicateToolName\n >\n >()\n })\n})\n\ndescribe(\"Toolkit.namespace - typed toolkits\", () => {\n const Query = Schema.Struct({ q: Schema.String })\n type GeoShape = { readonly lookup: (q: string) => Effect.Effect<string> }\n class Geo extends Context.Service<Geo, GeoShape>()(\"test/Geo\") {}\n\n const search = Tool.make({\n name: \"search\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Query),\n run: ({ q }) =>\n Effect.gen(function* () {\n const geo = yield* Geo\n if (q === \"\") return yield* Effect.fail(\"empty_query\" as const)\n return yield* geo.lookup(q)\n }),\n })\n\n const kit = Toolkit.namespace(\"geo\", Toolkit.make(search))\n\n it(\"type: prefixes the name literal of a service-requiring tool\", () => {\n expectTypeOf(kit.geo__search.name).toEqualTypeOf<\"geo__search\">()\n })\n\n it(\"type: preserves E and R through namespace\", () => {\n expectTypeOf<Toolkit.ToolkitE<typeof kit>>().toEqualTypeOf<\"empty_query\">()\n expectTypeOf<Toolkit.ToolkitR<typeof kit>>().toEqualTypeOf<Geo>()\n })\n\n it(\"type: preserves E and R through makeNamespaced\", () => {\n const namespaced = Toolkit.makeNamespaced(\"geo\", search)\n expectTypeOf(namespaced.geo__search.name).toEqualTypeOf<\"geo__search\">()\n expectTypeOf<Toolkit.ToolkitE<typeof namespaced>>().toEqualTypeOf<\"empty_query\">()\n expectTypeOf<Toolkit.ToolkitR<typeof namespaced>>().toEqualTypeOf<Geo>()\n })\n\n it(\"type: Toolkit.run surfaces the namespaced tool's R (string E is absorbed)\", () => {\n const stream = Toolkit.run(kit, [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, Geo>\n >()\n })\n\n it(\"runtime: renames the tool and executes it under the prefixed name\", async () => {\n const call: ToolCall = {\n type: \"function_call\",\n call_id: \"call_1\",\n name: \"geo__search\",\n arguments: '{\"q\":\"lisbon\"}',\n }\n const events = await Effect.runPromise(\n Stream.runCollect(Toolkit.run(kit, [call])).pipe(\n Effect.provide(Layer.succeed(Geo, { lookup: (q) => Effect.succeed(`geo:${q}`) })),\n ),\n )\n const outputs = events.filter(isOutput).map((e) => e.result)\n expect(outputs).toHaveLength(1)\n expect(outputs[0]).toMatchObject({ _tag: \"Ok\", tool: \"geo__search\", value: \"geo:lisbon\" })\n })\n})\n\ndescribe(\"Toolkit.wrap - middleware\", () => {\n const Empty = Schema.Struct({})\n type AuditShape = { readonly record: (name: string) => Effect.Effect<void> }\n class Audit extends Context.Service<Audit, AuditShape>()(\"test/Audit\") {}\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed({ tempC: 18 }),\n })\n const escalate = Tool.signal({\n name: \"escalate\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n })\n\n // A middleware that needs the Audit service -> R2 = Audit.\n const audited: Toolkit.Middleware<Audit> = (run, name) => (input, emit) =>\n Effect.gen(function* () {\n const a = yield* Audit\n yield* a.record(name)\n return yield* run(input, emit)\n })\n\n const call = (name: string, id: string): ToolCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: \"{}\",\n })\n\n it(\"unions the middleware's R2 into the toolkit's requirements\", () => {\n const wrapped = Toolkit.wrap(Toolkit.make(getWeather), audited)\n const stream = Toolkit.run(wrapped, [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, Audit>\n >()\n })\n\n it(\"runs the wrapped handler and leaves non-local kinds untouched\", async () => {\n const recorded: Array<string> = []\n const wrapped = pipe(Toolkit.make(getWeather, escalate), Toolkit.wrap(audited))\n\n const events = await Effect.runPromise(\n Toolkit.run(wrapped, [call(\"get_weather\", \"w1\"), call(\"escalate\", \"s1\")]).pipe(\n Stream.runCollect,\n Effect.provideService(Audit, { record: (n) => Effect.sync(() => void recorded.push(n)) }),\n ),\n )\n const byCall = new Map(\n Array.from(events)\n .filter(isOutput)\n .map((e) => [e.result.call_id, e.result] as const),\n )\n\n // Local tool ran through the middleware (audit fired).\n expect(recorded).toEqual([\"get_weather\"])\n const w = byCall.get(\"w1\")\n expect(w !== undefined && isOk(w) && w.value).toEqual({ tempC: 18 })\n // Signal kind is left alone by wrap -> still reported non-local.\n const s = byCall.get(\"s1\")\n expect(s?._tag === \"Failure\" && s.kind).toBe(\"non_local_tool\")\n })\n})\n\ndescribe(\"Toolkit.run - executor outcomes\", () => {\n const plain = <N extends string>(name: N, out: unknown) =>\n Tool.make({\n name,\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(out),\n })\n\n it(\"absorbs a bare string failure as a tool_failed result\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(\"nope\"),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"tool_failed\", reason: \"nope\" })\n })\n\n it(\"absorbs Tool.fail with a custom kind\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Tool.fail(\"missing\", { kind: \"not_found\" }),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"not_found\", reason: \"missing\" })\n })\n\n it(\"propagates a typed non-sentinel failure on the stream\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const exit = await Effect.runPromiseExit(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"dies on a defect instead of masking it as a result\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.die(new Error(\"boom\")),\n })\n const exit = await Effect.runPromiseExit(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(exit._tag).toBe(\"Failure\")\n })\n\n it(\"streams progress events strictly before the terminal output\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: (_input, emit) =>\n emit(\"a\").pipe(Effect.andThen(emit(\"b\")), Effect.andThen(Effect.succeed(\"done\"))),\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n expect(events.map((e) => e._tag)).toEqual([\"Progress\", \"Progress\", \"Output\"])\n expect(events.filter(isProgress).map((p) => p.data)).toEqual([\"a\", \"b\"])\n })\n\n it(\"reports input validation failures as a result carrying the issue detail\", async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ x: Schema.Number })),\n run: () => Effect.succeed(1),\n })\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\", '{\"x\":\"nope\"}')]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"input_validation_error\" })\n expect(result !== undefined && isFailure(result) && result.reason).toBeTruthy()\n })\n\n it(\"normalizes empty arguments to {} for a zero-arg tool\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"t\", \"1\", \"\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Ok\", value: \"ok\" })\n })\n\n it(\"reports unparseable arguments as input_validation_error\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"t\", \"1\", \"not json\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"input_validation_error\" })\n })\n\n it(\"a call named constructor is unknown_tool, not non_local_tool\", async () => {\n const events = await Effect.runPromise(\n runToArray(Toolkit.make(plain(\"t\", \"ok\")), [toolCall(\"constructor\", \"1\")]),\n )\n const result = events.filter(isOutput)[0]?.result\n expect(result).toMatchObject({ _tag: \"Failure\", kind: \"unknown_tool\" })\n })\n\n it('serializes an undefined output as \"null\" on the wire', async () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.void,\n })\n const events = await Effect.runPromise(runToArray(Toolkit.make(t), [toolCall(\"t\", \"1\")]))\n const result = events.filter(isOutput)[0]?.result\n expect(result !== undefined && toToolCallOutput(result).output).toBe(\"null\")\n })\n\n it(\"preserves call order with concurrency 1\", async () => {\n const kit = Toolkit.make(plain(\"a\", \"A\"), plain(\"b\", \"B\"))\n const events = await Effect.runPromise(\n runToArray(kit, [toolCall(\"a\", \"a1\"), toolCall(\"b\", \"b1\")], { concurrency: 1 }),\n )\n expect(events.filter(isOutput).map((e) => e.result.call_id)).toEqual([\"a1\", \"b1\"])\n })\n\n it(\"type: run of a describeFailures toolkit has no stream error\", () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const kit = Toolkit.describeFailures(Toolkit.make(t), AiError.describe)\n const stream = Toolkit.run(kit, [])\n expectTypeOf(stream).toEqualTypeOf<Stream.Stream<ToolEvent, never, never>>()\n })\n\n it(\"type: run of a typed toolkit propagates the tool's E\", () => {\n const t = Tool.make({\n name: \"t\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.fail(new AiError.RateLimited({ provider: \"x\", raw: null })),\n })\n const stream = Toolkit.run(Toolkit.make(t), [])\n expectTypeOf(stream).toEqualTypeOf<Stream.Stream<ToolEvent, AiError.RateLimited, never>>()\n })\n})\n"],"mappings":"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"}
@@ -20,7 +20,7 @@ var Transcriber = class extends Context.Service()("@betalyra/effect-uai/Transcri
20
20
  */
21
21
  var SttStreaming = class extends Context.Service()("@betalyra/effect-uai/capability/SttStreaming") {};
22
22
  /** One-shot transcription. */
23
- const transcribe = (request) => Effect.flatMap(Transcriber.asEffect(), (t) => t.transcribe(request));
23
+ const transcribe = (request) => Effect.flatMap(Transcriber, (t) => t.transcribe(request));
24
24
  /**
25
25
  * Live transcription. Dual-arity: pipeable (data-last) and direct
26
26
  * (data-first). Requires `SttStreaming` in R — providers without
@@ -39,8 +39,8 @@ const transcribe = (request) => Effect.flatMap(Transcriber.asEffect(), (t) => t.
39
39
  * ```
40
40
  */
41
41
  const streamTranscriptionFrom = Function.dual(2, (audioIn, request) => Stream.unwrap(Effect.gen(function* () {
42
- const t = yield* Transcriber.asEffect();
43
- yield* SttStreaming.asEffect();
42
+ const t = yield* Transcriber;
43
+ yield* SttStreaming;
44
44
  return t.streamTranscriptionFrom(audioIn, request);
45
45
  })));
46
46
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"Transcriber.mjs","names":[],"sources":["../../src/transcriber/Transcriber.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { AudioFormat, AudioSource } from \"../domain/Audio.js\"\nimport type { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\n\n/**\n * Cross-provider sync transcription request. Provider-specific\n * extensions (Deepgram `keyterm[]`, ElevenLabs `diarize`, Google\n * `adaptation`, …) live on each provider's typed request which extends\n * this and narrows `model`.\n */\nexport type CommonTranscribeRequest = {\n readonly audio: AudioSource\n /** Model identifier. Each provider narrows to its typed literal union. */\n readonly model: string\n /** ISO-639-1 / BCP-47. Omit for autodetection (where supported). */\n readonly language?: string\n /** Free-form context / style hint (Whisper-style prose). OpenAI honors\n * it; providers without a prompt field `warnDropped`. */\n readonly prompt?: string\n /** Vocabulary biasing — discrete terms to boost (names, jargon). Maps to\n * Deepgram `keyterm`, ElevenLabs `keyterms`, Google `adaptation`,\n * Inworld `prompts`; others `warnDropped`. */\n readonly biasingTerms?: ReadonlyArray<string>\n readonly diarization?: boolean\n readonly wordTimestamps?: boolean\n}\n\n/**\n * Streaming-transcription request. `inputFormat` declares what the\n * bytes in the input stream will look like — providers reject\n * formats they can't ingest at stream startup with\n * `AiError.Unsupported` (a per-Layer capability gap, not a wire-shape\n * mismatch).\n */\nexport type CommonStreamTranscribeRequest = Omit<CommonTranscribeRequest, \"audio\"> & {\n readonly inputFormat: AudioFormat\n readonly interimResults?: boolean\n readonly vadEvents?: boolean\n}\n\nexport type TranscriberService = {\n /**\n * One-shot transcription. Universal — AWS Transcribe (which has no\n * native sync endpoint) emulates this by draining a streaming session\n * internally.\n */\n readonly transcribe: (\n request: CommonTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n /**\n * Live transcription as a Stream transformer. Consumes audio bytes\n * from `audioIn`; emits `TranscriptEvent`s as they arrive. The\n * underlying WS / gRPC connection is acquired on first pull and\n * released when the output stream is finalized (success, failure, or\n * interruption) via `Stream.scoped` — no explicit Scope handling at\n * the call site.\n *\n * Gated by the `SttStreaming` capability marker on the top-level\n * helper — providers without streaming-STT support don't ship the\n * marker, so calls fail at `Effect.provide` with a type error.\n */\n readonly streamTranscriptionFrom: <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R>\n}\n\nexport class Transcriber extends Context.Service<Transcriber, TranscriberService>()(\n \"@betalyra/effect-uai/Transcriber\",\n) {}\n\n/**\n * Capability marker — provided by provider layers whose\n * `streamTranscriptionFrom` is wired up at the wire level. Azure does\n * not ship it (streaming-STT is SDK-internal). Calling\n * `streamTranscriptionFrom` while only Azure's Layer is in scope fails\n * at `Effect.provide` with a type error, not at runtime.\n *\n * Phantom — the value is `void`; providers register with\n * `Layer.succeed(SttStreaming, undefined)`.\n */\nexport class SttStreaming extends Context.Service<SttStreaming, void>()(\n \"@betalyra/effect-uai/capability/SttStreaming\",\n) {}\n\n/** One-shot transcription. */\nexport const transcribe = (\n request: CommonTranscribeRequest,\n): Effect.Effect<TranscriptResult, AiError.AiError, Transcriber> =>\n Effect.flatMap(Transcriber.asEffect(), (t) => t.transcribe(request))\n\n/**\n * Live transcription. Dual-arity: pipeable (data-last) and direct\n * (data-first). Requires `SttStreaming` in R — providers without\n * streaming support are a type error at provide time.\n *\n * @example\n * ```ts\n * // Pipeable — composes with other Stream operators\n * mic.frames.pipe(\n * Transcriber.streamTranscriptionFrom(req),\n * Stream.filter((e) => e._tag === \"final\"),\n * )\n *\n * // Direct\n * Transcriber.streamTranscriptionFrom(mic.frames, req)\n * ```\n */\nexport const streamTranscriptionFrom: {\n (\n request: CommonStreamTranscribeRequest,\n ): <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n} = Function.dual(\n 2,\n <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const t = yield* Transcriber.asEffect()\n yield* SttStreaming.asEffect()\n return t.streamTranscriptionFrom(audioIn, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;AAoEA,IAAa,cAAb,cAAiC,QAAQ,SAA0C,CACjF,mCACD,CAAC;;;;;;;;;;;AAYF,IAAa,eAAb,cAAkC,QAAQ,SAA6B,CACrE,+CACD,CAAC;;AAGF,MAAa,cACX,YAEA,OAAO,QAAQ,YAAY,UAAU,GAAG,MAAM,EAAE,WAAW,QAAQ,CAAC;;;;;;;;;;;;;;;;;;AAmBtE,MAAa,0BAUT,SAAS,KACX,IACO,SAA0C,YAC/C,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO,YAAY,UAAU;AACvC,QAAO,aAAa,UAAU;AAC9B,QAAO,EAAE,wBAAwB,SAAS,QAAQ;EAClD,CACH,CACJ"}
1
+ {"version":3,"file":"Transcriber.mjs","names":[],"sources":["../../src/transcriber/Transcriber.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { AudioFormat, AudioSource } from \"../domain/Audio.js\"\nimport type { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\n\n/**\n * Cross-provider sync transcription request. Provider-specific\n * extensions (Deepgram `keyterm[]`, ElevenLabs `diarize`, Google\n * `adaptation`, …) live on each provider's typed request which extends\n * this and narrows `model`.\n */\nexport type CommonTranscribeRequest = {\n readonly audio: AudioSource\n /** Model identifier. Each provider narrows to its typed literal union. */\n readonly model: string\n /** ISO-639-1 / BCP-47. Omit for autodetection (where supported). */\n readonly language?: string\n /** Free-form context / style hint (Whisper-style prose). OpenAI honors\n * it; providers without a prompt field `warnDropped`. */\n readonly prompt?: string\n /** Vocabulary biasing — discrete terms to boost (names, jargon). Maps to\n * Deepgram `keyterm`, ElevenLabs `keyterms`, Google `adaptation`,\n * Inworld `prompts`; others `warnDropped`. */\n readonly biasingTerms?: ReadonlyArray<string>\n readonly diarization?: boolean\n readonly wordTimestamps?: boolean\n}\n\n/**\n * Streaming-transcription request. `inputFormat` declares what the\n * bytes in the input stream will look like — providers reject\n * formats they can't ingest at stream startup with\n * `AiError.Unsupported` (a per-Layer capability gap, not a wire-shape\n * mismatch).\n */\nexport type CommonStreamTranscribeRequest = Omit<CommonTranscribeRequest, \"audio\"> & {\n readonly inputFormat: AudioFormat\n readonly interimResults?: boolean\n readonly vadEvents?: boolean\n}\n\nexport type TranscriberService = {\n /**\n * One-shot transcription. Universal — AWS Transcribe (which has no\n * native sync endpoint) emulates this by draining a streaming session\n * internally.\n */\n readonly transcribe: (\n request: CommonTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n /**\n * Live transcription as a Stream transformer. Consumes audio bytes\n * from `audioIn`; emits `TranscriptEvent`s as they arrive. The\n * underlying WS / gRPC connection is acquired on first pull and\n * released when the output stream is finalized (success, failure, or\n * interruption) via `Stream.scoped` — no explicit Scope handling at\n * the call site.\n *\n * Gated by the `SttStreaming` capability marker on the top-level\n * helper — providers without streaming-STT support don't ship the\n * marker, so calls fail at `Effect.provide` with a type error.\n */\n readonly streamTranscriptionFrom: <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R>\n}\n\nexport class Transcriber extends Context.Service<Transcriber, TranscriberService>()(\n \"@betalyra/effect-uai/Transcriber\",\n) {}\n\n/**\n * Capability marker — provided by provider layers whose\n * `streamTranscriptionFrom` is wired up at the wire level. Azure does\n * not ship it (streaming-STT is SDK-internal). Calling\n * `streamTranscriptionFrom` while only Azure's Layer is in scope fails\n * at `Effect.provide` with a type error, not at runtime.\n *\n * Phantom — the value is `void`; providers register with\n * `Layer.succeed(SttStreaming, undefined)`.\n */\nexport class SttStreaming extends Context.Service<SttStreaming, void>()(\n \"@betalyra/effect-uai/capability/SttStreaming\",\n) {}\n\n/** One-shot transcription. */\nexport const transcribe = (\n request: CommonTranscribeRequest,\n): Effect.Effect<TranscriptResult, AiError.AiError, Transcriber> =>\n Effect.flatMap(Transcriber, (t) => t.transcribe(request))\n\n/**\n * Live transcription. Dual-arity: pipeable (data-last) and direct\n * (data-first). Requires `SttStreaming` in R — providers without\n * streaming support are a type error at provide time.\n *\n * @example\n * ```ts\n * // Pipeable — composes with other Stream operators\n * mic.frames.pipe(\n * Transcriber.streamTranscriptionFrom(req),\n * Stream.filter((e) => e._tag === \"final\"),\n * )\n *\n * // Direct\n * Transcriber.streamTranscriptionFrom(mic.frames, req)\n * ```\n */\nexport const streamTranscriptionFrom: {\n (\n request: CommonStreamTranscribeRequest,\n ): <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n} = Function.dual(\n 2,\n <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const t = yield* Transcriber\n yield* SttStreaming\n return t.streamTranscriptionFrom(audioIn, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;AAoEA,IAAa,cAAb,cAAiC,QAAQ,SAA0C,CACjF,mCACD,CAAC;;;;;;;;;;;AAYF,IAAa,eAAb,cAAkC,QAAQ,SAA6B,CACrE,+CACD,CAAC;;AAGF,MAAa,cACX,YAEA,OAAO,QAAQ,cAAc,MAAM,EAAE,WAAW,QAAQ,CAAC;;;;;;;;;;;;;;;;;;AAmB3D,MAAa,0BAUT,SAAS,KACX,IACO,SAA0C,YAC/C,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO;AACjB,QAAO;AACP,QAAO,EAAE,wBAAwB,SAAS,QAAQ;EAClD,CACH,CACJ"}
@@ -1,5 +1,5 @@
1
1
  import { streamTranscriptionFrom, transcribe } from "./Transcriber.mjs";
2
- import { a as describe, n as globalExpect, o as it, t as import_dist } from "../dist-B04dlFHg.mjs";
2
+ import { c as it, n as globalExpect, o as describe, t as import_dist } from "../dist-CcRV3-A5.mjs";
3
3
  import { layer, layerSyncOnly } from "../testing/MockTranscriber.mjs";
4
4
  import { Duration, Effect, Stream } from "effect";
5
5
  //#region src/transcriber/Transcriber.test.ts
@@ -11,7 +11,7 @@ var WebRead_exports = /* @__PURE__ */ __exportAll({
11
11
  */
12
12
  var WebRead = class extends Context.Service()("@betalyra/effect-uai/WebRead") {};
13
13
  /** Read one URL against whichever provider Layer is in scope. */
14
- const read = (request) => Effect.flatMap(WebRead.asEffect(), (s) => s.read(request));
14
+ const read = (request) => Effect.flatMap(WebRead, (s) => s.read(request));
15
15
  //#endregion
16
16
  export { WebRead, read, WebRead_exports as t };
17
17
 
@@ -1 +1 @@
1
- {"version":3,"file":"WebRead.mjs","names":[],"sources":["../../src/web-read/WebRead.ts"],"sourcesContent":["import { Context, type Duration, Effect } from \"effect\"\nimport type * as AiError from \"../domain/AiError.js\"\n\n// ---------------------------------------------------------------------------\n// Format\n// ---------------------------------------------------------------------------\n\n/** Representation to return for a page. `markdown` is the default. */\nexport type ReadFormat = \"markdown\" | \"html\"\n\n// ---------------------------------------------------------------------------\n// Request\n// ---------------------------------------------------------------------------\n\n/**\n * Cross-provider read request: one URL in, clean content out. Provider-specific\n * knobs live on the provider-typed request.\n */\nexport type CommonReadRequest = {\n /** The page to read. */\n readonly url: string\n /** Output representation. Defaults to `markdown`. See {@link ReadFormat}. */\n readonly format?: ReadFormat\n /** Upper bound on time spent fetching the page. */\n readonly timeout?: Duration.Duration\n}\n\n// ---------------------------------------------------------------------------\n// Response\n// ---------------------------------------------------------------------------\n\n/** One normalized read result. */\nexport type ReadResponse = {\n /** The URL that was read. */\n readonly url: string\n /** Extracted content in the requested {@link ReadFormat}. */\n readonly content: string\n /** Page title, when reported. */\n readonly title?: string\n /** Links discovered on the page, when returned. */\n readonly links?: ReadonlyArray<string>\n /** The provider-native response. */\n readonly raw: unknown\n}\n\n// ---------------------------------------------------------------------------\n// Service + helper\n// ---------------------------------------------------------------------------\n\n/**\n * The portable read surface: one operation, `read`. Every implementor can\n * answer, so this capability needs no marker tags.\n */\nexport type WebReadService = {\n readonly read: (request: CommonReadRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Generic read service tag. Yield this for provider-portable code; yield a\n * provider tag for provider-specific knobs. A provider `layer` registers both.\n */\nexport class WebRead extends Context.Service<WebRead, WebReadService>()(\n \"@betalyra/effect-uai/WebRead\",\n) {}\n\n/** Read one URL against whichever provider Layer is in scope. */\nexport const read = (\n request: CommonReadRequest,\n): Effect.Effect<ReadResponse, AiError.AiError, WebRead> =>\n Effect.flatMap(WebRead.asEffect(), (s) => s.read(request))\n"],"mappings":";;;;;;;;;;;AA6DA,IAAa,UAAb,cAA6B,QAAQ,SAAkC,CACrE,+BACD,CAAC;;AAGF,MAAa,QACX,YAEA,OAAO,QAAQ,QAAQ,UAAU,GAAG,MAAM,EAAE,KAAK,QAAQ,CAAC"}
1
+ {"version":3,"file":"WebRead.mjs","names":[],"sources":["../../src/web-read/WebRead.ts"],"sourcesContent":["import { Context, type Duration, Effect } from \"effect\"\nimport type * as AiError from \"../domain/AiError.js\"\n\n// ---------------------------------------------------------------------------\n// Format\n// ---------------------------------------------------------------------------\n\n/** Representation to return for a page. `markdown` is the default. */\nexport type ReadFormat = \"markdown\" | \"html\"\n\n// ---------------------------------------------------------------------------\n// Request\n// ---------------------------------------------------------------------------\n\n/**\n * Cross-provider read request: one URL in, clean content out. Provider-specific\n * knobs live on the provider-typed request.\n */\nexport type CommonReadRequest = {\n /** The page to read. */\n readonly url: string\n /** Output representation. Defaults to `markdown`. See {@link ReadFormat}. */\n readonly format?: ReadFormat\n /** Upper bound on time spent fetching the page. */\n readonly timeout?: Duration.Duration\n}\n\n// ---------------------------------------------------------------------------\n// Response\n// ---------------------------------------------------------------------------\n\n/** One normalized read result. */\nexport type ReadResponse = {\n /** The URL that was read. */\n readonly url: string\n /** Extracted content in the requested {@link ReadFormat}. */\n readonly content: string\n /** Page title, when reported. */\n readonly title?: string\n /** Links discovered on the page, when returned. */\n readonly links?: ReadonlyArray<string>\n /** The provider-native response. */\n readonly raw: unknown\n}\n\n// ---------------------------------------------------------------------------\n// Service + helper\n// ---------------------------------------------------------------------------\n\n/**\n * The portable read surface: one operation, `read`. Every implementor can\n * answer, so this capability needs no marker tags.\n */\nexport type WebReadService = {\n readonly read: (request: CommonReadRequest) => Effect.Effect<ReadResponse, AiError.AiError>\n}\n\n/**\n * Generic read service tag. Yield this for provider-portable code; yield a\n * provider tag for provider-specific knobs. A provider `layer` registers both.\n */\nexport class WebRead extends Context.Service<WebRead, WebReadService>()(\n \"@betalyra/effect-uai/WebRead\",\n) {}\n\n/** Read one URL against whichever provider Layer is in scope. */\nexport const read = (\n request: CommonReadRequest,\n): Effect.Effect<ReadResponse, AiError.AiError, WebRead> =>\n Effect.flatMap(WebRead, (s) => s.read(request))\n"],"mappings":";;;;;;;;;;;AA6DA,IAAa,UAAb,cAA6B,QAAQ,SAAkC,CACrE,+BACD,CAAC;;AAGF,MAAa,QACX,YAEA,OAAO,QAAQ,UAAU,MAAM,EAAE,KAAK,QAAQ,CAAC"}
@@ -1,4 +1,4 @@
1
- import { l as Tool } from "../Tool-2lC_XMth.mjs";
1
+ import { l as Tool } from "../Tool-u5bag8SO.mjs";
2
2
  import { t as AiError } from "../AiError-Bf1pq4BB.mjs";
3
3
  import { ReadFormat, ReadResponse, WebRead } from "./WebRead.mjs";
4
4
  import { Schema } from "effect";
@@ -35,7 +35,7 @@ const SearchRecency = Schema.Literals([
35
35
  */
36
36
  var WebSearch = class extends Context.Service()("@betalyra/effect-uai/WebSearch") {};
37
37
  /** Run one web search against whichever provider Layer is in scope. */
38
- const search = (request) => Effect.flatMap(WebSearch.asEffect(), (s) => s.search(request));
38
+ const search = (request) => Effect.flatMap(WebSearch, (s) => s.search(request));
39
39
  //#endregion
40
40
  export { SearchRecency, WebSearch, search, WebSearch_exports as t };
41
41