@effect-uai/core 0.18.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.
Files changed (162) hide show
  1. package/README.md +4 -4
  2. package/dist/Content-D_Qfn8Ja.d.mts +48 -0
  3. package/dist/Content-D_Qfn8Ja.d.mts.map +1 -0
  4. package/dist/File-B923KvZ9.d.mts +46 -0
  5. package/dist/File-B923KvZ9.d.mts.map +1 -0
  6. package/dist/{Image-Cv6iBLTy.d.mts → Image-Bzc1H_UK.d.mts} +19 -8
  7. package/dist/Image-Bzc1H_UK.d.mts.map +1 -0
  8. package/dist/{Items-C7MyrYPX.d.mts → Items-Bvx8s9QH.d.mts} +109 -16
  9. package/dist/Items-Bvx8s9QH.d.mts.map +1 -0
  10. package/dist/{Loop-ChpaO-ya.d.mts → Loop-CeA0AelN.d.mts} +7 -6
  11. package/dist/Loop-CeA0AelN.d.mts.map +1 -0
  12. package/dist/Tool-Bkryw6-h.d.mts +206 -0
  13. package/dist/Tool-Bkryw6-h.d.mts.map +1 -0
  14. package/dist/ToolEvent-9inLx5cI.d.mts +22 -0
  15. package/dist/ToolEvent-9inLx5cI.d.mts.map +1 -0
  16. package/dist/ToolOutput-BNTIQgGB.d.mts +151 -0
  17. package/dist/ToolOutput-BNTIQgGB.d.mts.map +1 -0
  18. package/dist/Toolkit-D2VGeD5R.d.mts +212 -0
  19. package/dist/Toolkit-D2VGeD5R.d.mts.map +1 -0
  20. package/dist/browser/BrowserTool.d.mts +84 -22
  21. package/dist/browser/BrowserTool.d.mts.map +1 -1
  22. package/dist/browser/BrowserTool.mjs +5 -0
  23. package/dist/browser/BrowserTool.mjs.map +1 -1
  24. package/dist/capabilities/Capabilities.d.mts +1 -1
  25. package/dist/decision-model/Decision.test.mjs +1 -1
  26. package/dist/{dist-C8RmtoR8.mjs → dist-B5K-y4Be.mjs} +51 -32
  27. package/dist/dist-B5K-y4Be.mjs.map +1 -0
  28. package/dist/domain/File.d.mts +2 -0
  29. package/dist/domain/File.mjs +51 -0
  30. package/dist/domain/File.mjs.map +1 -0
  31. package/dist/domain/Image.d.mts +2 -2
  32. package/dist/domain/Image.mjs +12 -2
  33. package/dist/domain/Image.mjs.map +1 -1
  34. package/dist/domain/Items.d.mts +2 -2
  35. package/dist/domain/Items.mjs +65 -4
  36. package/dist/domain/Items.mjs.map +1 -1
  37. package/dist/domain/Realtime.d.mts +72 -490
  38. package/dist/domain/Realtime.d.mts.map +1 -1
  39. package/dist/domain/Research.d.mts +1 -1
  40. package/dist/domain/Transcript.test.mjs +1 -1
  41. package/dist/domain/Turn.d.mts +57 -262
  42. package/dist/domain/Turn.d.mts.map +1 -1
  43. package/dist/domain/Turn.mjs +1 -1
  44. package/dist/embedding-model/Embedding.d.mts +1 -1
  45. package/dist/image-generator/ImageGenerator.d.mts +10 -66
  46. package/dist/image-generator/ImageGenerator.d.mts.map +1 -1
  47. package/dist/index.d.mts +10 -9
  48. package/dist/index.mjs +7 -6
  49. package/dist/language-model/LanguageModel.d.mts +2 -2
  50. package/dist/loop/Loop.d.mts +1 -1
  51. package/dist/loop/Loop.mjs.map +1 -1
  52. package/dist/messenger/Messenger.d.mts +43 -289
  53. package/dist/messenger/Messenger.d.mts.map +1 -1
  54. package/dist/messenger/MessengerAdapter.test.mjs +1 -1
  55. package/dist/observability/Metrics.d.mts +1 -1
  56. package/dist/observability/Metrics.test.mjs +1 -1
  57. package/dist/observability/Telemetry.test.mjs +1 -1
  58. package/dist/realtime/RealtimeSession.d.mts +1 -1
  59. package/dist/realtime/RealtimeSession.test.mjs +1 -1
  60. package/dist/sandbox/Sandbox.d.mts +32 -237
  61. package/dist/sandbox/Sandbox.d.mts.map +1 -1
  62. package/dist/streaming/Multipart.mjs +2 -1
  63. package/dist/streaming/Multipart.mjs.map +1 -1
  64. package/dist/streaming/Multipart.test.mjs +28 -0
  65. package/dist/streaming/Multipart.test.mjs.map +1 -0
  66. package/dist/streaming/Settle.test.mjs +1 -1
  67. package/dist/streaming/WebSocketSession.test.mjs +1 -1
  68. package/dist/testing/MockMessenger.d.mts +19 -134
  69. package/dist/testing/MockMessenger.d.mts.map +1 -1
  70. package/dist/testing/MockProvider.d.mts +1 -1
  71. package/dist/testing/MockSandbox.d.mts +65 -446
  72. package/dist/testing/MockSandbox.d.mts.map +1 -1
  73. package/dist/tool/Content.d.mts +2 -0
  74. package/dist/tool/Content.mjs +46 -0
  75. package/dist/tool/Content.mjs.map +1 -0
  76. package/dist/tool/Content.test.d.mts +1 -0
  77. package/dist/tool/Content.test.mjs +48 -0
  78. package/dist/tool/Content.test.mjs.map +1 -0
  79. package/dist/tool/HistoryCheck.d.mts +6 -15
  80. package/dist/tool/HistoryCheck.d.mts.map +1 -1
  81. package/dist/tool/HistoryCheck.mjs +10 -24
  82. package/dist/tool/HistoryCheck.mjs.map +1 -1
  83. package/dist/tool/Tool.d.mts +2 -2
  84. package/dist/tool/Tool.mjs +55 -130
  85. package/dist/tool/Tool.mjs.map +1 -1
  86. package/dist/tool/Tool.test.mjs +220 -176
  87. package/dist/tool/Tool.test.mjs.map +1 -1
  88. package/dist/tool/ToolDecision.d.mts +164 -0
  89. package/dist/tool/ToolDecision.d.mts.map +1 -0
  90. package/dist/tool/ToolDecision.mjs +146 -0
  91. package/dist/tool/ToolDecision.mjs.map +1 -0
  92. package/dist/tool/ToolDecision.test.d.mts +1 -0
  93. package/dist/tool/ToolDecision.test.mjs +330 -0
  94. package/dist/tool/ToolDecision.test.mjs.map +1 -0
  95. package/dist/tool/ToolEvent.d.mts +2 -2
  96. package/dist/tool/ToolEvent.mjs +2 -34
  97. package/dist/tool/ToolEvent.mjs.map +1 -1
  98. package/dist/tool/ToolOutput.d.mts +2 -0
  99. package/dist/tool/ToolOutput.mjs +128 -0
  100. package/dist/tool/ToolOutput.mjs.map +1 -0
  101. package/dist/tool/ToolOutput.test.d.mts +1 -0
  102. package/dist/tool/ToolOutput.test.mjs +153 -0
  103. package/dist/tool/ToolOutput.test.mjs.map +1 -0
  104. package/dist/tool/Toolkit.d.mts +2 -2
  105. package/dist/tool/Toolkit.mjs +230 -165
  106. package/dist/tool/Toolkit.mjs.map +1 -1
  107. package/dist/tool/Toolkit.test.mjs +431 -389
  108. package/dist/tool/Toolkit.test.mjs.map +1 -1
  109. package/dist/web-read/WebReadTool.d.mts +2 -2
  110. package/dist/web-read/WebReadTool.mjs +1 -0
  111. package/dist/web-read/WebReadTool.mjs.map +1 -1
  112. package/dist/web-search/WebSearchTool.d.mts +2 -2
  113. package/dist/web-search/WebSearchTool.mjs +1 -0
  114. package/dist/web-search/WebSearchTool.mjs.map +1 -1
  115. package/package.json +17 -9
  116. package/src/browser/BrowserTool.ts +5 -0
  117. package/src/domain/File.ts +79 -0
  118. package/src/domain/Image.ts +14 -4
  119. package/src/domain/Items.ts +39 -3
  120. package/src/index.ts +3 -2
  121. package/src/loop/Loop.ts +24 -13
  122. package/src/streaming/Multipart.test.ts +26 -0
  123. package/src/streaming/Multipart.ts +5 -4
  124. package/src/tool/Content.test.ts +40 -0
  125. package/src/tool/Content.ts +38 -0
  126. package/src/tool/HistoryCheck.ts +21 -26
  127. package/src/tool/Tool.test.ts +240 -241
  128. package/src/tool/Tool.ts +203 -308
  129. package/src/tool/ToolDecision.test.ts +301 -0
  130. package/src/tool/ToolDecision.ts +360 -0
  131. package/src/tool/ToolEvent.ts +21 -41
  132. package/src/tool/ToolOutput.test.ts +132 -0
  133. package/src/tool/ToolOutput.ts +198 -0
  134. package/src/tool/Toolkit.test.ts +594 -454
  135. package/src/tool/Toolkit.ts +631 -356
  136. package/src/web-read/WebReadTool.ts +2 -1
  137. package/src/web-search/WebSearchTool.ts +2 -1
  138. package/dist/Image-Cv6iBLTy.d.mts.map +0 -1
  139. package/dist/Items-C7MyrYPX.d.mts.map +0 -1
  140. package/dist/Loop-ChpaO-ya.d.mts.map +0 -1
  141. package/dist/Tool-BF78QWtn.d.mts +0 -314
  142. package/dist/Tool-BF78QWtn.d.mts.map +0 -1
  143. package/dist/ToolEvent-Di4a723B.d.mts +0 -144
  144. package/dist/ToolEvent-Di4a723B.d.mts.map +0 -1
  145. package/dist/ToolResult-BsYQRjmW.d.mts +0 -152
  146. package/dist/ToolResult-BsYQRjmW.d.mts.map +0 -1
  147. package/dist/Toolkit-tlL0F2YR.d.mts +0 -193
  148. package/dist/Toolkit-tlL0F2YR.d.mts.map +0 -1
  149. package/dist/dist-C8RmtoR8.mjs.map +0 -1
  150. package/dist/tool/Approval.d.mts +0 -113
  151. package/dist/tool/Approval.d.mts.map +0 -1
  152. package/dist/tool/Approval.mjs +0 -80
  153. package/dist/tool/Approval.mjs.map +0 -1
  154. package/dist/tool/Approval.test.mjs +0 -371
  155. package/dist/tool/Approval.test.mjs.map +0 -1
  156. package/dist/tool/ToolResult.d.mts +0 -2
  157. package/dist/tool/ToolResult.mjs +0 -85
  158. package/dist/tool/ToolResult.mjs.map +0 -1
  159. package/src/tool/Approval.test.ts +0 -400
  160. package/src/tool/Approval.ts +0 -181
  161. package/src/tool/ToolResult.ts +0 -114
  162. /package/dist/{tool/Approval.test.d.mts → streaming/Multipart.test.d.mts} +0 -0
