@effect-uai/core 0.9.0 → 0.10.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 (112) hide show
  1. package/dist/{AiError-CrdR1EQi.d.mts → AiError-Bf1pq4BB.d.mts} +8 -3
  2. package/dist/{AiError-CrdR1EQi.d.mts.map → AiError-Bf1pq4BB.d.mts.map} +1 -1
  3. package/dist/{Audio-DDSKCkSe.d.mts → Audio-CR7CSljJ.d.mts} +2 -2
  4. package/dist/{Audio-DDSKCkSe.d.mts.map → Audio-CR7CSljJ.d.mts.map} +1 -1
  5. package/dist/{Image-C_Zjpwhc.d.mts → Image-BZmKfIdq.d.mts} +2 -2
  6. package/dist/{Image-C_Zjpwhc.d.mts.map → Image-BZmKfIdq.d.mts.map} +1 -1
  7. package/dist/{Items-CgKD6Npw.d.mts → Items-BnIduEV2.d.mts} +9 -9
  8. package/dist/{Items-CgKD6Npw.d.mts.map → Items-BnIduEV2.d.mts.map} +1 -1
  9. package/dist/{Loop-B5_cqumi.d.mts → Loop-D8nWpowc.d.mts} +2 -2
  10. package/dist/{Loop-B5_cqumi.d.mts.map → Loop-D8nWpowc.d.mts.map} +1 -1
  11. package/dist/{Media-DOLyaygI.d.mts → Media-D_CpcM1Z.d.mts} +1 -1
  12. package/dist/{Media-DOLyaygI.d.mts.map → Media-D_CpcM1Z.d.mts.map} +1 -1
  13. package/dist/{StructuredFormat-CMJFD3sn.d.mts → StructuredFormat-DHaUJBdd.d.mts} +1 -1
  14. package/dist/{StructuredFormat-CMJFD3sn.d.mts.map → StructuredFormat-DHaUJBdd.d.mts.map} +1 -1
  15. package/dist/{Tool-0XUIT2A2.d.mts → Tool-2lC_XMth.d.mts} +64 -12
  16. package/dist/Tool-2lC_XMth.d.mts.map +1 -0
  17. package/dist/{ToolEvent-DYrAUJGs.d.mts → ToolEvent-cWZ2BnbS.d.mts} +2 -2
  18. package/dist/ToolEvent-cWZ2BnbS.d.mts.map +1 -0
  19. package/dist/{ToolResult-BZd-cEWW.d.mts → ToolResult-CL4hK_9z.d.mts} +10 -4
  20. package/dist/ToolResult-CL4hK_9z.d.mts.map +1 -0
  21. package/dist/{Toolkit-BKC8b8Xo.d.mts → Toolkit-Dr9l_hNy.d.mts} +64 -23
  22. package/dist/Toolkit-Dr9l_hNy.d.mts.map +1 -0
  23. package/dist/browser/Browser.d.mts +190 -0
  24. package/dist/browser/Browser.d.mts.map +1 -0
  25. package/dist/browser/Browser.mjs +34 -0
  26. package/dist/browser/Browser.mjs.map +1 -0
  27. package/dist/browser/BrowserError.d.mts +141 -0
  28. package/dist/browser/BrowserError.d.mts.map +1 -0
  29. package/dist/browser/BrowserError.mjs +89 -0
  30. package/dist/browser/BrowserError.mjs.map +1 -0
  31. package/dist/browser/BrowserTool.d.mts +58 -0
  32. package/dist/browser/BrowserTool.d.mts.map +1 -0
  33. package/dist/browser/BrowserTool.mjs +86 -0
  34. package/dist/browser/BrowserTool.mjs.map +1 -0
  35. package/dist/domain/AiError.d.mts +2 -2
  36. package/dist/domain/AiError.mjs +22 -3
  37. package/dist/domain/AiError.mjs.map +1 -1
  38. package/dist/domain/Audio.d.mts +1 -1
  39. package/dist/domain/Image.d.mts +1 -1
  40. package/dist/domain/Items.d.mts +1 -1
  41. package/dist/domain/Media.d.mts +1 -1
  42. package/dist/domain/Music.d.mts +1 -1
  43. package/dist/domain/Turn.d.mts +3 -3
  44. package/dist/embedding-model/Embedding.d.mts +1 -1
  45. package/dist/embedding-model/EmbeddingModel.d.mts +1 -1
  46. package/dist/index.d.mts +17 -12
  47. package/dist/index.mjs +14 -9
  48. package/dist/language-model/LanguageModel.d.mts +4 -4
  49. package/dist/loop/Loop.d.mts +1 -1
  50. package/dist/music-generator/MusicGenerator.d.mts +2 -2
  51. package/dist/observability/Metrics.d.mts +1 -1
  52. package/dist/retry/Retry.d.mts +1 -1
  53. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +2 -2
  54. package/dist/structured-format/StructuredFormat.d.mts +1 -1
  55. package/dist/testing/MockMusicGenerator.d.mts +1 -1
  56. package/dist/testing/MockProvider.d.mts +1 -1
  57. package/dist/testing/MockSpeechSynthesizer.d.mts +1 -1
  58. package/dist/tool/Approval.d.mts +19 -7
  59. package/dist/tool/Approval.d.mts.map +1 -1
  60. package/dist/tool/Approval.mjs +18 -11
  61. package/dist/tool/Approval.mjs.map +1 -1
  62. package/dist/tool/Approval.test.mjs +46 -7
  63. package/dist/tool/Approval.test.mjs.map +1 -1
  64. package/dist/tool/HistoryCheck.d.mts +2 -2
  65. package/dist/tool/Tool.d.mts +2 -2
  66. package/dist/tool/Tool.mjs +86 -22
  67. package/dist/tool/Tool.mjs.map +1 -1
  68. package/dist/tool/ToolEvent.d.mts +1 -1
  69. package/dist/tool/ToolEvent.mjs +5 -0
  70. package/dist/tool/ToolEvent.mjs.map +1 -1
  71. package/dist/tool/ToolResult.d.mts +2 -2
  72. package/dist/tool/ToolResult.mjs +15 -6
  73. package/dist/tool/ToolResult.mjs.map +1 -1
  74. package/dist/tool/Toolkit.d.mts +2 -2
  75. package/dist/tool/Toolkit.mjs +62 -22
  76. package/dist/tool/Toolkit.mjs.map +1 -1
  77. package/dist/tool/Toolkit.test.mjs +163 -9
  78. package/dist/tool/Toolkit.test.mjs.map +1 -1
  79. package/dist/transcriber/Transcriber.d.mts +2 -2
  80. package/dist/web-read/WebRead.d.mts +44 -0
  81. package/dist/web-read/WebRead.d.mts.map +1 -0
  82. package/dist/web-read/WebRead.mjs +18 -0
  83. package/dist/web-read/WebRead.mjs.map +1 -0
  84. package/dist/web-read/WebReadTool.d.mts +57 -0
  85. package/dist/web-read/WebReadTool.d.mts.map +1 -0
  86. package/dist/web-read/WebReadTool.mjs +59 -0
  87. package/dist/web-read/WebReadTool.mjs.map +1 -0
  88. package/dist/web-search/WebSearch.d.mts +1 -1
  89. package/dist/web-search/WebSearchTool.d.mts +12 -6
  90. package/dist/web-search/WebSearchTool.d.mts.map +1 -1
  91. package/dist/web-search/WebSearchTool.mjs +10 -5
  92. package/dist/web-search/WebSearchTool.mjs.map +1 -1
  93. package/package.json +21 -1
  94. package/src/browser/Browser.ts +268 -0
  95. package/src/browser/BrowserError.ts +145 -0
  96. package/src/browser/BrowserTool.ts +161 -0
  97. package/src/domain/AiError.ts +27 -1
  98. package/src/index.ts +5 -0
  99. package/src/tool/Approval.test.ts +55 -5
  100. package/src/tool/Approval.ts +72 -27
  101. package/src/tool/Tool.ts +146 -47
  102. package/src/tool/ToolEvent.ts +5 -0
  103. package/src/tool/ToolResult.ts +14 -8
  104. package/src/tool/Toolkit.test.ts +171 -2
  105. package/src/tool/Toolkit.ts +173 -60
  106. package/src/web-read/WebRead.ts +70 -0
  107. package/src/web-read/WebReadTool.ts +96 -0
  108. package/src/web-search/WebSearchTool.ts +11 -5
  109. package/dist/Tool-0XUIT2A2.d.mts.map +0 -1
  110. package/dist/ToolEvent-DYrAUJGs.d.mts.map +0 -1
  111. package/dist/ToolResult-BZd-cEWW.d.mts.map +0 -1
  112. package/dist/Toolkit-BKC8b8Xo.d.mts.map +0 -1
