@effect-uai/core 0.17.0 → 0.19.0-dev-20261109183959
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.
- package/README.md +5 -5
- package/dist/Content-D_Qfn8Ja.d.mts +48 -0
- package/dist/Content-D_Qfn8Ja.d.mts.map +1 -0
- package/dist/File-B923KvZ9.d.mts +46 -0
- package/dist/File-B923KvZ9.d.mts.map +1 -0
- package/dist/{Image-Cv6iBLTy.d.mts → Image-Bzc1H_UK.d.mts} +19 -8
- package/dist/Image-Bzc1H_UK.d.mts.map +1 -0
- package/dist/{Items-C7MyrYPX.d.mts → Items-Bvx8s9QH.d.mts} +109 -16
- package/dist/Items-Bvx8s9QH.d.mts.map +1 -0
- package/dist/{Loop-ChpaO-ya.d.mts → Loop-CeA0AelN.d.mts} +7 -6
- package/dist/Loop-CeA0AelN.d.mts.map +1 -0
- package/dist/Tool-Bkryw6-h.d.mts +206 -0
- package/dist/Tool-Bkryw6-h.d.mts.map +1 -0
- package/dist/ToolEvent-9inLx5cI.d.mts +22 -0
- package/dist/ToolEvent-9inLx5cI.d.mts.map +1 -0
- package/dist/ToolOutput-BNTIQgGB.d.mts +151 -0
- package/dist/ToolOutput-BNTIQgGB.d.mts.map +1 -0
- package/dist/Toolkit-D2VGeD5R.d.mts +212 -0
- package/dist/Toolkit-D2VGeD5R.d.mts.map +1 -0
- package/dist/browser/BrowserTool.d.mts +84 -22
- package/dist/browser/BrowserTool.d.mts.map +1 -1
- package/dist/browser/BrowserTool.mjs +5 -0
- package/dist/browser/BrowserTool.mjs.map +1 -1
- package/dist/capabilities/Capabilities.d.mts +1 -1
- package/dist/decision-model/Decision.test.mjs +4 -4
- package/dist/dist-B5K-y4Be.mjs +191 -0
- package/dist/dist-B5K-y4Be.mjs.map +1 -0
- package/dist/dist-CorQTQL-.mjs +1533 -0
- package/dist/dist-CorQTQL-.mjs.map +1 -0
- package/dist/dist-DNeuh84d.mjs +7 -0
- package/dist/dist-DNeuh84d.mjs.map +1 -0
- package/dist/dist-DzeSyeYF.mjs +10253 -0
- package/dist/dist-DzeSyeYF.mjs.map +1 -0
- package/dist/domain/File.d.mts +2 -0
- package/dist/domain/File.mjs +51 -0
- package/dist/domain/File.mjs.map +1 -0
- package/dist/domain/Image.d.mts +2 -2
- package/dist/domain/Image.mjs +12 -2
- package/dist/domain/Image.mjs.map +1 -1
- package/dist/domain/Items.d.mts +2 -2
- package/dist/domain/Items.mjs +65 -4
- package/dist/domain/Items.mjs.map +1 -1
- package/dist/domain/Realtime.d.mts +72 -490
- package/dist/domain/Realtime.d.mts.map +1 -1
- package/dist/domain/Research.d.mts +1 -1
- package/dist/domain/Transcript.test.mjs +3 -3
- package/dist/domain/Turn.d.mts +57 -262
- package/dist/domain/Turn.d.mts.map +1 -1
- package/dist/domain/Turn.mjs +1 -1
- package/dist/domain/Turn.test.mjs +2 -2
- package/dist/embedding-model/Embedding.d.mts +1 -1
- package/dist/embedding-model/EmbeddingModel.test.mjs +2 -2
- package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
- package/dist/image-generator/ImageGenerator.d.mts +10 -66
- package/dist/image-generator/ImageGenerator.d.mts.map +1 -1
- package/dist/index.C4tKZ0yW-LuFnBtmu.mjs +7042 -0
- package/dist/index.C4tKZ0yW-LuFnBtmu.mjs.map +1 -0
- package/dist/index.d.mts +10 -9
- package/dist/index.mjs +7 -6
- package/dist/language-model/LanguageModel.d.mts +2 -2
- package/dist/language-model/LanguageModel.test.mjs +2 -2
- package/dist/loop/Loop.d.mts +1 -1
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.mjs +3 -3
- package/dist/math/Probability.test.mjs +2 -2
- package/dist/messenger/Messenger.d.mts +43 -289
- package/dist/messenger/Messenger.d.mts.map +1 -1
- package/dist/messenger/MessengerAdapter.test.mjs +3 -3
- package/dist/music-generator/MusicGenerator.test.mjs +3 -3
- package/dist/observability/Metrics.d.mts +1 -1
- package/dist/observability/Metrics.test.mjs +3 -3
- package/dist/observability/Telemetry.d.mts +1 -1
- package/dist/observability/Telemetry.mjs +1 -1
- package/dist/observability/Telemetry.mjs.map +1 -1
- package/dist/observability/Telemetry.test.mjs +4 -4
- package/dist/observability/Telemetry.test.mjs.map +1 -1
- package/dist/realtime/RealtimeSession.d.mts +1 -1
- package/dist/realtime/RealtimeSession.test.mjs +4 -4
- package/dist/retry/Retry.test.mjs +2 -2
- package/dist/sandbox/Sandbox.d.mts +32 -237
- package/dist/sandbox/Sandbox.d.mts.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +3 -3
- package/dist/streaming/JSONL.test.mjs +2 -2
- package/dist/streaming/Multipart.d.mts +1 -1
- package/dist/streaming/Multipart.mjs +3 -2
- package/dist/streaming/Multipart.mjs.map +1 -1
- package/dist/streaming/Multipart.test.mjs +28 -0
- package/dist/streaming/Multipart.test.mjs.map +1 -0
- package/dist/streaming/SSE.d.mts +1 -1
- package/dist/streaming/SSE.mjs +2 -2
- package/dist/streaming/SSE.mjs.map +1 -1
- package/dist/streaming/SSE.test.mjs +2 -2
- package/dist/streaming/Settle.test.mjs +3 -3
- package/dist/streaming/WebSocketSession.d.mts +5 -3
- package/dist/streaming/WebSocketSession.d.mts.map +1 -1
- package/dist/streaming/WebSocketSession.mjs +22 -10
- package/dist/streaming/WebSocketSession.mjs.map +1 -1
- package/dist/streaming/WebSocketSession.test.mjs +41 -5
- package/dist/streaming/WebSocketSession.test.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.test.mjs +2 -2
- package/dist/testing/FakeWebSocket.d.mts +5 -1
- package/dist/testing/FakeWebSocket.d.mts.map +1 -1
- package/dist/testing/FakeWebSocket.mjs +12 -6
- package/dist/testing/FakeWebSocket.mjs.map +1 -1
- package/dist/testing/MockMessenger.d.mts +19 -134
- package/dist/testing/MockMessenger.d.mts.map +1 -1
- package/dist/testing/MockProvider.d.mts +1 -1
- package/dist/testing/MockSandbox.d.mts +65 -446
- package/dist/testing/MockSandbox.d.mts.map +1 -1
- package/dist/tool/Content.d.mts +2 -0
- package/dist/tool/Content.mjs +46 -0
- package/dist/tool/Content.mjs.map +1 -0
- package/dist/tool/Content.test.d.mts +1 -0
- package/dist/tool/Content.test.mjs +48 -0
- package/dist/tool/Content.test.mjs.map +1 -0
- package/dist/tool/HistoryCheck.d.mts +6 -15
- package/dist/tool/HistoryCheck.d.mts.map +1 -1
- package/dist/tool/HistoryCheck.mjs +10 -24
- package/dist/tool/HistoryCheck.mjs.map +1 -1
- package/dist/tool/Tool.d.mts +2 -2
- package/dist/tool/Tool.mjs +55 -130
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/Tool.test.mjs +222 -178
- package/dist/tool/Tool.test.mjs.map +1 -1
- package/dist/tool/ToolDecision.d.mts +164 -0
- package/dist/tool/ToolDecision.d.mts.map +1 -0
- package/dist/tool/ToolDecision.mjs +146 -0
- package/dist/tool/ToolDecision.mjs.map +1 -0
- package/dist/tool/ToolDecision.test.d.mts +1 -0
- package/dist/tool/ToolDecision.test.mjs +330 -0
- package/dist/tool/ToolDecision.test.mjs.map +1 -0
- package/dist/tool/ToolEvent.d.mts +2 -2
- package/dist/tool/ToolEvent.mjs +2 -34
- package/dist/tool/ToolEvent.mjs.map +1 -1
- package/dist/tool/ToolOutput.d.mts +2 -0
- package/dist/tool/ToolOutput.mjs +128 -0
- package/dist/tool/ToolOutput.mjs.map +1 -0
- package/dist/tool/ToolOutput.test.d.mts +1 -0
- package/dist/tool/ToolOutput.test.mjs +153 -0
- package/dist/tool/ToolOutput.test.mjs.map +1 -0
- package/dist/tool/Toolkit.d.mts +2 -2
- package/dist/tool/Toolkit.mjs +230 -165
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/tool/Toolkit.test.mjs +433 -391
- package/dist/tool/Toolkit.test.mjs.map +1 -1
- package/dist/transcriber/Transcriber.test.mjs +3 -3
- package/dist/web-read/WebReadTool.d.mts +2 -2
- package/dist/web-read/WebReadTool.mjs +1 -0
- package/dist/web-read/WebReadTool.mjs.map +1 -1
- package/dist/web-search/WebSearchTool.d.mts +2 -2
- package/dist/web-search/WebSearchTool.mjs +1 -0
- package/dist/web-search/WebSearchTool.mjs.map +1 -1
- package/package.json +18 -10
- package/src/browser/BrowserTool.ts +5 -0
- package/src/domain/File.ts +79 -0
- package/src/domain/Image.ts +14 -4
- package/src/domain/Items.ts +39 -3
- package/src/embedding-model/EmbeddingModel.test.ts +2 -2
- package/src/index.ts +3 -2
- package/src/loop/Loop.ts +25 -14
- package/src/observability/Telemetry.test.ts +1 -1
- package/src/observability/Telemetry.ts +2 -2
- package/src/streaming/Multipart.test.ts +26 -0
- package/src/streaming/Multipart.ts +6 -5
- package/src/streaming/SSE.ts +2 -2
- package/src/streaming/WebSocketSession.test.ts +54 -2
- package/src/streaming/WebSocketSession.ts +27 -6
- package/src/structured-format/StructuredFormat.ts +1 -1
- package/src/testing/FakeWebSocket.ts +21 -6
- package/src/tool/Content.test.ts +40 -0
- package/src/tool/Content.ts +38 -0
- package/src/tool/HistoryCheck.ts +21 -26
- package/src/tool/Tool.test.ts +240 -241
- package/src/tool/Tool.ts +204 -309
- package/src/tool/ToolDecision.test.ts +301 -0
- package/src/tool/ToolDecision.ts +360 -0
- package/src/tool/ToolEvent.ts +21 -41
- package/src/tool/ToolOutput.test.ts +132 -0
- package/src/tool/ToolOutput.ts +198 -0
- package/src/tool/Toolkit.test.ts +594 -454
- package/src/tool/Toolkit.ts +631 -356
- package/src/web-read/WebReadTool.ts +2 -1
- package/src/web-search/WebSearchTool.ts +2 -1
- package/dist/Image-Cv6iBLTy.d.mts.map +0 -1
- package/dist/Items-C7MyrYPX.d.mts.map +0 -1
- package/dist/Loop-ChpaO-ya.d.mts.map +0 -1
- package/dist/Tool-BF78QWtn.d.mts +0 -314
- package/dist/Tool-BF78QWtn.d.mts.map +0 -1
- package/dist/ToolEvent-Di4a723B.d.mts +0 -144
- package/dist/ToolEvent-Di4a723B.d.mts.map +0 -1
- package/dist/ToolResult-BsYQRjmW.d.mts +0 -152
- package/dist/ToolResult-BsYQRjmW.d.mts.map +0 -1
- package/dist/Toolkit-tlL0F2YR.d.mts +0 -193
- package/dist/Toolkit-tlL0F2YR.d.mts.map +0 -1
- package/dist/dist-BaenoU1A.mjs +0 -145
- package/dist/dist-BaenoU1A.mjs.map +0 -1
- package/dist/dist-nohcnkD6.mjs +0 -182
- package/dist/dist-nohcnkD6.mjs.map +0 -1
- package/dist/magic-string.es-CB2hOw9v.mjs +0 -1014
- package/dist/magic-string.es-CB2hOw9v.mjs.map +0 -1
- package/dist/test.DNmyFkvJ-YPzDR0Co.mjs +0 -10254
- package/dist/test.DNmyFkvJ-YPzDR0Co.mjs.map +0 -1
- package/dist/tool/Approval.d.mts +0 -113
- package/dist/tool/Approval.d.mts.map +0 -1
- package/dist/tool/Approval.mjs +0 -80
- package/dist/tool/Approval.mjs.map +0 -1
- package/dist/tool/Approval.test.mjs +0 -371
- package/dist/tool/Approval.test.mjs.map +0 -1
- package/dist/tool/ToolResult.d.mts +0 -2
- package/dist/tool/ToolResult.mjs +0 -85
- package/dist/tool/ToolResult.mjs.map +0 -1
- package/dist/utils-CiH_DjFW.mjs +0 -3918
- package/dist/utils-CiH_DjFW.mjs.map +0 -1
- package/src/tool/Approval.test.ts +0 -400
- package/src/tool/Approval.ts +0 -181
- package/src/tool/ToolResult.ts +0 -114
- /package/dist/{tool/Approval.test.d.mts → streaming/Multipart.test.d.mts} +0 -0
package/dist/tool/Toolkit.mjs
CHANGED
|
@@ -1,224 +1,289 @@
|
|
|
1
1
|
import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Array, Cause, Data, Effect,
|
|
2
|
+
import { ToolFailed, decodeCallInput, isStreamed, toDescriptors, toStream, withFailures as withFailures$1 } from "./Tool.mjs";
|
|
3
|
+
import { appendToHistory, getToolCalls } from "../domain/Turn.mjs";
|
|
4
|
+
import { next as next$1, value } from "../loop/Loop.mjs";
|
|
5
|
+
import { Value } from "./Content.mjs";
|
|
6
|
+
import { CoreError, InputInvalid, OutputInvalid, UnknownTool, encoded, failure, success, value as value$1 } from "./ToolOutput.mjs";
|
|
7
|
+
import { Array, Cause, Data, Effect, Function, Match, Option, Predicate, Record, Ref, Result, Schema, Stream } from "effect";
|
|
8
8
|
//#region src/tool/Toolkit.ts
|
|
9
9
|
var Toolkit_exports = /* @__PURE__ */ __exportAll({
|
|
10
10
|
DuplicateToolName: () => DuplicateToolName,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
UndeclaredFailure: () => UndeclaredFailure,
|
|
12
|
+
appendResults: () => appendResults,
|
|
13
|
+
close: () => close,
|
|
14
|
+
complete: () => complete,
|
|
13
15
|
compose: () => compose,
|
|
14
|
-
|
|
16
|
+
continueWith: () => continueWith,
|
|
17
|
+
decode: () => decode,
|
|
15
18
|
describeFailures: () => describeFailures,
|
|
16
19
|
descriptors: () => descriptors,
|
|
17
20
|
fromArray: () => fromArray,
|
|
21
|
+
isOutcome: () => isOutcome,
|
|
18
22
|
make: () => make,
|
|
19
23
|
makeNamespaced: () => makeNamespaced,
|
|
20
24
|
namespace: () => namespace,
|
|
25
|
+
next: () => next,
|
|
26
|
+
output: () => output,
|
|
21
27
|
run: () => run,
|
|
28
|
+
split: () => split,
|
|
29
|
+
withFailures: () => withFailures,
|
|
22
30
|
wrap: () => wrap
|
|
23
31
|
});
|
|
24
32
|
const indexByName = (tools) => Object.fromEntries(tools.map((tool) => [tool.name, tool]));
|
|
25
|
-
/**
|
|
26
|
-
* Index literal tools by their `name`. Duplicate names are a compile error
|
|
27
|
-
* (`UniqueTools`). Names are not otherwise checked: a malformed name is a
|
|
28
|
-
* developer typo in a literal that the provider rejects clearly on first use,
|
|
29
|
-
* and there is no single cross-provider name rule to enforce here anyway.
|
|
30
|
-
*/
|
|
31
33
|
const buildStatic = (tools) => indexByName(tools);
|
|
34
|
+
/** Index tools by `name`. A repeated literal name is a compile error. */
|
|
32
35
|
const make = (...tools) => buildStatic(tools);
|
|
33
|
-
/**
|
|
34
|
-
* Build a toolkit from a dynamically-sourced array (MCP server, plugin). Names
|
|
35
|
-
* are not validated (MCP legitimately uses dots / >64 chars — sanitize at the
|
|
36
|
-
* provider adapter); same-named tools resolve last-wins. Cross-source clashes
|
|
37
|
-
* are caught by `compose`.
|
|
38
|
-
*/
|
|
36
|
+
/** A toolkit from a dynamic source (MCP server, plugin). Same-named tools: last wins. */
|
|
39
37
|
const fromArray = (tools) => indexByName(tools);
|
|
40
|
-
/** Render the provider-facing descriptors for a toolkit on demand. */
|
|
41
38
|
const descriptors = (toolkit) => toDescriptors(Object.values(toolkit));
|
|
42
|
-
/**
|
|
43
|
-
* Two composed toolkits resolved to the same final name. Carries the colliding
|
|
44
|
-
* `sources` (the positions of the clashing toolkits) so the error says *which*
|
|
45
|
-
* inputs collided. Raised at runtime by `compose` for dynamic sources; static
|
|
46
|
-
* collisions are caught earlier at compile time.
|
|
47
|
-
*/
|
|
39
|
+
/** Two composed toolkits share a final name; `sources` are their positions. */
|
|
48
40
|
var DuplicateToolName = class extends Data.TaggedError("DuplicateToolName") {};
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
*/
|
|
55
|
-
const namespace = (prefix, toolkit) => {
|
|
56
|
-
const renamed = Array.map(Record.values(toolkit), (tool) => ({
|
|
57
|
-
...tool,
|
|
58
|
-
name: `${prefix}__${tool.name}`
|
|
59
|
-
}));
|
|
60
|
-
return indexByName(renamed);
|
|
61
|
-
};
|
|
62
|
-
/** `namespace(prefix, make(...tools))` in one step, for static namespaced tools. */
|
|
41
|
+
/** Prefix every tool name with `<namespace>__`, e.g. to keep two `search` tools apart. */
|
|
42
|
+
const namespace = (prefix, toolkit) => indexByName(Array.map(Record.values(toolkit), (tool) => ({
|
|
43
|
+
...tool,
|
|
44
|
+
name: `${prefix}__${tool.name}`
|
|
45
|
+
})));
|
|
46
|
+
/** `namespace(prefix, make(...tools))` in one step. */
|
|
63
47
|
const makeNamespaced = (prefix, ...tools) => namespace(prefix, buildStatic(tools));
|
|
64
48
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* duplicate final name fails with `DuplicateToolName` (naming the colliding
|
|
68
|
-
* input positions) instead of silently overwriting or 400-ing later at the
|
|
69
|
-
* provider. Statically-known collisions are additionally a compile error.
|
|
49
|
+
* Join independently built toolkits. A duplicate final name fails with
|
|
50
|
+
* `DuplicateToolName`; statically known duplicates are a compile error too.
|
|
70
51
|
*/
|
|
71
|
-
const compose = (...toolkits) =>
|
|
52
|
+
const compose = (...toolkits) => {
|
|
72
53
|
const kits = toolkits;
|
|
73
54
|
const labelled = Array.flatMap(kits, (kit, i) => Array.map(Record.keys(kit), (name) => ({
|
|
74
55
|
name,
|
|
75
56
|
source: `toolkit-${i}`
|
|
76
57
|
})));
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
58
|
+
return Array.findFirst(Record.toEntries(Array.groupBy(labelled, (entry) => entry.name)), ([, group]) => group.length > 1).pipe(Option.match({
|
|
59
|
+
onNone: () => Effect.succeed(Array.reduce(kits, {}, (acc, kit) => ({
|
|
60
|
+
...acc,
|
|
61
|
+
...kit
|
|
62
|
+
}))),
|
|
63
|
+
onSome: ([name, group]) => Effect.fail(new DuplicateToolName({
|
|
81
64
|
name,
|
|
82
65
|
sources: Array.map(group, (e) => e.source)
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
return Array.reduce(kits, {}, (acc, kit) => ({
|
|
86
|
-
...acc,
|
|
87
|
-
...kit
|
|
66
|
+
}))
|
|
88
67
|
}));
|
|
89
|
-
}
|
|
68
|
+
};
|
|
69
|
+
const mapLocal = (toolkit, f) => Record.map(toolkit, (tool) => tool._tag === "LocalTool" ? f(tool) : tool);
|
|
90
70
|
/**
|
|
91
|
-
* Wrap every local tool's `run
|
|
92
|
-
* `
|
|
93
|
-
*
|
|
94
|
-
* `pipe` - inner-first, so the last `wrap` applied is the outermost and runs
|
|
95
|
-
* first:
|
|
71
|
+
* Wrap every local tool's `run`. Stacks with `pipe`, inner first: the last
|
|
72
|
+
* `wrap` runs first. The middleware sees a stream; an `Effect` tool stays one,
|
|
73
|
+
* with the stream's last value.
|
|
96
74
|
*
|
|
97
75
|
* const observed = pipe(toolkit, Toolkit.wrap(logging), Toolkit.wrap(authz))
|
|
98
|
-
* Toolkit.run(observed, calls) // authz runs, then logging, then the tool
|
|
99
|
-
*
|
|
100
|
-
* A middleware that fails with `string` / `ToolFailed` is absorbed by the
|
|
101
|
-
* executor into a `ToolResult.Failure`; any other error propagates typed. Use
|
|
102
|
-
* `describeFailures` for the total "everything to the model" case, or the
|
|
103
|
-
* `Approval` flow for a distinct "denied" verdict.
|
|
104
76
|
*/
|
|
105
|
-
const wrap = Function.dual(2, (toolkit, middleware) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
77
|
+
const wrap = Function.dual(2, (toolkit, middleware) => mapLocal(toolkit, (tool) => {
|
|
78
|
+
const wrapped = middleware((input) => toStream(tool.run(input)), tool.name);
|
|
79
|
+
return {
|
|
80
|
+
...tool,
|
|
81
|
+
run: (input) => isStreamed(tool.run(input)) ? wrapped(input) : lastOf(tool.name, wrapped(input))
|
|
82
|
+
};
|
|
83
|
+
}));
|
|
84
|
+
const lastOf = (name, values) => Effect.flatMap(Stream.runLast(values), Option.match({
|
|
85
|
+
onSome: Effect.succeed,
|
|
86
|
+
onNone: () => Effect.die(`A middleware left the Effect tool "${name}" without a value`)
|
|
87
|
+
}));
|
|
109
88
|
/**
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* with the domain `describe` helpers (`AiError.describe`,
|
|
113
|
-
* `BrowserError.describe`). Non-local kinds pass through.
|
|
114
|
-
*
|
|
115
|
-
* const kit = yield* Toolkit.compose(
|
|
116
|
-
* Toolkit.describeFailures(searchKit, AiError.describe),
|
|
117
|
-
* browserToolkit(session), // failures stay typed
|
|
118
|
-
* )
|
|
89
|
+
* `Tool.withFailures` for every local tool, e.g. a whole MCP toolkit that needs
|
|
90
|
+
* approval. Prefer it per tool when only some tools are gated.
|
|
119
91
|
*/
|
|
120
|
-
const
|
|
121
|
-
...tool,
|
|
122
|
-
run: (input, emit) => tool.run(input, emit).pipe(Effect.mapError(describe))
|
|
123
|
-
} : tool));
|
|
92
|
+
const withFailures = Function.dual(2, (toolkit, failures) => mapLocal(toolkit, (tool) => withFailures$1(tool, failures)));
|
|
124
93
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
94
|
+
* Undeclared errors become a `ToolFailed` the model reads, e.g. with
|
|
95
|
+
* `AiError.describe`. Declared failures pass through unchanged. Return
|
|
96
|
+
* `undefined` for an error that should still fail the run (a dead session).
|
|
97
|
+
*/
|
|
98
|
+
const describeFailures = Function.dual(2, (toolkit, describe) => mapLocal(toolkit, (tool) => {
|
|
99
|
+
const isDeclared = isDeclaredBy(tool);
|
|
100
|
+
const toFailed = (e) => isDeclared(e) ? e : Option.match(Option.fromNullishOr(describe(e)), {
|
|
101
|
+
onNone: () => e,
|
|
102
|
+
onSome: (message) => new ToolFailed({ message })
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
...tool,
|
|
106
|
+
failureSchema: tool.failureSchema === void 0 ? ToolFailed : Schema.Union([tool.failureSchema, ToolFailed]),
|
|
107
|
+
run: (input) => {
|
|
108
|
+
const out = tool.run(input);
|
|
109
|
+
return isStreamed(out) ? Stream.mapError(out, toFailed) : Effect.mapError(out, toFailed);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}));
|
|
113
|
+
/**
|
|
114
|
+
* Run the calls, emitting each one's partials and its `Output`, typed per tool
|
|
115
|
+
* and decoded. An error matching the tool's `failureSchema` becomes a `Failure`;
|
|
116
|
+
* any other error fails the stream (siblings of a failed batch may then stay
|
|
117
|
+
* unanswered: see `HistoryCheck.cancelAllPending`). Which calls run is the caller's call.
|
|
133
118
|
*/
|
|
134
119
|
const run = (toolkit, calls, options) => Stream.fromIterable(calls).pipe(Stream.flatMap((call) => runOne(toolkit, call), { concurrency: options?.concurrency ?? "unbounded" }));
|
|
135
|
-
const
|
|
120
|
+
const lookup = (toolkit, name) => Object.hasOwn(toolkit, name) ? toolkit[name] : void 0;
|
|
121
|
+
const local = (toolkit, name) => {
|
|
122
|
+
const tool = lookup(toolkit, name);
|
|
123
|
+
return tool === void 0 || tool._tag === "ProviderTool" ? void 0 : tool;
|
|
124
|
+
};
|
|
125
|
+
const done = (call, result) => ({
|
|
126
|
+
_tag: "Output",
|
|
136
127
|
call_id: call.call_id,
|
|
137
|
-
tool,
|
|
138
|
-
|
|
128
|
+
tool: call.name,
|
|
129
|
+
result
|
|
139
130
|
});
|
|
140
|
-
const
|
|
141
|
-
const runOne = (toolkit, call) => {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
131
|
+
const failWith = (call, error) => Stream.succeed(done(call, Result.fail(error)));
|
|
132
|
+
const runOne = (toolkit, call) => Match.value(lookup(toolkit, call.name)).pipe(Match.when(Match.undefined, () => failWith(call, new UnknownTool({ message: `No tool named "${call.name}"` }))), Match.discriminatorsExhaustive("_tag")({
|
|
133
|
+
LocalTool: (tool) => runTool(tool, call),
|
|
134
|
+
DeclaredTool: () => failWith(call, new UnknownTool({ message: `"${call.name}" has no run; the app handles its calls` })),
|
|
135
|
+
ProviderTool: () => failWith(call, new UnknownTool({ message: `"${call.name}" is run by the provider` }))
|
|
136
|
+
}));
|
|
145
137
|
const describeIssues = (issues) => issues.map((issue) => {
|
|
146
138
|
const path = (issue.path ?? []).map((seg) => typeof seg === "object" && seg !== null ? String(seg.key) : String(seg)).join(".");
|
|
147
139
|
return path === "" ? issue.message : `${path}: ${issue.message}`;
|
|
148
140
|
}).join("; ").slice(0, 500);
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
141
|
+
const decodeInput = (tool, call) => Effect.map(decodeCallInput(tool, call), Match.type().pipe(Match.tag("parseError", () => Result.fail(new InputInvalid({ message: "arguments are not valid JSON" }))), Match.tag("invalid", ({ issues }) => Result.fail(new InputInvalid({ message: describeIssues(issues) }))), Match.tag("ok", ({ input }) => Result.succeed(input)), Match.exhaustive));
|
|
142
|
+
const runTool = (tool, call) => Stream.unwrap(Effect.map(decodeInput(tool, call), Result.match({
|
|
143
|
+
onFailure: (error) => failWith(call, error),
|
|
144
|
+
onSuccess: (input) => execute(tool, call, input)
|
|
145
|
+
})));
|
|
146
|
+
const execute = (tool, call, input) => {
|
|
147
|
+
const out = tool.run(input);
|
|
148
|
+
const events = isStreamed(out) ? streamed(tool, call, out) : Stream.fromEffect(Effect.map(Effect.tap(out, (value) => check(tool, value)), (value) => done(call, Result.succeed(value))));
|
|
149
|
+
return Stream.catchCause(events, (cause) => Stream.fromEffect(Effect.map(declared(tool, cause), (error) => done(call, Result.fail(error)))));
|
|
150
|
+
};
|
|
151
|
+
const streamed = (tool, call, values) => values.pipe(Stream.mapEffect((value) => Effect.as(check(tool, value), Option.some(value))), Stream.concat(Stream.succeed(Option.none())), Stream.mapAccum(() => Option.none(), (last, next) => Option.match(next, {
|
|
152
|
+
onSome: (value) => [Option.some(value), [{
|
|
153
|
+
_tag: "Partial",
|
|
155
154
|
call_id: call.call_id,
|
|
156
155
|
tool: tool.name,
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}))
|
|
163
|
-
|
|
156
|
+
value
|
|
157
|
+
}]],
|
|
158
|
+
onNone: () => [last, [done(call, Option.match(last, {
|
|
159
|
+
onNone: () => Result.fail(new OutputInvalid({ message: "The tool produced no value" })),
|
|
160
|
+
onSome: Result.succeed
|
|
161
|
+
}))]]
|
|
162
|
+
})));
|
|
163
|
+
var EncodeFailed = class extends Data.TaggedError("EncodeFailed") {};
|
|
164
|
+
const check = (tool, value) => tool.outputSchema === void 0 ? Effect.void : Schema.encodeUnknownEffect(tool.outputSchema)(value).pipe(Effect.mapError((e) => new EncodeFailed({ message: e.message.slice(0, 500) })));
|
|
165
|
+
const isDeclaredBy = (tool) => tool.failureSchema === void 0 ? () => false : Schema.is(tool.failureSchema);
|
|
166
|
+
const soleError = (cause) => Option.fromNullishOr(cause.reasons.length === 1 ? cause.reasons[0] : void 0).pipe(Option.filter(Cause.isFailReason), Option.map((reason) => reason.error));
|
|
167
|
+
const declared = (tool, cause) => soleError(cause).pipe(Option.flatMap((error) => Match.value(error).pipe(Match.when(Match.instanceOf(EncodeFailed), (e) => Option.some(new OutputInvalid({ message: e.message }))), Match.when(isDeclaredBy(tool), Option.some), Match.orElse(() => Option.none()))), Option.match({
|
|
168
|
+
onNone: () => Effect.failCause(cause),
|
|
169
|
+
onSome: Effect.succeed
|
|
164
170
|
}));
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return
|
|
173
|
-
|
|
171
|
+
const outputs = (tool) => tool?.outputSchema ?? Value;
|
|
172
|
+
const failures = (tool) => tool?.failureSchema === void 0 ? CoreError : Schema.Union([tool.failureSchema, CoreError]);
|
|
173
|
+
/** `Toolkit.close` got an error the call's tool doesn't declare: a bug in the app. */
|
|
174
|
+
var UndeclaredFailure = class extends Data.TaggedError("UndeclaredFailure") {};
|
|
175
|
+
const describeError = (error) => Predicate.hasProperty(error, "_tag") && Predicate.isString(error._tag) ? error._tag : String(error);
|
|
176
|
+
function close(toolkit, ...args) {
|
|
177
|
+
const [name, call, error] = args.length === 3 ? args : [args[0].name, ...args];
|
|
178
|
+
return Match.value({
|
|
179
|
+
name,
|
|
180
|
+
call,
|
|
181
|
+
error
|
|
182
|
+
}).pipe(Match.when(({ call }) => call.name !== name, () => Effect.fail(new UndeclaredFailure({
|
|
183
|
+
tool: name,
|
|
184
|
+
message: `the call is for "${call.name}", not "${name}"`
|
|
185
|
+
}))), Match.when(({ error }) => !Schema.is(failures(local(toolkit, name)))(error), () => Effect.fail(new UndeclaredFailure({
|
|
186
|
+
tool: name,
|
|
187
|
+
message: `"${name}" does not declare ${describeError(error)} in its failureSchema`
|
|
188
|
+
}))), Match.orElse(() => Effect.succeed({
|
|
189
|
+
_tag: "Output",
|
|
190
|
+
call_id: call.call_id,
|
|
191
|
+
tool: name,
|
|
192
|
+
result: Result.fail(error)
|
|
193
|
+
})));
|
|
194
|
+
}
|
|
174
195
|
/**
|
|
175
|
-
*
|
|
176
|
-
* the
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* Toolkit.run(toolkit, calls).pipe(
|
|
180
|
-
* Toolkit.continueWithResults(Toolkit.appendToolResults(state, turn)),
|
|
181
|
-
* )
|
|
182
|
-
*
|
|
183
|
-
* Equivalent to `appendToHistory(state, turn, results.map(toToolCallOutput))`
|
|
184
|
-
* — the helper just hides the wire-conversion step.
|
|
196
|
+
* A call the app answers itself (`ask_user`, a signal's acknowledgement), as
|
|
197
|
+
* the outcome `run` would emit: `close`'s success side. `value` is typed by the
|
|
198
|
+
* tool's `outputSchema`; a value it rejects ends the call with `OutputInvalid`,
|
|
199
|
+
* as from `run`. Fails with `UndeclaredFailure` if `call` is for another tool.
|
|
185
200
|
*/
|
|
186
|
-
const
|
|
201
|
+
const complete = (toolkit, name, call, value) => Option.match(Option.fromNullishOr(call.name === name ? local(toolkit, name) : void 0), {
|
|
202
|
+
onNone: () => Effect.fail(new UndeclaredFailure({
|
|
203
|
+
tool: name,
|
|
204
|
+
message: call.name === name ? `"${name}" is not a tool of this toolkit` : `the call is for "${call.name}", not "${name}"`
|
|
205
|
+
})),
|
|
206
|
+
onSome: (tool) => check(tool, value).pipe(Effect.match({
|
|
207
|
+
onFailure: (e) => Result.fail(new OutputInvalid({ message: e.message })),
|
|
208
|
+
onSuccess: () => Result.succeed(value)
|
|
209
|
+
}), Effect.map((result) => ({
|
|
210
|
+
_tag: "Output",
|
|
211
|
+
call_id: call.call_id,
|
|
212
|
+
tool: name,
|
|
213
|
+
result
|
|
214
|
+
})))
|
|
215
|
+
});
|
|
216
|
+
const sortCall = (toolkit, call) => {
|
|
217
|
+
const tool = lookup(toolkit, call.name);
|
|
218
|
+
return tool?._tag === "DeclaredTool" ? Effect.map(decodeInput(tool, call), Result.match({
|
|
219
|
+
onFailure: (error) => ({
|
|
220
|
+
_tag: "invalid",
|
|
221
|
+
output: {
|
|
222
|
+
_tag: "Output",
|
|
223
|
+
call_id: call.call_id,
|
|
224
|
+
tool: call.name,
|
|
225
|
+
result: Result.fail(error)
|
|
226
|
+
}
|
|
227
|
+
}),
|
|
228
|
+
onSuccess: (input) => ({
|
|
229
|
+
_tag: "declared",
|
|
230
|
+
declared: {
|
|
231
|
+
tool: call.name,
|
|
232
|
+
call,
|
|
233
|
+
input
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
})) : Effect.succeed({
|
|
237
|
+
_tag: "run",
|
|
238
|
+
call
|
|
239
|
+
});
|
|
240
|
+
};
|
|
187
241
|
/**
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
242
|
+
* The turn's tool calls, sorted: `run` for `Toolkit.run`, and the calls to
|
|
243
|
+
* declared tools decoded and typed per tool. Handling `declared` with
|
|
244
|
+
* `Match.discriminatorsExhaustive("tool")` makes a new declared tool a compile
|
|
245
|
+
* error until the loop handles it:
|
|
192
246
|
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* broadcast/fork-and-merge for callers that want to vary the left arm
|
|
196
|
-
* (e.g., filter/tap ToolEvents before forwarding).
|
|
247
|
+
* const { run, declared, invalid } = yield* Toolkit.split(toolkit, turn)
|
|
248
|
+
* declared.map(Match.discriminatorsExhaustive("tool")({ ask_user: ..., escalate: ... }))
|
|
197
249
|
*/
|
|
198
|
-
const
|
|
250
|
+
const split = (toolkit, turn) => Effect.map(Effect.forEach(getToolCalls(turn), (call) => sortCall(toolkit, call)), (sorted) => ({
|
|
251
|
+
run: sorted.flatMap((s) => s._tag === "run" ? [s.call] : []),
|
|
252
|
+
declared: sorted.flatMap((s) => s._tag === "declared" ? [s.declared] : []),
|
|
253
|
+
invalid: sorted.flatMap((s) => s._tag === "invalid" ? [s.output] : [])
|
|
254
|
+
}));
|
|
255
|
+
/** Narrows to the `Output` members (also in a loop's mixed stream), keeping the per-tool types. */
|
|
256
|
+
const isOutcome = (e) => e._tag === "Output";
|
|
257
|
+
const toOutput = (toolkit, event) => {
|
|
258
|
+
const tool = local(toolkit, event.tool);
|
|
259
|
+
return Result.match(event.result, {
|
|
260
|
+
onSuccess: (value) => success(event, outputs(tool), value),
|
|
261
|
+
onFailure: (error) => failure(event, failures(tool), error)
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
function output(toolkit, event) {
|
|
265
|
+
return event === void 0 ? (e) => toOutput(toolkit, e) : toOutput(toolkit, event);
|
|
266
|
+
}
|
|
267
|
+
function decode(toolkit, name, item) {
|
|
268
|
+
const tool = local(toolkit, name);
|
|
269
|
+
const decodeWith = (schema) => Effect.flatMap(encoded(item.output), Schema.decodeUnknownEffect(schema));
|
|
270
|
+
return Match.value(item).pipe(Match.when({ isError: true }, () => Effect.map(decodeWith(failures(tool)), Result.fail)), Match.orElse(() => Option.match(Option.fromNullishOr(tool?.outputSchema), {
|
|
271
|
+
onNone: () => Effect.succeed(Result.succeed(value$1(item.output))),
|
|
272
|
+
onSome: (schema) => Effect.map(decodeWith(schema), Result.succeed)
|
|
273
|
+
})));
|
|
274
|
+
}
|
|
275
|
+
const isOutcomeOf = (e) => e._tag === "Output";
|
|
199
276
|
/**
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* from terminal `Output` events.
|
|
204
|
-
*
|
|
205
|
-
* Conceptually a broadcast fork (one arm passes events through, the other
|
|
206
|
-
* drains them into a state). Implemented as a single-pass Ref tap + concat
|
|
207
|
-
* for zero buffering — the broadcast version is observationally equivalent
|
|
208
|
-
* but holds events in a PubSub until both arms drain. The public primitives
|
|
209
|
-
* (`Loop.value`, `collectResults`, `Loop.next`) compose the same pattern
|
|
210
|
-
* when you need to vary an arm.
|
|
211
|
-
*
|
|
212
|
-
* Dual: data-first `continueWithResults(stream, build)` and data-last
|
|
213
|
-
* `stream.pipe(continueWithResults(build))` both work.
|
|
277
|
+
* Forward every event as a `Loop.value`, then continue the loop with
|
|
278
|
+
* `build(outcomes)`. `build` is an Effect, so it can encode. The stream may
|
|
279
|
+
* carry the app's own tagged events too (any tag but `Output`).
|
|
214
280
|
*/
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
})));
|
|
281
|
+
const continueWith = Function.dual(2, (stream, build) => Stream.unwrap(Effect.map(Ref.make([]), (outcomes) => Stream.concat(stream.pipe(Stream.tap((e) => isOutcomeOf(e) ? Ref.update(outcomes, Array.append(e)) : Effect.void), Stream.map(value)), Stream.unwrap(Ref.get(outcomes).pipe(Effect.flatMap(build), Effect.map(next$1)))))));
|
|
282
|
+
/** Encode each outcome with `output` and append the turn plus the outputs to history. */
|
|
283
|
+
const appendResults = (toolkit, state, turn) => (outcomes) => Effect.map(Effect.forEach(outcomes, output(toolkit)), (items) => appendToHistory(state, turn, items));
|
|
284
|
+
/** `continueWith(appendResults(toolkit, state, turn))`. */
|
|
285
|
+
const next = (toolkit, state, turn) => continueWith(appendResults(toolkit, state, turn));
|
|
221
286
|
//#endregion
|
|
222
|
-
export { DuplicateToolName,
|
|
287
|
+
export { DuplicateToolName, UndeclaredFailure, appendResults, close, complete, compose, continueWith, decode, describeFailures, descriptors, fromArray, isOutcome, make, makeNamespaced, namespace, next, output, run, split, Toolkit_exports as t, withFailures, wrap };
|
|
223
288
|
|
|
224
289
|
//# sourceMappingURL=Toolkit.mjs.map
|