@@ -1,371 +0,0 @@
1
- import { toolCallOutput, userText } from "../domain/Items.mjs";
2
- import { isFailure, isOk, toToolCallOutput } from "./ToolResult.mjs";
3
- import { fromEffectSchema, make } from "./Tool.mjs";
4
- import { ToolEvent, isApprovalRequested, isOutput, isProgress } from "./ToolEvent.mjs";
5
- import { make as make$1, run } from "./Toolkit.mjs";
6
- import { fromMap, fromQueue } from "./Approval.mjs";
7
- import { cancelAllPending, findUnansweredCalls, isReconciled } from "./HistoryCheck.mjs";
8
- import { b as it, m as describe } from "../dist-DzeSyeYF.mjs";
9
- import { n as globalExpect } from "../index.C4tKZ0yW-LuFnBtmu.mjs";
10
- import { Effect, Queue, Schema, Stream } from "effect";
11
- //#region src/tool/Approval.test.ts
12
- /**
13
- * Tests for approval planners + history-reconciliation primitives. Exercises
14
- * the full HITL + streaming-tool stack end-to-end by composing approval plans
15
- * with `run`, with the four wire-shaped scenarios:
16
- *
17
- * 1. Approval : gated calls approved → tools execute, structured Values
18
- * 2. Denial : gated calls denied → Failure(denied) results
19
- * 3. Cancellation : missing verdicts → Failure(cancelled) results
20
- * 4. Mixed + history : reconciliation via cancelAllPending
21
- *
22
- * Plus: hallucinated tool name (graceful Failure), unknown_tool kind.
23
- */
24
- const webSearch = make({
25
- name: "web_search",
26
- description: "search",
27
- inputSchema: fromEffectSchema(Schema.Struct({ query: Schema.String })),
28
- run: ({ query }, emit) => Effect.gen(function* () {
29
- const hits = [
30
- {
31
- url: "a",
32
- title: `${query} 1`
33
- },
34
- {
35
- url: "b",
36
- title: `${query} 2`
37
- },
38
- {
39
- url: "c",
40
- title: `${query} 3`
41
- }
42
- ];
43
- yield* Stream.runForEach(Stream.fromIterable(hits), emit);
44
- return { count: hits.length };
45
- })
46
- });
47
- const bulkEmail = make({
48
- name: "bulk_email",
49
- description: "send",
50
- inputSchema: fromEffectSchema(Schema.Struct({
51
- recipients: Schema.Array(Schema.String),
52
- subject: Schema.String
53
- })),
54
- run: ({ recipients }, emit) => Effect.gen(function* () {
55
- yield* Effect.forEach(recipients, (_, i) => emit({
56
- type: "progress",
57
- sent: i + 1,
58
- total: recipients.length
59
- }));
60
- return {
61
- status: "sent",
62
- delivered: recipients.length
63
- };
64
- })
65
- });
66
- const deleteDatabase = make({
67
- name: "delete_database",
68
- description: "drop",
69
- inputSchema: fromEffectSchema(Schema.Struct({ name: Schema.String })),
70
- run: ({ name }) => Effect.succeed({
71
- status: "dropped",
72
- name
73
- })
74
- });
75
- const allTools = make$1(webSearch, bulkEmail, deleteDatabase);
76
- const SENSITIVE = /* @__PURE__ */ new Set(["bulk_email", "delete_database"]);
77
- const isSensitive = (call) => SENSITIVE.has(call.name);
78
- const fc = (call_id, name, args) => ({
79
- type: "function_call",
80
- call_id,
81
- name,
82
- arguments: JSON.stringify(args)
83
- });
84
- const calls = [
85
- fc("c1", "web_search", { query: "effect" }),
86
- fc("c2", "bulk_email", {
87
- recipients: ["a@x", "b@x"],
88
- subject: "Hi"
89
- }),
90
- fc("c3", "delete_database", { name: "prod" })
91
- ];
92
- const resultsFrom = (collected) => collected.filter(isOutput).map((e) => e.result);
93
- const byCallId = (results) => new Map(results.map((r) => [r.call_id, r]));
94
- const rejectedStream = (rejected) => Stream.fromIterable(rejected.map((result) => ToolEvent.Output({ result })));
95
- const eventsFromApprovalMap = (approvals) => {
96
- const plan = fromMap(isSensitive, approvals)(calls);
97
- return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected));
98
- };
99
- const eventsFromDecision = (decision) => decision._tag === "Approved" ? run(allTools, [decision.call]) : Stream.succeed(ToolEvent.Output({ result: decision.result }));
100
- describe("fromMap + run", () => {
101
- it("approval: all gated approved → tools execute, structured Values", async () => {
102
- const approvals = /* @__PURE__ */ new Map([["c2", { decision: "approve" }], ["c3", { decision: "approve" }]]);
103
- const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)));
104
- const by = byCallId(resultsFrom(collected));
105
- globalExpect(by.get("c1")).toMatchObject({
106
- _tag: "Ok",
107
- value: { count: 3 }
108
- });
109
- globalExpect(by.get("c2")).toMatchObject({
110
- _tag: "Ok",
111
- value: {
112
- status: "sent",
113
- delivered: 2
114
- }
115
- });
116
- globalExpect(by.get("c3")).toMatchObject({
117
- _tag: "Ok",
118
- value: {
119
- status: "dropped",
120
- name: "prod"
121
- }
122
- });
123
- globalExpect(collected.filter(isApprovalRequested)).toHaveLength(0);
124
- });
125
- it("denial: gated denied → Failure(denied), no execution", async () => {
126
- const approvals = /* @__PURE__ */ new Map([["c2", {
127
- decision: "deny",
128
- reason: "spam concern"
129
- }], ["c3", {
130
- decision: "deny",
131
- reason: "prod is sacred"
132
- }]]);
133
- const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)));
134
- globalExpect(collected.filter(isProgress).filter((e) => e.tool === "bulk_email")).toHaveLength(0);
135
- const by = byCallId(resultsFrom(collected));
136
- globalExpect(by.get("c2")).toMatchObject({
137
- _tag: "Failure",
138
- kind: "denied",
139
- reason: "spam concern"
140
- });
141
- globalExpect(by.get("c3")).toMatchObject({
142
- _tag: "Failure",
143
- kind: "denied",
144
- reason: "prod is sacred"
145
- });
146
- });
147
- it("cancellation: missing verdicts → Failure(cancelled)", async () => {
148
- const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(/* @__PURE__ */ new Map())));
149
- const by = byCallId(resultsFrom(collected));
150
- globalExpect(by.get("c1")).toMatchObject({
151
- _tag: "Ok",
152
- value: { count: 3 }
153
- });
154
- globalExpect(by.get("c2")).toMatchObject({
155
- _tag: "Failure",
156
- kind: "cancelled"
157
- });
158
- globalExpect(by.get("c3")).toMatchObject({
159
- _tag: "Failure",
160
- kind: "cancelled"
161
- });
162
- });
163
- it("mixed: approve + deny + omit → all three kinds", async () => {
164
- const approvals = /* @__PURE__ */ new Map([["c2", { decision: "approve" }]]);
165
- const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)));
166
- const by = byCallId(resultsFrom(collected));
167
- globalExpect(by.get("c1")).toMatchObject({
168
- _tag: "Ok",
169
- value: { count: 3 }
170
- });
171
- globalExpect(by.get("c2")).toMatchObject({
172
- _tag: "Ok",
173
- value: { status: "sent" }
174
- });
175
- globalExpect(by.get("c3")).toMatchObject({
176
- _tag: "Failure",
177
- kind: "cancelled"
178
- });
179
- });
180
- });
181
- describe("run: graceful degradation", () => {
182
- it("unknown tool name → Failure(unknown_tool); other calls still execute", async () => {
183
- const callsWithBogus = [
184
- fc("c1", "web_search", { query: "x" }),
185
- fc("c2", "does_not_exist", {}),
186
- fc("c3", "delete_database", { name: "prod" })
187
- ];
188
- const collected = await Effect.runPromise(Stream.runCollect((() => {
189
- const plan = fromMap(isSensitive, /* @__PURE__ */ new Map([["c3", { decision: "approve" }]]))(callsWithBogus);
190
- return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected));
191
- })()));
192
- const by = byCallId(resultsFrom(collected));
193
- globalExpect(by.get("c1")).toMatchObject({ _tag: "Ok" });
194
- globalExpect(by.get("c2")).toMatchObject({
195
- _tag: "Failure",
196
- kind: "unknown_tool"
197
- });
198
- globalExpect(by.get("c3")).toMatchObject({
199
- _tag: "Ok",
200
- value: { status: "dropped" }
201
- });
202
- });
203
- });
204
- describe("fromQueue + run", () => {
205
- it("queue-driven: approve + deny resolve correctly with ApprovalRequested events", async () => {
206
- const collected = await Effect.runPromise(Effect.gen(function* () {
207
- const verdicts = yield* Queue.unbounded();
208
- yield* Queue.offer(verdicts, {
209
- call_id: "c2",
210
- decision: "approve"
211
- });
212
- yield* Queue.offer(verdicts, {
213
- call_id: "c3",
214
- decision: "deny",
215
- reason: "too risky"
216
- });
217
- const events = Stream.unwrap(Effect.gen(function* () {
218
- const { approved, decisions, approvalRequests } = yield* fromQueue(isSensitive, verdicts)(calls);
219
- return Stream.merge(approvalRequests, Stream.merge(run(allTools, approved), decisions.pipe(Stream.flatMap(eventsFromDecision))));
220
- }));
221
- return yield* Stream.runCollect(events);
222
- }));
223
- globalExpect(collected.filter(isApprovalRequested)).toHaveLength(2);
224
- const by = byCallId(resultsFrom(collected));
225
- globalExpect(by.get("c2")).toMatchObject({
226
- _tag: "Ok",
227
- value: { status: "sent" }
228
- });
229
- globalExpect(by.get("c3")).toMatchObject({
230
- _tag: "Failure",
231
- kind: "denied",
232
- reason: "too risky"
233
- });
234
- });
235
- it("router retires per round, so sequential rounds over one queue both resolve", async () => {
236
- const decisions = await Effect.runPromise(Effect.scoped(Effect.gen(function* () {
237
- const verdicts = yield* Queue.unbounded();
238
- const round = (id) => [fc(id, "delete_database", { name: "prod" })];
239
- const r1 = yield* fromQueue(isSensitive, verdicts)(round("c1"));
240
- yield* Queue.offer(verdicts, {
241
- call_id: "c1",
242
- decision: "approve"
243
- });
244
- const d1 = Array.from(yield* Stream.runCollect(r1.decisions));
245
- const r2 = yield* fromQueue(isSensitive, verdicts)(round("c2"));
246
- yield* Queue.offer(verdicts, {
247
- call_id: "c2",
248
- decision: "deny",
249
- reason: "no"
250
- });
251
- return {
252
- d1,
253
- d2: Array.from(yield* Stream.runCollect(r2.decisions))
254
- };
255
- })));
256
- globalExpect(decisions.d1[0]).toMatchObject({ _tag: "Approved" });
257
- globalExpect(decisions.d2[0]).toMatchObject({
258
- _tag: "Rejected",
259
- result: { kind: "denied" }
260
- });
261
- });
262
- it("timeout resolves an unanswered gated call as cancelled", async () => {
263
- const decisions = await Effect.runPromise(Effect.scoped(Effect.gen(function* () {
264
- const verdicts = yield* Queue.unbounded();
265
- const { decisions } = yield* fromQueue(isSensitive, verdicts, { timeout: "10 millis" })([fc("c1", "delete_database", { name: "prod" })]);
266
- return Array.from(yield* Stream.runCollect(decisions));
267
- })));
268
- globalExpect(decisions[0]).toMatchObject({
269
- _tag: "Rejected",
270
- result: {
271
- _tag: "Failure",
272
- kind: "cancelled"
273
- }
274
- });
275
- });
276
- });
277
- describe("findUnansweredCalls / cancelAllPending / isReconciled", () => {
278
- const orphan = fc("c99", "delete_database", { name: "prod" });
279
- const answered = fc("c98", "web_search", { query: "x" });
280
- const answeredOutput = toolCallOutput("c98", JSON.stringify({ count: 0 }));
281
- it("findUnansweredCalls returns only orphans", () => {
282
- const history = [
283
- userText("hi"),
284
- answered,
285
- orphan,
286
- answeredOutput
287
- ];
288
- const unanswered = findUnansweredCalls(history);
289
- globalExpect(unanswered).toHaveLength(1);
290
- globalExpect(unanswered[0].call_id).toBe("c99");
291
- });
292
- it("isReconciled is false when orphans exist, true otherwise", () => {
293
- const stale = [userText("hi"), orphan];
294
- globalExpect(isReconciled(stale)).toBe(false);
295
- const reconciled = [...stale, ...cancelAllPending(stale).map(toToolCallOutput)];
296
- globalExpect(isReconciled(reconciled)).toBe(true);
297
- });
298
- it("cancelAllPending synthesizes one Failure(cancelled) per orphan", () => {
299
- const history = [
300
- userText("hi"),
301
- answered,
302
- orphan,
303
- answeredOutput
304
- ];
305
- const closures = cancelAllPending(history, "user moved on");
306
- globalExpect(closures).toHaveLength(1);
307
- const c = closures[0];
308
- globalExpect(isFailure(c)).toBe(true);
309
- globalExpect(c).toMatchObject({
310
- _tag: "Failure",
311
- call_id: "c99",
312
- kind: "cancelled",
313
- reason: "user moved on"
314
- });
315
- });
316
- it("follow-up: map closures to ToolCallOutput before appending new user message", () => {
317
- const stale = [userText("first request"), orphan];
318
- const closures = cancelAllPending(stale, "user redirected");
319
- const reconciled = [
320
- ...stale,
321
- ...closures.map(toToolCallOutput),
322
- userText("never mind")
323
- ];
324
- globalExpect(findUnansweredCalls(reconciled)).toHaveLength(0);
325
- });
326
- });
327
- describe("toToolCallOutput", () => {
328
- it("round-trips a Value result", () => {
329
- const out = toToolCallOutput({
330
- _tag: "Ok",
331
- call_id: "c1",
332
- tool: "web_search",
333
- value: { count: 3 }
334
- });
335
- globalExpect(out.call_id).toBe("c1");
336
- globalExpect(JSON.parse(out.output)).toEqual({ count: 3 });
337
- });
338
- it("renders a Failure with reason as a nested error object", () => {
339
- const out = toToolCallOutput({
340
- _tag: "Failure",
341
- call_id: "c2",
342
- tool: "bulk_email",
343
- kind: "denied",
344
- reason: "spam concern"
345
- });
346
- globalExpect(JSON.parse(out.output)).toEqual({ error: {
347
- kind: "denied",
348
- message: "spam concern"
349
- } });
350
- });
351
- it("renders a Failure without reason (omits message)", () => {
352
- const out = toToolCallOutput({
353
- _tag: "Failure",
354
- call_id: "c3",
355
- tool: "delete_database",
356
- kind: "cancelled"
357
- });
358
- globalExpect(JSON.parse(out.output)).toEqual({ error: { kind: "cancelled" } });
359
- });
360
- });
361
- describe("run", () => {
362
- it("runs all calls passed to it", async () => {
363
- const collected = await Effect.runPromise(Stream.runCollect(run(allTools, calls)));
364
- globalExpect(collected.filter(isOutput)).toHaveLength(3);
365
- globalExpect(collected.filter(isOutput).every((e) => isOk(e.result))).toBe(true);
366
- });
367
- });
368
- //#endregion
369
- export {};
370
-
371
- //# sourceMappingURL=Approval.test.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Approval.test.mjs","names":["makeTool","makeToolkit","Items.toolCallOutput","Items.userText"],"sources":["../../src/tool/Approval.test.ts"],"sourcesContent":["/**\n * Tests for approval planners + history-reconciliation primitives. Exercises\n * the full HITL + streaming-tool stack end-to-end by composing approval plans\n * with `run`, with the four wire-shaped scenarios:\n *\n * 1. Approval : gated calls approved → tools execute, structured Values\n * 2. Denial : gated calls denied → Failure(denied) results\n * 3. Cancellation : missing verdicts → Failure(cancelled) results\n * 4. Mixed + history : reconciliation via cancelAllPending\n *\n * Plus: hallucinated tool name (graceful Failure), unknown_tool kind.\n */\nimport { Effect, Queue, Schema, Stream } from \"effect\"\nimport { describe, expect, it } from \"vitest\"\nimport * as Items from \"../domain/Items.js\"\nimport { findUnansweredCalls, cancelAllPending, isReconciled } from \"./HistoryCheck.js\"\nimport { type ToolResult, isFailure, isOk, toToolCallOutput } from \"./ToolResult.js\"\nimport {\n type ApprovalMapEntry,\n type ApprovalDecision,\n type Verdict,\n fromMap,\n fromQueue,\n} from \"./Approval.js\"\nimport { fromEffectSchema, make as makeTool } from \"./Tool.js\"\nimport { make as makeToolkit, run } from \"./Toolkit.js\"\nimport { ToolEvent, isApprovalRequested, isProgress, isOutput } from \"./ToolEvent.js\"\n\n// ---------------------------------------------------------------------------\n// Three demo tools covering the matrix:\n// - web_search : streaming, no approval\n// - bulk_email : streaming, requires approval\n// - delete_database : non-streaming, requires approval\n// ---------------------------------------------------------------------------\n\nconst webSearch = makeTool({\n name: \"web_search\",\n description: \"search\",\n inputSchema: fromEffectSchema(Schema.Struct({ query: Schema.String })),\n run: ({ query }, emit) =>\n Effect.gen(function* () {\n const hits = [\n { url: \"a\", title: `${query} 1` },\n { url: \"b\", title: `${query} 2` },\n { url: \"c\", title: `${query} 3` },\n ]\n yield* Stream.runForEach(Stream.fromIterable(hits), emit)\n return { count: hits.length }\n }),\n})\n\nconst bulkEmail = makeTool({\n name: \"bulk_email\",\n description: \"send\",\n inputSchema: fromEffectSchema(\n Schema.Struct({ recipients: Schema.Array(Schema.String), subject: Schema.String }),\n ),\n run: ({ recipients }, emit) =>\n Effect.gen(function* () {\n yield* Effect.forEach(recipients, (_, i) =>\n emit({ type: \"progress\" as const, sent: i + 1, total: recipients.length }),\n )\n return { status: \"sent\" as const, delivered: recipients.length }\n }),\n})\n\nconst deleteDatabase = makeTool({\n name: \"delete_database\",\n description: \"drop\",\n inputSchema: fromEffectSchema(Schema.Struct({ name: Schema.String })),\n run: ({ name }) => Effect.succeed({ status: \"dropped\", name }),\n})\n\nconst allTools = makeToolkit(webSearch, bulkEmail, deleteDatabase)\nconst SENSITIVE = new Set([\"bulk_email\", \"delete_database\"])\nconst isSensitive = (call: Items.ToolCall) => SENSITIVE.has(call.name)\n\nconst fc = (call_id: string, name: string, args: unknown): Items.ToolCall => ({\n type: \"function_call\",\n call_id,\n name,\n arguments: JSON.stringify(args),\n})\n\nconst calls = [\n fc(\"c1\", \"web_search\", { query: \"effect\" }),\n fc(\"c2\", \"bulk_email\", { recipients: [\"a@x\", \"b@x\"], subject: \"Hi\" }),\n fc(\"c3\", \"delete_database\", { name: \"prod\" }),\n]\n\nconst resultsFrom = (collected: ReadonlyArray<ToolEvent>): ReadonlyArray<ToolResult> =>\n collected.filter(isOutput).map((e) => e.result)\n\nconst byCallId = (results: ReadonlyArray<ToolResult>) => new Map(results.map((r) => [r.call_id, r]))\n\nconst rejectedStream = (rejected: ReadonlyArray<ToolResult>) =>\n Stream.fromIterable(rejected.map((result) => ToolEvent.Output({ result })))\n\nconst eventsFromApprovalMap = (approvals: ReadonlyMap<string, ApprovalMapEntry>) => {\n const plan = fromMap(isSensitive, approvals)(calls)\n return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected))\n}\n\nconst eventsFromDecision = (decision: ApprovalDecision): Stream.Stream<ToolEvent> =>\n decision._tag === \"Approved\"\n ? run(allTools, [decision.call])\n : Stream.succeed(ToolEvent.Output({ result: decision.result }))\n\n// ---------------------------------------------------------------------------\n// fromMap: HTTP-style scenarios\n// ---------------------------------------------------------------------------\n\ndescribe(\"fromMap + run\", () => {\n it(\"approval: all gated approved → tools execute, structured Values\", async () => {\n const approvals = new Map<string, ApprovalMapEntry>([\n [\"c2\", { decision: \"approve\" }],\n [\"c3\", { decision: \"approve\" }],\n ])\n const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)))\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c1\")).toMatchObject({ _tag: \"Ok\", value: { count: 3 } })\n expect(by.get(\"c2\")).toMatchObject({\n _tag: \"Ok\",\n value: { status: \"sent\", delivered: 2 },\n })\n expect(by.get(\"c3\")).toMatchObject({\n _tag: \"Ok\",\n value: { status: \"dropped\", name: \"prod\" },\n })\n\n // No ApprovalRequested events from the pure HTTP flow.\n expect(collected.filter(isApprovalRequested)).toHaveLength(0)\n })\n\n it(\"denial: gated denied → Failure(denied), no execution\", async () => {\n const approvals = new Map<string, ApprovalMapEntry>([\n [\"c2\", { decision: \"deny\", reason: \"spam concern\" }],\n [\"c3\", { decision: \"deny\", reason: \"prod is sacred\" }],\n ])\n const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)))\n\n // bulk_email never ran.\n expect(collected.filter(isProgress).filter((e) => e.tool === \"bulk_email\")).toHaveLength(0)\n\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c2\")).toMatchObject({\n _tag: \"Failure\",\n kind: \"denied\",\n reason: \"spam concern\",\n })\n expect(by.get(\"c3\")).toMatchObject({\n _tag: \"Failure\",\n kind: \"denied\",\n reason: \"prod is sacred\",\n })\n })\n\n it(\"cancellation: missing verdicts → Failure(cancelled)\", async () => {\n const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(new Map())))\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c1\")).toMatchObject({ _tag: \"Ok\", value: { count: 3 } })\n expect(by.get(\"c2\")).toMatchObject({ _tag: \"Failure\", kind: \"cancelled\" })\n expect(by.get(\"c3\")).toMatchObject({ _tag: \"Failure\", kind: \"cancelled\" })\n })\n\n it(\"mixed: approve + deny + omit → all three kinds\", async () => {\n const approvals = new Map<string, ApprovalMapEntry>([\n [\"c2\", { decision: \"approve\" }],\n // c3 omitted → cancelled\n ])\n const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)))\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c1\")).toMatchObject({ _tag: \"Ok\", value: { count: 3 } })\n expect(by.get(\"c2\")).toMatchObject({ _tag: \"Ok\", value: { status: \"sent\" } })\n expect(by.get(\"c3\")).toMatchObject({ _tag: \"Failure\", kind: \"cancelled\" })\n })\n})\n\n// ---------------------------------------------------------------------------\n// Graceful degradation: hallucinated tool name doesn't kill the turn.\n// ---------------------------------------------------------------------------\n\ndescribe(\"run: graceful degradation\", () => {\n it(\"unknown tool name → Failure(unknown_tool); other calls still execute\", async () => {\n const callsWithBogus = [\n fc(\"c1\", \"web_search\", { query: \"x\" }),\n fc(\"c2\", \"does_not_exist\", {}),\n fc(\"c3\", \"delete_database\", { name: \"prod\" }),\n ]\n const collected = await Effect.runPromise(\n Stream.runCollect(\n (() => {\n const plan = fromMap(\n isSensitive,\n new Map([[\"c3\", { decision: \"approve\" }]]),\n )(callsWithBogus)\n return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected))\n })(),\n ),\n )\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c1\")).toMatchObject({ _tag: \"Ok\" })\n expect(by.get(\"c2\")).toMatchObject({ _tag: \"Failure\", kind: \"unknown_tool\" })\n expect(by.get(\"c3\")).toMatchObject({ _tag: \"Ok\", value: { status: \"dropped\" } })\n })\n})\n\n// ---------------------------------------------------------------------------\n// fromQueue: WebSocket-style scenarios\n// ---------------------------------------------------------------------------\n\ndescribe(\"fromQueue + run\", () => {\n it(\"queue-driven: approve + deny resolve correctly with ApprovalRequested events\", async () => {\n const collected = await Effect.runPromise(\n Effect.gen(function* () {\n const verdicts = yield* Queue.unbounded<{\n readonly call_id: string\n readonly decision: \"approve\" | \"deny\"\n readonly reason?: string\n }>()\n yield* Queue.offer(verdicts, { call_id: \"c2\", decision: \"approve\" })\n yield* Queue.offer(verdicts, {\n call_id: \"c3\",\n decision: \"deny\",\n reason: \"too risky\",\n })\n\n // Stream.unwrap supplies the Scope for fromQueue's router.\n const events = Stream.unwrap(\n Effect.gen(function* () {\n const { approved, decisions, approvalRequests } = yield* fromQueue(\n isSensitive,\n verdicts,\n )(calls)\n return Stream.merge(\n approvalRequests,\n Stream.merge(\n run(allTools, approved),\n decisions.pipe(Stream.flatMap(eventsFromDecision)),\n ),\n )\n }),\n )\n return yield* Stream.runCollect(events)\n }),\n )\n\n expect(collected.filter(isApprovalRequested)).toHaveLength(2)\n\n const by = byCallId(resultsFrom(collected))\n expect(by.get(\"c2\")).toMatchObject({ _tag: \"Ok\", value: { status: \"sent\" } })\n expect(by.get(\"c3\")).toMatchObject({\n _tag: \"Failure\",\n kind: \"denied\",\n reason: \"too risky\",\n })\n })\n\n it(\"router retires per round, so sequential rounds over one queue both resolve\", async () => {\n // A verdict-per-round over one shared queue and scope. The old\n // never-terminating router would still be alive for round 1 and steal\n // round 2's verdict (not in its map), hanging round 2.\n const decisions = await Effect.runPromise(\n Effect.scoped(\n Effect.gen(function* () {\n const verdicts = yield* Queue.unbounded<Verdict>()\n const round = (id: string) => [fc(id, \"delete_database\", { name: \"prod\" })]\n\n const r1 = yield* fromQueue(isSensitive, verdicts)(round(\"c1\"))\n yield* Queue.offer(verdicts, { call_id: \"c1\", decision: \"approve\" })\n const d1 = Array.from(yield* Stream.runCollect(r1.decisions))\n\n const r2 = yield* fromQueue(isSensitive, verdicts)(round(\"c2\"))\n yield* Queue.offer(verdicts, { call_id: \"c2\", decision: \"deny\", reason: \"no\" })\n const d2 = Array.from(yield* Stream.runCollect(r2.decisions))\n\n return { d1, d2 }\n }),\n ),\n )\n expect(decisions.d1[0]).toMatchObject({ _tag: \"Approved\" })\n expect(decisions.d2[0]).toMatchObject({ _tag: \"Rejected\", result: { kind: \"denied\" } })\n })\n\n it(\"timeout resolves an unanswered gated call as cancelled\", async () => {\n const decisions = await Effect.runPromise(\n Effect.scoped(\n Effect.gen(function* () {\n const verdicts = yield* Queue.unbounded<Verdict>()\n const { decisions } = yield* fromQueue(isSensitive, verdicts, { timeout: \"10 millis\" })([\n fc(\"c1\", \"delete_database\", { name: \"prod\" }),\n ])\n return Array.from(yield* Stream.runCollect(decisions))\n }),\n ),\n )\n expect(decisions[0]).toMatchObject({\n _tag: \"Rejected\",\n result: { _tag: \"Failure\", kind: \"cancelled\" },\n })\n })\n})\n\n// ---------------------------------------------------------------------------\n// History reconciliation\n// ---------------------------------------------------------------------------\n\ndescribe(\"findUnansweredCalls / cancelAllPending / isReconciled\", () => {\n const orphan = fc(\"c99\", \"delete_database\", { name: \"prod\" })\n const answered = fc(\"c98\", \"web_search\", { query: \"x\" })\n const answeredOutput = Items.toolCallOutput(\"c98\", JSON.stringify({ count: 0 }))\n\n it(\"findUnansweredCalls returns only orphans\", () => {\n const history = [Items.userText(\"hi\"), answered, orphan, answeredOutput]\n const unanswered = findUnansweredCalls(history)\n expect(unanswered).toHaveLength(1)\n expect(unanswered[0]!.call_id).toBe(\"c99\")\n })\n\n it(\"isReconciled is false when orphans exist, true otherwise\", () => {\n const stale = [Items.userText(\"hi\"), orphan]\n expect(isReconciled(stale)).toBe(false)\n const reconciled = [...stale, ...cancelAllPending(stale).map(toToolCallOutput)]\n expect(isReconciled(reconciled)).toBe(true)\n })\n\n it(\"cancelAllPending synthesizes one Failure(cancelled) per orphan\", () => {\n const history = [Items.userText(\"hi\"), answered, orphan, answeredOutput]\n const closures = cancelAllPending(history, \"user moved on\")\n expect(closures).toHaveLength(1)\n const c = closures[0]!\n expect(isFailure(c)).toBe(true)\n expect(c).toMatchObject({\n _tag: \"Failure\",\n call_id: \"c99\",\n kind: \"cancelled\",\n reason: \"user moved on\",\n })\n })\n\n it(\"follow-up: map closures to ToolCallOutput before appending new user message\", () => {\n const stale = [Items.userText(\"first request\"), orphan]\n const closures = cancelAllPending(stale, \"user redirected\")\n const reconciled = [...stale, ...closures.map(toToolCallOutput), Items.userText(\"never mind\")]\n expect(findUnansweredCalls(reconciled)).toHaveLength(0)\n })\n})\n\n// ---------------------------------------------------------------------------\n// Wire conversion\n// ---------------------------------------------------------------------------\n\ndescribe(\"toToolCallOutput\", () => {\n it(\"round-trips a Value result\", () => {\n const r: ToolResult = {\n _tag: \"Ok\",\n call_id: \"c1\",\n tool: \"web_search\",\n value: { count: 3 },\n }\n const out = toToolCallOutput(r)\n expect(out.call_id).toBe(\"c1\")\n expect(JSON.parse(out.output)).toEqual({ count: 3 })\n })\n\n it(\"renders a Failure with reason as a nested error object\", () => {\n const r: ToolResult = {\n _tag: \"Failure\",\n call_id: \"c2\",\n tool: \"bulk_email\",\n kind: \"denied\",\n reason: \"spam concern\",\n }\n const out = toToolCallOutput(r)\n expect(JSON.parse(out.output)).toEqual({ error: { kind: \"denied\", message: \"spam concern\" } })\n })\n\n it(\"renders a Failure without reason (omits message)\", () => {\n const r: ToolResult = {\n _tag: \"Failure\",\n call_id: \"c3\",\n tool: \"delete_database\",\n kind: \"cancelled\",\n }\n const out = toToolCallOutput(r)\n expect(JSON.parse(out.output)).toEqual({ error: { kind: \"cancelled\" } })\n })\n})\n\n// ---------------------------------------------------------------------------\n// run\n// ---------------------------------------------------------------------------\n\ndescribe(\"run\", () => {\n it(\"runs all calls passed to it\", async () => {\n const collected = await Effect.runPromise(Stream.runCollect(run(allTools, calls)))\n expect(collected.filter(isOutput)).toHaveLength(3)\n expect(collected.filter(isOutput).every((e) => isOk(e.result))).toBe(true)\n })\n})\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
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-BsYQRjmW.mjs";
2
- export { ToolResult, cancelled, denied, executionError, failed, isFailure, isOk, nonLocalTool, serializeValue, toToolCallOutput, validationError };
@@ -1,85 +0,0 @@
1
- import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
- import { toolCallOutput } from "../domain/Items.mjs";
3
- import { Data } from "effect";
4
- //#region src/tool/ToolResult.ts
5
- /**
6
- * Post-execution and synthetic tool results.
7
- *
8
- * - Executed tools emit ToolResult.Ok.
9
- * - Approval/cancellation policy emits synthetic ToolResult.Failure.
10
- *
11
- * Wire conversion stays at the recipe boundary via `toToolCallOutput`
12
- * so recipes can inspect, redact, or audit values before serialization.
13
- *
14
- * `output` and `reason` are `string`, not `unknown`: the wire wants strings,
15
- * and `unknown` would invite non-serializable values (Date, Map, BigInt,
16
- * fn). Recipes that want structured detail JSON.stringify themselves.
17
- */
18
- var ToolResult_exports = /* @__PURE__ */ __exportAll({
19
- ToolResult: () => ToolResult,
20
- cancelled: () => cancelled,
21
- denied: () => denied,
22
- executionError: () => executionError,
23
- failed: () => failed,
24
- isFailure: () => isFailure,
25
- isOk: () => isOk,
26
- nonLocalTool: () => nonLocalTool,
27
- serializeValue: () => serializeValue,
28
- toToolCallOutput: () => toToolCallOutput,
29
- validationError: () => validationError
30
- });
31
- /**
32
- * Namespace of constructors, type guards, and matchers for `ToolResult`,
33
- * provided by `Data.taggedEnum`. Use `ToolResult.$is("Ok")` for type
34
- * narrowing and `ToolResult.$match({ Ok, Failure })` for exhaustive
35
- * pattern matching. Synthetic-result helpers (`denied`, `cancelled`,
36
- * `executionError`, `failed`) below are kinder constructors than the
37
- * raw `ToolResult.Failure(...)`.
38
- */
39
- const ToolResult = Data.taggedEnum();
40
- const isOk = ToolResult.$is("Ok");
41
- const isFailure = ToolResult.$is("Failure");
42
- const failed = (call, kind, reason) => ToolResult.Failure({
43
- call_id: call.call_id,
44
- tool: call.name,
45
- kind,
46
- ...reason !== void 0 ? { reason } : {}
47
- });
48
- /** Explicit user/policy rejection. */
49
- const denied = (call, reason) => failed(call, "denied", reason);
50
- /** Implicit non-answer (follow-up, inactivity, abort). */
51
- const cancelled = (call, reason) => failed(call, "cancelled", reason);
52
- /** Tool's own execution failed (JSON parse error, runtime crash). */
53
- const executionError = (call, reason) => failed(call, "execution_error", reason);
54
- /**
55
- * The model called a tool that is visible to it but has no local executor — a
56
- * provider-hosted, signal, or interaction tool routed through `Toolkit.run`.
57
- * Distinct from `unknown_tool` (no such tool at all): the loop is expected to
58
- * intercept these kinds before execution, so this kind flags a missing handler.
59
- */
60
- const nonLocalTool = (call, reason) => failed(call, "non_local_tool", reason ?? `Tool "${call.name}" is visible to the model but has no local executor`);
61
- /**
62
- * The model's `arguments` failed the tool's input schema. Distinct kind from
63
- * `execution_error` so recipes can tell a contract violation apart from a
64
- * runtime crash.
65
- */
66
- const validationError = (call, reason) => failed(call, "input_validation_error", reason ?? "Tool input failed schema validation");
67
- /**
68
- * Serialize a success value for the wire. A `string` passes through raw
69
- * (models read plain text best; JSON.stringify would quote and escape it);
70
- * `undefined` becomes `"null"`; anything else is JSON.
71
- */
72
- const serializeValue = (value) => typeof value === "string" ? value : JSON.stringify(value ?? null);
73
- /** The one failure shape the model sees: `{"error":{"kind","message"}}`. */
74
- const failureBody = (kind, message) => JSON.stringify({ error: message !== void 0 ? {
75
- kind,
76
- message
77
- } : { kind } });
78
- const toToolCallOutput = (r) => ToolResult.$match(r, {
79
- Ok: (v) => toolCallOutput(v.call_id, serializeValue(v.value)),
80
- Failure: (f) => toolCallOutput(f.call_id, failureBody(f.kind, f.reason))
81
- });
82
- //#endregion
83
- export { ToolResult, cancelled, denied, executionError, failed, isFailure, isOk, nonLocalTool, serializeValue, ToolResult_exports as t, toToolCallOutput, validationError };
84
-
85
- //# sourceMappingURL=ToolResult.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolResult.mjs","names":[],"sources":["../../src/tool/ToolResult.ts"],"sourcesContent":["/**\n * Post-execution and synthetic tool results.\n *\n * - Executed tools emit ToolResult.Ok.\n * - Approval/cancellation policy emits synthetic ToolResult.Failure.\n *\n * Wire conversion stays at the recipe boundary via `toToolCallOutput`\n * so recipes can inspect, redact, or audit values before serialization.\n *\n * `output` and `reason` are `string`, not `unknown`: the wire wants strings,\n * and `unknown` would invite non-serializable values (Date, Map, BigInt,\n * fn). Recipes that want structured detail JSON.stringify themselves.\n */\nimport { Data } from \"effect\"\nimport type { ToolCall, ToolCallOutput } from \"../domain/Items.js\"\nimport { toolCallOutput } from \"../domain/Items.js\"\n\n// ---------------------------------------------------------------------------\n// ToolResult\n// ---------------------------------------------------------------------------\n\nexport type ToolResult = Data.TaggedEnum<{\n Ok: {\n readonly call_id: string\n readonly tool: string\n readonly value: unknown\n }\n Failure: {\n readonly call_id: string\n readonly tool: string\n readonly kind: string\n readonly reason?: string\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolResult`,\n * provided by `Data.taggedEnum`. Use `ToolResult.$is(\"Ok\")` for type\n * narrowing and `ToolResult.$match({ Ok, Failure })` for exhaustive\n * pattern matching. Synthetic-result helpers (`denied`, `cancelled`,\n * `executionError`, `failed`) below are kinder constructors than the\n * raw `ToolResult.Failure(...)`.\n */\nexport const ToolResult = Data.taggedEnum<ToolResult>()\n\nexport const isOk = ToolResult.$is(\"Ok\")\nexport const isFailure = ToolResult.$is(\"Failure\")\n\n// Synthesizers. `denied` and `cancelled` are operationally distinct;\n// anything else is just a recipe-chosen `kind` via `failed`.\n// ---------------------------------------------------------------------------\n\nexport const failed = (call: ToolCall, kind: string, reason?: string): ToolResult =>\n ToolResult.Failure({\n call_id: call.call_id,\n tool: call.name,\n kind,\n ...(reason !== undefined ? { reason } : {}),\n })\n\n/** Explicit user/policy rejection. */\nexport const denied = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"denied\", reason)\n\n/** Implicit non-answer (follow-up, inactivity, abort). */\nexport const cancelled = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"cancelled\", reason)\n\n/** Tool's own execution failed (JSON parse error, runtime crash). */\nexport const executionError = (call: ToolCall, reason: string): ToolResult =>\n failed(call, \"execution_error\", reason)\n\n/**\n * The model called a tool that is visible to it but has no local executor — a\n * provider-hosted, signal, or interaction tool routed through `Toolkit.run`.\n * Distinct from `unknown_tool` (no such tool at all): the loop is expected to\n * intercept these kinds before execution, so this kind flags a missing handler.\n */\nexport const nonLocalTool = (call: ToolCall, reason?: string): ToolResult =>\n failed(\n call,\n \"non_local_tool\",\n reason ?? `Tool \"${call.name}\" is visible to the model but has no local executor`,\n )\n\n/**\n * The model's `arguments` failed the tool's input schema. Distinct kind from\n * `execution_error` so recipes can tell a contract violation apart from a\n * runtime crash.\n */\nexport const validationError = (call: ToolCall, reason?: string): ToolResult =>\n failed(call, \"input_validation_error\", reason ?? \"Tool input failed schema validation\")\n\n// ---------------------------------------------------------------------------\n// Wire conversion - the one place structured → string happens.\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a success value for the wire. A `string` passes through raw\n * (models read plain text best; JSON.stringify would quote and escape it);\n * `undefined` becomes `\"null\"`; anything else is JSON.\n */\nexport const serializeValue = (value: unknown): string =>\n typeof value === \"string\" ? value : JSON.stringify(value ?? null)\n\n/** The one failure shape the model sees: `{\"error\":{\"kind\",\"message\"}}`. */\nconst failureBody = (kind: string, message?: string): string =>\n JSON.stringify({ error: message !== undefined ? { kind, message } : { kind } })\n\nexport const toToolCallOutput = (r: ToolResult): ToolCallOutput =>\n ToolResult.$match(r, {\n Ok: (v) => toolCallOutput(v.call_id, serializeValue(v.value)),\n Failure: (f) => toolCallOutput(f.call_id, failureBody(f.kind, f.reason)),\n })\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,aAAa,KAAK,WAAuB;AAEtD,MAAa,OAAO,WAAW,IAAI,IAAI;AACvC,MAAa,YAAY,WAAW,IAAI,SAAS;AAMjD,MAAa,UAAU,MAAgB,MAAc,WACnD,WAAW,QAAQ;CACjB,SAAS,KAAK;CACd,MAAM,KAAK;CACX;CACA,GAAI,WAAW,KAAA,IAAY,EAAE,OAAO,IAAI,CAAC;AAC3C,CAAC;;AAGH,MAAa,UAAU,MAAgB,WACrC,OAAO,MAAM,UAAU,MAAM;;AAG/B,MAAa,aAAa,MAAgB,WACxC,OAAO,MAAM,aAAa,MAAM;;AAGlC,MAAa,kBAAkB,MAAgB,WAC7C,OAAO,MAAM,mBAAmB,MAAM;;;;;;;AAQxC,MAAa,gBAAgB,MAAgB,WAC3C,OACE,MACA,kBACA,UAAU,SAAS,KAAK,KAAK,oDAC/B;;;;;;AAOF,MAAa,mBAAmB,MAAgB,WAC9C,OAAO,MAAM,0BAA0B,UAAU,qCAAqC;;;;;;AAWxF,MAAa,kBAAkB,UAC7B,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,IAAI;;AAGlE,MAAM,eAAe,MAAc,YACjC,KAAK,UAAU,EAAE,OAAO,YAAY,KAAA,IAAY;CAAE;CAAM;AAAQ,IAAI,EAAE,KAAK,EAAE,CAAC;AAEhF,MAAa,oBAAoB,MAC/B,WAAW,OAAO,GAAG;CACnB,KAAK,MAAM,eAAe,EAAE,SAAS,eAAe,EAAE,KAAK,CAAC;CAC5D,UAAU,MAAM,eAAe,EAAE,SAAS,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC;AACzE,CAAC"}