@@ -1,3 +1,4 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec"
1
2
  import {
2
3
  Array as Arr,
3
4
  Cause,
@@ -10,7 +11,6 @@ import {
10
11
  Queue,
11
12
  Record,
12
13
  Ref,
13
- Schema,
14
14
  Stream,
15
15
  } from "effect"
16
16
  import * as Loop from "../loop/Loop.js"
@@ -19,18 +19,21 @@ import { appendToHistory, type Turn } from "../domain/Turn.js"
19
19
  import {
20
20
  type AnyLocalTool,
21
21
  type AnyTool,
22
+ type DecodeResult,
23
+ decodeCallInput,
22
24
  type Emit,
23
25
  type InteractionTool,
24
26
  type ProviderTool,
25
27
  type SignalTool,
26
28
  type Tool,
27
29
  type ToolDescriptor,
30
+ type ToolE,
31
+ ToolFailed,
28
32
  type ToolR,
29
33
  toDescriptors,
30
34
  } from "./Tool.js"
31
35
  import {
32
36
  ToolResult,
33
- executionError,
34
37
  failed,
35
38
  nonLocalTool,
36
39
  toToolCallOutput,
@@ -93,6 +96,15 @@ export const descriptors = (toolkit: Toolkit): ReadonlyArray<ToolDescriptor> =>
93
96
  */
94
97
  export type ToolkitR<T extends Toolkit> = ToolMap extends T ? never : ToolR<T[keyof T]>
95
98
 
99
+ /**
100
+ * Union of every tool's `E` error type in a toolkit. `Toolkit.run` reports
101
+ * `Exclude<ToolkitE<T>, string | ToolFailed>` on its stream: the sentinel
102
+ * failures are absorbed into results, everything else propagates typed. The
103
+ * same `ToolMap extends T` guard as `ToolkitR` keeps a wide toolkit at
104
+ * `never` rather than `any`.
105
+ */
106
+ export type ToolkitE<T extends Toolkit> = ToolMap extends T ? never : ToolE<T[keyof T]>
107
+
96
108
  // ---------------------------------------------------------------------------
97
109
  // Uniqueness & composition. `make` rejects duplicate literal names at compile
98
110
  // time; `compose` rejects cross-source collisions — at compile time when the
@@ -257,45 +269,99 @@ export const compose = <const Kits extends ReadonlyArray<Toolkit>>(
257
269
  // widening every tool's type.
258
270
  // ---------------------------------------------------------------------------
259
271
 
260
- type Handler<Input, Event, Output, R> = (
272
+ type Handler<Input, Event, Output, E, R> = (
261
273
  input: Input,
262
274
  emit: Emit<Event>,
263
- ) => Effect.Effect<Output, unknown, R>
275
+ ) => Effect.Effect<Output, E, R>
264
276
 
265
- export type Middleware<R2 = never> = <Input, Event, Output, R>(
266
- run: Handler<Input, Event, Output, R>,
277
+ /**
278
+ * A `run` transform, tracking an added requirement `R2` and optional added
279
+ * error `E2`. The tool's own `E` is preserved; `describeFailures` is the
280
+ * combinator that *replaces* it (mapping every error to `string`).
281
+ */
282
+ export type Middleware<R2 = never, E2 = never> = <Input, Event, Output, E, R>(
283
+ run: Handler<Input, Event, Output, E, R>,
267
284
  name: string,
268
- ) => Handler<Input, Event, Output, R | R2>
285
+ ) => Handler<Input, Event, Output, E | E2, R | R2>
269
286
 
270
- type WrapTool<T extends AnyTool, R2> =
271
- T extends Tool<infer N, infer I, infer Ev, infer O, infer R> ? Tool<N, I, Ev, O, R | R2> : T
287
+ type WrapTool<T extends AnyTool, R2, E2> =
288
+ T extends Tool<infer N, infer I, infer Ev, infer O, infer E, infer R>
289
+ ? Tool<N, I, Ev, O, E | E2, R | R2>
290
+ : T
272
291
 
273
- type WrapToolkit<T extends ToolMap, R2> = {
274
- readonly [K in keyof T]: T[K] extends AnyTool ? WrapTool<T[K], R2> : T[K]
292
+ type WrapToolkit<T extends ToolMap, R2, E2> = {
293
+ readonly [K in keyof T]: T[K] extends AnyTool ? WrapTool<T[K], R2, E2> : T[K]
275
294
  }
276
295
 
277
296
  /**
278
297
  * Wrap every local tool's `run` with a middleware, preserving each tool's
279
298
  * `Input`/`Output` and unioning the middleware's requirement `R2` into the
280
299
  * toolkit's `ToolkitR`. Non-local kinds pass through unchanged. Stack with
281
- * `pipe`:
300
+ * `pipe` - inner-first, so the last `wrap` applied is the outermost and runs
301
+ * first:
282
302
  *
283
303
  * const observed = pipe(toolkit, Toolkit.wrap(logging), Toolkit.wrap(authz))
284
- * Toolkit.run(observed, calls) // R = ToolkitR<typeof toolkit> | <authz R>
304
+ * Toolkit.run(observed, calls) // authz runs, then logging, then the tool
285
305
  *
286
- * A middleware that `Effect.fail`s surfaces as a `ToolResult.Failure` from the
287
- * executor like any run failure; use the `Approval` flow for a distinct
288
- * "denied" verdict.
306
+ * A middleware that fails with `string` / `ToolFailed` is absorbed by the
307
+ * executor into a `ToolResult.Failure`; any other error propagates typed. Use
308
+ * `describeFailures` for the total "everything to the model" case, or the
309
+ * `Approval` flow for a distinct "denied" verdict.
289
310
  */
290
311
  export const wrap: {
291
- <R2>(middleware: Middleware<R2>): <T extends Toolkit>(toolkit: T) => WrapToolkit<T, R2>
292
- <T extends Toolkit, R2>(toolkit: T, middleware: Middleware<R2>): WrapToolkit<T, R2>
312
+ <R2, E2>(
313
+ middleware: Middleware<R2, E2>,
314
+ ): <T extends Toolkit>(toolkit: T) => WrapToolkit<T, R2, E2>
315
+ <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2>
293
316
  } = Function.dual(
294
317
  2,
295
- <T extends Toolkit, R2>(toolkit: T, middleware: Middleware<R2>): WrapToolkit<T, R2> =>
318
+ <T extends Toolkit, R2, E2>(toolkit: T, middleware: Middleware<R2, E2>): WrapToolkit<T, R2, E2> =>
296
319
  Record.map(toolkit, (tool) =>
297
320
  tool._tag === "LocalTool" ? { ...tool, run: middleware(tool.run, tool.name) } : tool,
298
- ) as WrapToolkit<T, R2>,
321
+ ) as WrapToolkit<T, R2, E2>,
322
+ )
323
+
324
+ // ---------------------------------------------------------------------------
325
+ // describeFailures: map every local tool's error to a model-visible string,
326
+ // opting its failures into recovery. Group tools by recovery policy, wrap
327
+ // each group, then compose.
328
+ // ---------------------------------------------------------------------------
329
+
330
+ type DescribeTool<T extends AnyTool> =
331
+ T extends Tool<infer N, infer I, infer Ev, infer O, any, infer R>
332
+ ? Tool<N, I, Ev, O, string, R>
333
+ : T
334
+
335
+ type DescribeToolkit<T extends ToolMap> = {
336
+ readonly [K in keyof T]: T[K] extends AnyTool ? DescribeTool<T[K]> : T[K]
337
+ }
338
+
339
+ /**
340
+ * Map every local tool's error to a `string`, so `Toolkit.run` absorbs those
341
+ * failures into `ToolResult.Failure` the model reads and adapts to. Pairs
342
+ * with the domain `describe` helpers (`AiError.describe`,
343
+ * `BrowserError.describe`). Non-local kinds pass through.
344
+ *
345
+ * const kit = yield* Toolkit.compose(
346
+ * Toolkit.describeFailures(searchKit, AiError.describe),
347
+ * browserToolkit(session), // failures stay typed
348
+ * )
349
+ */
350
+ export const describeFailures: {
351
+ <E>(describe: (e: E) => string): <T extends Toolkit>(toolkit: T) => DescribeToolkit<T>
352
+ <T extends Toolkit>(toolkit: T, describe: (e: ToolkitE<T>) => string): DescribeToolkit<T>
353
+ } = Function.dual(
354
+ 2,
355
+ <T extends Toolkit>(toolkit: T, describe: (e: any) => string): DescribeToolkit<T> =>
356
+ Record.map(toolkit, (tool) =>
357
+ tool._tag === "LocalTool"
358
+ ? {
359
+ ...tool,
360
+ run: (input: unknown, emit: Emit<unknown>) =>
361
+ (tool as AnyLocalTool).run(input, emit).pipe(Effect.mapError(describe)),
362
+ }
363
+ : tool,
364
+ ) as DescribeToolkit<T>,
299
365
  )
300
366
 
301
367
  // ---------------------------------------------------------------------------
@@ -307,17 +373,26 @@ export type ExecuteOptions = {
307
373
  readonly concurrency?: number | "unbounded"
308
374
  }
309
375
 
310
- /** Execute every provided call. Approval/rejection policy belongs upstream. */
376
+ /**
377
+ * Execute every provided call. Approval/rejection policy belongs upstream.
378
+ *
379
+ * String and `ToolFailed` failures are absorbed into `ToolResult.Failure`
380
+ * (the model reads and adapts to them); every other tool error propagates
381
+ * typed on the stream, so `Exclude<ToolkitE<T>, string | ToolFailed>` is
382
+ * what a caller catches or lets end the run. If one call in a concurrent
383
+ * batch fails the stream, siblings may go unanswered: a loop that catches
384
+ * and continues must reconcile history first (`HistoryCheck.cancelAllPending`).
385
+ */
311
386
  export const run = <T extends Toolkit>(
312
387
  toolkit: T,
313
388
  calls: ReadonlyArray<ToolCall>,
314
389
  options?: ExecuteOptions,
315
- ): Stream.Stream<ToolEvent, never, ToolkitR<T>> =>
390
+ ): Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>> =>
316
391
  Stream.fromIterable(calls).pipe(
317
392
  Stream.flatMap((call) => runOne(toolkit, call), {
318
393
  concurrency: options?.concurrency ?? "unbounded",
319
394
  }),
320
- ) as Stream.Stream<ToolEvent, never, ToolkitR<T>>
395
+ ) as Stream.Stream<ToolEvent, Exclude<ToolkitE<T>, string | ToolFailed>, ToolkitR<T>>
321
396
 
322
397
  const okResult = (call: ToolCall, tool: string, value: unknown): ToolResult =>
323
398
  ToolResult.Ok({ call_id: call.call_id, tool, value })
@@ -325,8 +400,11 @@ const okResult = (call: ToolCall, tool: string, value: unknown): ToolResult =>
325
400
  const outputEvent = (result: ToolResult): Stream.Stream<ToolEvent, never, any> =>
326
401
  Stream.succeed(ToolEvent.Output({ result }))
327
402
 
328
- const runOne = (toolkit: ToolMap, call: ToolCall): Stream.Stream<ToolEvent, never, any> =>
329
- Match.value(toolkit[call.name]).pipe(
403
+ const runOne = (toolkit: ToolMap, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> => {
404
+ // Own-property lookup: a hallucinated call named `constructor` / `toString`
405
+ // must not resolve to a prototype method.
406
+ const tool = Object.hasOwn(toolkit, call.name) ? toolkit[call.name] : undefined
407
+ return Match.value(tool).pipe(
330
408
  // No such tool. Graceful: emit a synthetic Failure so OTHER calls in this
331
409
  // turn still execute. LLMs hallucinate tool names; MCP tools come and go.
332
410
  Match.when(Match.undefined, () =>
@@ -338,23 +416,46 @@ const runOne = (toolkit: ToolMap, call: ToolCall): Stream.Stream<ToolEvent, neve
338
416
  // execution; reaching here means it didn't, so report it rather than crash.
339
417
  Match.orElse(() => outputEvent(nonLocalTool(call))),
340
418
  )
419
+ }
341
420
 
342
- const parseJsonUnknown = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Unknown))
421
+ const describeIssues = (issues: ReadonlyArray<StandardSchemaV1.Issue>): string =>
422
+ issues
423
+ .map((issue) => {
424
+ const path = (issue.path ?? [])
425
+ .map((seg) => (typeof seg === "object" && seg !== null ? String(seg.key) : String(seg)))
426
+ .join(".")
427
+ return path === "" ? issue.message : `${path}: ${issue.message}`
428
+ })
429
+ .join("; ")
430
+ .slice(0, 500)
431
+
432
+ // A decode failure is the model's contract violation: report it as a result
433
+ // (never a stream failure) so the model can correct its next call.
434
+ const runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, unknown, any> =>
435
+ Stream.unwrap(
436
+ decodeCallInput(tool, call).pipe(
437
+ Effect.map(
438
+ Match.type<DecodeResult<unknown>>().pipe(
439
+ Match.tag("parseError", () =>
440
+ outputEvent(validationError(call, "arguments are not valid JSON")),
441
+ ),
442
+ Match.tag("invalid", ({ issues }) =>
443
+ outputEvent(validationError(call, describeIssues(issues))),
444
+ ),
445
+ Match.tag("ok", ({ input }) => runValidated(tool, call, input)),
446
+ Match.exhaustive,
447
+ ),
448
+ ),
449
+ ),
450
+ )
343
451
 
344
- const runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, never, any> =>
452
+ const runValidated = (
453
+ tool: AnyLocalTool,
454
+ call: ToolCall,
455
+ input: unknown,
456
+ ): Stream.Stream<ToolEvent, unknown, any> =>
345
457
  Stream.unwrap(
346
458
  Effect.gen(function* () {
347
- const parsed = yield* parseJsonUnknown(call.arguments).pipe(
348
- Effect.mapError(() => "json_parse_error" as const),
349
- )
350
- const validated = yield* Effect.tryPromise({
351
- try: () => Promise.resolve(tool.inputSchema["~standard"].validate(parsed)),
352
- catch: () => "validation_threw" as const,
353
- })
354
- if (validated.issues !== undefined) {
355
- return Stream.succeed<ToolEvent>(ToolEvent.Output({ result: validationError(call) }))
356
- }
357
-
358
459
  // Per-call queue: `emit` offers events as `run` produces them; the
359
460
  // progress stream drains them in real time; `Queue.end` (in the
360
461
  // `ensuring`) flushes pending events then signals a clean end, which
@@ -363,7 +464,7 @@ const runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, n
363
464
  const queue = yield* Queue.make<unknown, Cause.Done>({ capacity: tool.emitBufferSize })
364
465
  const emit = (event: unknown) => Effect.asVoid(Queue.offer(queue, event))
365
466
  const fiber = yield* tool
366
- .run(validated.value, emit)
467
+ .run(input, emit)
367
468
  .pipe(Effect.ensuring(Queue.end(queue)), Effect.forkScoped)
368
469
 
369
470
  const progress = Stream.fromQueue(queue).pipe(
@@ -371,24 +472,38 @@ const runTool = (tool: AnyLocalTool, call: ToolCall): Stream.Stream<ToolEvent, n
371
472
  )
372
473
  const output = Stream.fromEffect(
373
474
  Fiber.join(fiber).pipe(
374
- Effect.map((value) => ToolEvent.Output({ result: okResult(call, tool.name, value) })),
375
- Effect.catchCause(() =>
376
- Effect.succeed(
377
- ToolEvent.Output({ result: executionError(call, "Tool execution failed") }),
378
- ),
379
- ),
475
+ Effect.matchCauseEffect({
476
+ onSuccess: (value: unknown) =>
477
+ Effect.succeed(ToolEvent.Output({ result: okResult(call, tool.name, value) })),
478
+ onFailure: (cause: Cause.Cause<unknown>) => routeCause(call, cause),
479
+ }),
380
480
  ),
381
481
  )
382
482
  return progress.pipe(Stream.concat(output))
383
483
  }),
384
- ).pipe(
385
- // Backstop for the input-parsing failures (`json_parse_error`,
386
- // `validation_threw`) and any defect before the run fiber is forked.
387
- Stream.catchCause(() =>
388
- Stream.succeed(ToolEvent.Output({ result: executionError(call, "Tool execution failed") })),
389
- ),
390
484
  )
391
485
 
486
+ // A clean single string / ToolFailed failure is absorbed as a model-visible
487
+ // result; interruption, defects, and typed non-sentinel errors propagate.
488
+ const routeCause = (
489
+ call: ToolCall,
490
+ cause: Cause.Cause<unknown>,
491
+ ): Effect.Effect<ToolEvent, unknown> => {
492
+ const only = cause.reasons.length === 1 ? cause.reasons[0] : undefined
493
+ if (only !== undefined && Cause.isFailReason(only)) {
494
+ const error = only.error
495
+ if (typeof error === "string") {
496
+ return Effect.succeed(ToolEvent.Output({ result: failed(call, "tool_failed", error) }))
497
+ }
498
+ if (error instanceof ToolFailed) {
499
+ return Effect.succeed(
500
+ ToolEvent.Output({ result: failed(call, error.kind ?? "tool_failed", error.message) }),
501
+ )
502
+ }
503
+ }
504
+ return Effect.failCause(cause)
505
+ }
506
+
392
507
  // ---------------------------------------------------------------------------
393
508
  // `continueWithResults` - bridge from a `Stream<ToolEvent>` to the loop's
394
509
  // emit shape. Drains the stream to the consumer in real-time, taps every
@@ -455,19 +570,17 @@ export const collectResults = <E, R>(
455
570
  export const continueWithResults: {
456
571
  <S>(
457
572
  build: (results: ReadonlyArray<ToolResult>) => S,
458
- ): <R>(
459
- stream: Stream.Stream<ToolEvent, never, R>,
460
- ) => Stream.Stream<Loop.Step<ToolEvent, S>, never, R>
461
- <S, R>(
462
- stream: Stream.Stream<ToolEvent, never, R>,
573
+ ): <E, R>(stream: Stream.Stream<ToolEvent, E, R>) => Stream.Stream<Loop.Step<ToolEvent, S>, E, R>
574
+ <S, E, R>(
575
+ stream: Stream.Stream<ToolEvent, E, R>,
463
576
  build: (results: ReadonlyArray<ToolResult>) => S,
464
- ): Stream.Stream<Loop.Step<ToolEvent, S>, never, R>
577
+ ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R>
465
578
  } = Function.dual(
466
579
  2,
467
- <S, R>(
468
- stream: Stream.Stream<ToolEvent, never, R>,
580
+ <S, E, R>(
581
+ stream: Stream.Stream<ToolEvent, E, R>,
469
582
  build: (results: ReadonlyArray<ToolResult>) => S,
470
- ): Stream.Stream<Loop.Step<ToolEvent, S>, never, R> =>
583
+ ): Stream.Stream<Loop.Step<ToolEvent, S>, E, R> =>
471
584
  Stream.unwrap(
472
585
  Effect.gen(function* () {
473
586
  const ref = yield* Ref.make<ReadonlyArray<ToolResult>>([])
@@ -0,0 +1,70 @@
1
+ import { Context, type Duration, Effect } from "effect"
2
+ import type * as AiError from "../domain/AiError.js"
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Format
6
+ // ---------------------------------------------------------------------------
7
+
8
+ /** Representation to return for a page. `markdown` is the default. */
9
+ export type ReadFormat = "markdown" | "html"
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Request
13
+ // ---------------------------------------------------------------------------
14
+
15
+ /**
16
+ * Cross-provider read request: one URL in, clean content out. Provider-specific
17
+ * knobs live on the provider-typed request.
18
+ */
19
+ export type CommonReadRequest = {
20
+ /** The page to read. */
21
+ readonly url: string
22
+ /** Output representation. Defaults to `markdown`. See {@link ReadFormat}. */
23
+ readonly format?: ReadFormat
24
+ /** Upper bound on time spent fetching the page. */
25
+ readonly timeout?: Duration.Duration
26
+ }
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Response
30
+ // ---------------------------------------------------------------------------
31
+
32
+ /** One normalized read result. */
33
+ export type ReadResponse = {
34
+ /** The URL that was read. */
35
+ readonly url: string
36
+ /** Extracted content in the requested {@link ReadFormat}. */
37
+ readonly content: string
38
+ /** Page title, when reported. */
39
+ readonly title?: string
40
+ /** Links discovered on the page, when returned. */
41
+ readonly links?: ReadonlyArray<string>
42
+ /** The provider-native response. */
43
+ readonly raw: unknown
44
+ }
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Service + helper
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /**
51
+ * The portable read surface: one operation, `read`. Every implementor can
52
+ * answer, so this capability needs no marker tags.
53
+ */
54
+ export type WebReadService = {
55
+ readonly read: (request: CommonReadRequest) => Effect.Effect<ReadResponse, AiError.AiError>
56
+ }
57
+
58
+ /**
59
+ * Generic read service tag. Yield this for provider-portable code; yield a
60
+ * provider tag for provider-specific knobs. A provider `layer` registers both.
61
+ */
62
+ export class WebRead extends Context.Service<WebRead, WebReadService>()(
63
+ "@betalyra/effect-uai/WebRead",
64
+ ) {}
65
+
66
+ /** Read one URL against whichever provider Layer is in scope. */
67
+ export const read = (
68
+ request: CommonReadRequest,
69
+ ): Effect.Effect<ReadResponse, AiError.AiError, WebRead> =>
70
+ Effect.flatMap(WebRead.asEffect(), (s) => s.read(request))
@@ -0,0 +1,96 @@
1
+ import { Effect, Schema } from "effect"
2
+ import type * as AiError from "../domain/AiError.js"
3
+ import * as Tool from "../tool/Tool.js"
4
+ import { read, type ReadFormat, type ReadResponse, WebRead } from "./WebRead.js"
5
+
6
+ /**
7
+ * Model-facing argument schema. Deliberately narrow: the URL is the only
8
+ * thing that is genuinely the model's call. Output format and the content
9
+ * cost ceiling are app policy, pinned on the constructor instead, so they
10
+ * are guaranteed rather than left to the model to remember each call.
11
+ */
12
+ const WebReadToolArgs = Schema.Struct({
13
+ url: Schema.String.annotate({
14
+ description: "Absolute URL of the page to read.",
15
+ }),
16
+ })
17
+
18
+ export type WebReadToolArgs = typeof WebReadToolArgs.Type
19
+
20
+ /** `title / url` header plus the extracted content - what a model reads best. */
21
+ const defaultRender = (response: ReadResponse, maxChars: number): string => {
22
+ const header = response.title === undefined ? response.url : `${response.title}\n${response.url}`
23
+ const body =
24
+ response.content.length > maxChars
25
+ ? `${response.content.slice(0, maxChars)}\n[content truncated at ${maxChars} characters]`
26
+ : response.content
27
+ return `${header}\n\n${body}`
28
+ }
29
+
30
+ export type WebReadToolOptions = {
31
+ /** Tool name the model sees. Default `"web_read"`. */
32
+ readonly name?: string
33
+ /**
34
+ * App-fixed representation for every read - a policy choice, not a model
35
+ * knob. Default `"markdown"` (what models read best).
36
+ */
37
+ readonly format?: ReadFormat
38
+ /**
39
+ * App-fixed ceiling on rendered content characters per call - a context
40
+ * cost guard, not exposed to the model. Default `20000`.
41
+ */
42
+ readonly maxChars?: number
43
+ /**
44
+ * Override how a read response is rendered into the model-facing string.
45
+ * Default: `title / url` header plus the content, truncated at `maxChars`.
46
+ */
47
+ readonly render?: (response: ReadResponse) => string
48
+ }
49
+
50
+ /**
51
+ * The canonical web-read tool. Its `R` is just `WebRead`, so providing any
52
+ * read-provider Layer satisfies it and the model-facing contract (name,
53
+ * description, schema) is identical no matter which backend answers - swap
54
+ * `FirecrawlRead.layer` for `JinaRead.layer` and the tool the model sees
55
+ * does not change. Drops straight into a `Toolkit` next to `webSearchTool`
56
+ * (search finds the URLs, read fetches the ones worth reading in full).
57
+ *
58
+ * `Output` is the rendered page; failures stay typed on the `AiError`
59
+ * channel. To let the model recover from a failed read, wrap the toolkit in
60
+ * `Toolkit.describeFailures(AiError.describe)`.
61
+ *
62
+ * This is a simple default implementation for quick use. For more elaborate
63
+ * cases (a different output contract, more model-facing knobs), build your
64
+ * own tool with `Tool.make` over the same `read` helper.
65
+ */
66
+ export const webReadTool = (
67
+ options?: WebReadToolOptions,
68
+ ): Tool.Tool<string, WebReadToolArgs, never, string, AiError.AiError, WebRead> => {
69
+ const name = options?.name ?? "web_read"
70
+ const format = options?.format ?? "markdown"
71
+ const maxChars = options?.maxChars ?? 20_000
72
+ const render = options?.render ?? ((response: ReadResponse) => defaultRender(response, maxChars))
73
+ return Tool.make({
74
+ name,
75
+ description:
76
+ "Read a web page and return its content as clean text. Use it to read a promising URL in full (e.g. one found via search).",
77
+ inputSchema: Tool.fromEffectSchema(WebReadToolArgs),
78
+ // The call is wrapped in a tracing span so agent traces show the tool
79
+ // invocation (url, format, content length) nested under the model turn.
80
+ // `withSpan` is a no-op until a Tracer is installed, so this is free by
81
+ // default and adds nothing to the requirements.
82
+ run: (args) =>
83
+ read({ url: args.url, format }).pipe(
84
+ Effect.tap((r) => Effect.annotateCurrentSpan("web_read.content_chars", r.content.length)),
85
+ Effect.map(render),
86
+ Effect.withSpan(name, {
87
+ kind: "client",
88
+ attributes: {
89
+ "web_read.url": args.url,
90
+ "web_read.format": format,
91
+ "web_read.max_chars": maxChars,
92
+ },
93
+ }),
94
+ ),
95
+ })
96
+ }
@@ -1,4 +1,5 @@
1
1
  import { Effect, Schema } from "effect"
2
+ import type * as AiError from "../domain/AiError.js"
2
3
  import * as Tool from "../tool/Tool.js"
3
4
  import { search, SearchRecency, type SearchResult, WebSearch } from "./WebSearch.js"
4
5
 
@@ -59,14 +60,19 @@ export type WebSearchToolOptions = {
59
60
  * `LanguageModel` tool-calling loop.
60
61
  *
61
62
  * The model controls only `query` and `recency`; domain scoping and the
62
- * result cap are app policy, fixed here on the constructor. `Output` is
63
- * rendered text, not raw `SearchResult[]`: the model reads the list, while
64
- * the app still gets the structured results through the normal tool-result
65
- * channel. Pass `render` to change the format.
63
+ * result cap are app policy, fixed here on the constructor. `Output` is the
64
+ * rendered text (the model reads a numbered list better than raw
65
+ * `SearchResult[]`; pass `render` to change the format); failures stay typed
66
+ * on the `AiError` channel. To let the model recover from a failed search,
67
+ * wrap the toolkit in `Toolkit.describeFailures(AiError.describe)`.
68
+ *
69
+ * This is a simple default implementation for quick use. For more elaborate
70
+ * cases (a different output contract, more model-facing knobs), build your
71
+ * own tool with `Tool.make` over the same `search` helper.
66
72
  */
67
73
  export const webSearchTool = (
68
74
  options?: WebSearchToolOptions,
69
- ): Tool.Tool<string, WebSearchToolArgs, never, string, WebSearch> => {
75
+ ): Tool.Tool<string, WebSearchToolArgs, never, string, AiError.AiError, WebSearch> => {
70
76
  const name = options?.name ?? "web_search"
71
77
  const render = options?.render ?? defaultRender
72
78
  const maxResults = options?.maxResults ?? 5
@@ -1 +0,0 @@
1
- {"version":3,"file":"Tool-0XUIT2A2.d.mts","names":[],"sources":["../src/tool/Tool.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolEvent-DYrAUJGs.d.mts","names":[],"sources":["../src/tool/ToolEvent.ts"],"mappings":";;;;;;;;KAaY,SAAA,GAAY,IAAA,CAAK,UAAA;EAC3B,iBAAA;IAAA,SACW,OAAA;IAAA,SACA,IAAA;IAAA,SACA,SAAA;EAAA;EAEX,QAAA;IAAA,SACW,OAAA;IAAA,SACA,IAAA;IAAA,SACA,IAAA;EAAA;EAEX,MAAA;IAAA,SACW,MAAA,EAAQ,UAAA;EAAA;AAAA;;;;;AAWrB;;;cAAa,SAAA;EAAA;;;;;;;;;;;;;;qBAXQ,UAAA;EAAA;EAAA;;;;;;;;;;;;;;;;qBAAA,UAAA;EAAA;IAAA;;;;;;;;;;;;;;;;;;yBAAA,UAAA;MAAA;IAAA;;;;;;;;;;;;uBAAA,UAAA;IAAA;;;;;;;;;;;;;;;;yBAAA,UAAA;MAAA;IAAA;;;;;;;;;;;;uBAAA,UAAA;IAAA;;;cAaR,mBAAA,GAAmB,CAAA,cAAA,CAAA;EAAA;;;;;cACnB,UAAA,GAAU,CAAA,cAAA,CAAA;EAAA;;;;;cACV,QAAA,GAAQ,CAAA,cAAA,CAAA;EAAA;mBAfA,UAAA;AAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolResult-BZd-cEWW.d.mts","names":[],"sources":["../src/tool/ToolResult.ts"],"mappings":";;;;;;;;KAqBY,UAAA,GAAa,IAAA,CAAK,UAAA;EAC5B,EAAA;IAAA,SACW,OAAA;IAAA,SACA,IAAA;IAAA,SACA,KAAA;EAAA;EAEX,OAAA;IAAA,SACW,OAAA;IAAA,SACA,IAAA;IAAA,SACA,IAAA;IAAA,SACA,MAAA;EAAA;AAAA;;;;;;;;AAYb;cAAa,UAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAEA,IAAA,GAAI,CAAA,cAAA,CAAA;EAAA;;;;;cACJ,SAAA,GAAS,CAAA,cAAA,CAAA;EAAA;;;;;;cAMT,MAAA,GAAU,IAAA,EAAM,QAAA,EAAU,IAAA,UAAc,MAAA,cAAkB,UAAA;;cAS1D,MAAA,GAAU,IAAA,EAAM,QAAA,EAAU,MAAA,cAAkB,UAAA;;cAI5C,SAAA,GAAa,IAAA,EAAM,QAAA,EAAU,MAAA,cAAkB,UAAA;;cAI/C,cAAA,GAAkB,IAAA,EAAM,QAAA,EAAU,MAAA,aAAiB,UAAA;;;;;;;cASnD,YAAA,GAAgB,IAAA,EAAM,QAAA,EAAU,MAAA,cAAkB,UAAA;;;;;;cAYlD,eAAA,GAAmB,IAAA,EAAM,QAAA,EAAU,MAAA,cAAkB,UAAA;AAAA,cAOrD,gBAAA,GAAoB,CAAA,EAAG,UAAA,KAAa,cAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Toolkit-BKC8b8Xo.d.mts","names":[],"sources":["../src/tool/Toolkit.ts"],"mappings":"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"}