@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.
Files changed (218) hide show
  1. package/README.md +5 -5
  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 +4 -4
  26. package/dist/dist-B5K-y4Be.mjs +191 -0
  27. package/dist/dist-B5K-y4Be.mjs.map +1 -0
  28. package/dist/dist-CorQTQL-.mjs +1533 -0
  29. package/dist/dist-CorQTQL-.mjs.map +1 -0
  30. package/dist/dist-DNeuh84d.mjs +7 -0
  31. package/dist/dist-DNeuh84d.mjs.map +1 -0
  32. package/dist/dist-DzeSyeYF.mjs +10253 -0
  33. package/dist/dist-DzeSyeYF.mjs.map +1 -0
  34. package/dist/domain/File.d.mts +2 -0
  35. package/dist/domain/File.mjs +51 -0
  36. package/dist/domain/File.mjs.map +1 -0
  37. package/dist/domain/Image.d.mts +2 -2
  38. package/dist/domain/Image.mjs +12 -2
  39. package/dist/domain/Image.mjs.map +1 -1
  40. package/dist/domain/Items.d.mts +2 -2
  41. package/dist/domain/Items.mjs +65 -4
  42. package/dist/domain/Items.mjs.map +1 -1
  43. package/dist/domain/Realtime.d.mts +72 -490
  44. package/dist/domain/Realtime.d.mts.map +1 -1
  45. package/dist/domain/Research.d.mts +1 -1
  46. package/dist/domain/Transcript.test.mjs +3 -3
  47. package/dist/domain/Turn.d.mts +57 -262
  48. package/dist/domain/Turn.d.mts.map +1 -1
  49. package/dist/domain/Turn.mjs +1 -1
  50. package/dist/domain/Turn.test.mjs +2 -2
  51. package/dist/embedding-model/Embedding.d.mts +1 -1
  52. package/dist/embedding-model/EmbeddingModel.test.mjs +2 -2
  53. package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
  54. package/dist/image-generator/ImageGenerator.d.mts +10 -66
  55. package/dist/image-generator/ImageGenerator.d.mts.map +1 -1
  56. package/dist/index.C4tKZ0yW-LuFnBtmu.mjs +7042 -0
  57. package/dist/index.C4tKZ0yW-LuFnBtmu.mjs.map +1 -0
  58. package/dist/index.d.mts +10 -9
  59. package/dist/index.mjs +7 -6
  60. package/dist/language-model/LanguageModel.d.mts +2 -2
  61. package/dist/language-model/LanguageModel.test.mjs +2 -2
  62. package/dist/loop/Loop.d.mts +1 -1
  63. package/dist/loop/Loop.mjs.map +1 -1
  64. package/dist/loop/Loop.test.mjs +3 -3
  65. package/dist/math/Probability.test.mjs +2 -2
  66. package/dist/messenger/Messenger.d.mts +43 -289
  67. package/dist/messenger/Messenger.d.mts.map +1 -1
  68. package/dist/messenger/MessengerAdapter.test.mjs +3 -3
  69. package/dist/music-generator/MusicGenerator.test.mjs +3 -3
  70. package/dist/observability/Metrics.d.mts +1 -1
  71. package/dist/observability/Metrics.test.mjs +3 -3
  72. package/dist/observability/Telemetry.d.mts +1 -1
  73. package/dist/observability/Telemetry.mjs +1 -1
  74. package/dist/observability/Telemetry.mjs.map +1 -1
  75. package/dist/observability/Telemetry.test.mjs +4 -4
  76. package/dist/observability/Telemetry.test.mjs.map +1 -1
  77. package/dist/realtime/RealtimeSession.d.mts +1 -1
  78. package/dist/realtime/RealtimeSession.test.mjs +4 -4
  79. package/dist/retry/Retry.test.mjs +2 -2
  80. package/dist/sandbox/Sandbox.d.mts +32 -237
  81. package/dist/sandbox/Sandbox.d.mts.map +1 -1
  82. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +3 -3
  83. package/dist/streaming/JSONL.test.mjs +2 -2
  84. package/dist/streaming/Multipart.d.mts +1 -1
  85. package/dist/streaming/Multipart.mjs +3 -2
  86. package/dist/streaming/Multipart.mjs.map +1 -1
  87. package/dist/streaming/Multipart.test.mjs +28 -0
  88. package/dist/streaming/Multipart.test.mjs.map +1 -0
  89. package/dist/streaming/SSE.d.mts +1 -1
  90. package/dist/streaming/SSE.mjs +2 -2
  91. package/dist/streaming/SSE.mjs.map +1 -1
  92. package/dist/streaming/SSE.test.mjs +2 -2
  93. package/dist/streaming/Settle.test.mjs +3 -3
  94. package/dist/streaming/WebSocketSession.d.mts +5 -3
  95. package/dist/streaming/WebSocketSession.d.mts.map +1 -1
  96. package/dist/streaming/WebSocketSession.mjs +22 -10
  97. package/dist/streaming/WebSocketSession.mjs.map +1 -1
  98. package/dist/streaming/WebSocketSession.test.mjs +41 -5
  99. package/dist/streaming/WebSocketSession.test.mjs.map +1 -1
  100. package/dist/structured-format/StructuredFormat.mjs.map +1 -1
  101. package/dist/structured-format/StructuredFormat.test.mjs +2 -2
  102. package/dist/testing/FakeWebSocket.d.mts +5 -1
  103. package/dist/testing/FakeWebSocket.d.mts.map +1 -1
  104. package/dist/testing/FakeWebSocket.mjs +12 -6
  105. package/dist/testing/FakeWebSocket.mjs.map +1 -1
  106. package/dist/testing/MockMessenger.d.mts +19 -134
  107. package/dist/testing/MockMessenger.d.mts.map +1 -1
  108. package/dist/testing/MockProvider.d.mts +1 -1
  109. package/dist/testing/MockSandbox.d.mts +65 -446
  110. package/dist/testing/MockSandbox.d.mts.map +1 -1
  111. package/dist/tool/Content.d.mts +2 -0
  112. package/dist/tool/Content.mjs +46 -0
  113. package/dist/tool/Content.mjs.map +1 -0
  114. package/dist/tool/Content.test.d.mts +1 -0
  115. package/dist/tool/Content.test.mjs +48 -0
  116. package/dist/tool/Content.test.mjs.map +1 -0
  117. package/dist/tool/HistoryCheck.d.mts +6 -15
  118. package/dist/tool/HistoryCheck.d.mts.map +1 -1
  119. package/dist/tool/HistoryCheck.mjs +10 -24
  120. package/dist/tool/HistoryCheck.mjs.map +1 -1
  121. package/dist/tool/Tool.d.mts +2 -2
  122. package/dist/tool/Tool.mjs +55 -130
  123. package/dist/tool/Tool.mjs.map +1 -1
  124. package/dist/tool/Tool.test.mjs +222 -178
  125. package/dist/tool/Tool.test.mjs.map +1 -1
  126. package/dist/tool/ToolDecision.d.mts +164 -0
  127. package/dist/tool/ToolDecision.d.mts.map +1 -0
  128. package/dist/tool/ToolDecision.mjs +146 -0
  129. package/dist/tool/ToolDecision.mjs.map +1 -0
  130. package/dist/tool/ToolDecision.test.d.mts +1 -0
  131. package/dist/tool/ToolDecision.test.mjs +330 -0
  132. package/dist/tool/ToolDecision.test.mjs.map +1 -0
  133. package/dist/tool/ToolEvent.d.mts +2 -2
  134. package/dist/tool/ToolEvent.mjs +2 -34
  135. package/dist/tool/ToolEvent.mjs.map +1 -1
  136. package/dist/tool/ToolOutput.d.mts +2 -0
  137. package/dist/tool/ToolOutput.mjs +128 -0
  138. package/dist/tool/ToolOutput.mjs.map +1 -0
  139. package/dist/tool/ToolOutput.test.d.mts +1 -0
  140. package/dist/tool/ToolOutput.test.mjs +153 -0
  141. package/dist/tool/ToolOutput.test.mjs.map +1 -0
  142. package/dist/tool/Toolkit.d.mts +2 -2
  143. package/dist/tool/Toolkit.mjs +230 -165
  144. package/dist/tool/Toolkit.mjs.map +1 -1
  145. package/dist/tool/Toolkit.test.mjs +433 -391
  146. package/dist/tool/Toolkit.test.mjs.map +1 -1
  147. package/dist/transcriber/Transcriber.test.mjs +3 -3
  148. package/dist/web-read/WebReadTool.d.mts +2 -2
  149. package/dist/web-read/WebReadTool.mjs +1 -0
  150. package/dist/web-read/WebReadTool.mjs.map +1 -1
  151. package/dist/web-search/WebSearchTool.d.mts +2 -2
  152. package/dist/web-search/WebSearchTool.mjs +1 -0
  153. package/dist/web-search/WebSearchTool.mjs.map +1 -1
  154. package/package.json +18 -10
  155. package/src/browser/BrowserTool.ts +5 -0
  156. package/src/domain/File.ts +79 -0
  157. package/src/domain/Image.ts +14 -4
  158. package/src/domain/Items.ts +39 -3
  159. package/src/embedding-model/EmbeddingModel.test.ts +2 -2
  160. package/src/index.ts +3 -2
  161. package/src/loop/Loop.ts +25 -14
  162. package/src/observability/Telemetry.test.ts +1 -1
  163. package/src/observability/Telemetry.ts +2 -2
  164. package/src/streaming/Multipart.test.ts +26 -0
  165. package/src/streaming/Multipart.ts +6 -5
  166. package/src/streaming/SSE.ts +2 -2
  167. package/src/streaming/WebSocketSession.test.ts +54 -2
  168. package/src/streaming/WebSocketSession.ts +27 -6
  169. package/src/structured-format/StructuredFormat.ts +1 -1
  170. package/src/testing/FakeWebSocket.ts +21 -6
  171. package/src/tool/Content.test.ts +40 -0
  172. package/src/tool/Content.ts +38 -0
  173. package/src/tool/HistoryCheck.ts +21 -26
  174. package/src/tool/Tool.test.ts +240 -241
  175. package/src/tool/Tool.ts +204 -309
  176. package/src/tool/ToolDecision.test.ts +301 -0
  177. package/src/tool/ToolDecision.ts +360 -0
  178. package/src/tool/ToolEvent.ts +21 -41
  179. package/src/tool/ToolOutput.test.ts +132 -0
  180. package/src/tool/ToolOutput.ts +198 -0
  181. package/src/tool/Toolkit.test.ts +594 -454
  182. package/src/tool/Toolkit.ts +631 -356
  183. package/src/web-read/WebReadTool.ts +2 -1
  184. package/src/web-search/WebSearchTool.ts +2 -1
  185. package/dist/Image-Cv6iBLTy.d.mts.map +0 -1
  186. package/dist/Items-C7MyrYPX.d.mts.map +0 -1
  187. package/dist/Loop-ChpaO-ya.d.mts.map +0 -1
  188. package/dist/Tool-BF78QWtn.d.mts +0 -314
  189. package/dist/Tool-BF78QWtn.d.mts.map +0 -1
  190. package/dist/ToolEvent-Di4a723B.d.mts +0 -144
  191. package/dist/ToolEvent-Di4a723B.d.mts.map +0 -1
  192. package/dist/ToolResult-BsYQRjmW.d.mts +0 -152
  193. package/dist/ToolResult-BsYQRjmW.d.mts.map +0 -1
  194. package/dist/Toolkit-tlL0F2YR.d.mts +0 -193
  195. package/dist/Toolkit-tlL0F2YR.d.mts.map +0 -1
  196. package/dist/dist-BaenoU1A.mjs +0 -145
  197. package/dist/dist-BaenoU1A.mjs.map +0 -1
  198. package/dist/dist-nohcnkD6.mjs +0 -182
  199. package/dist/dist-nohcnkD6.mjs.map +0 -1
  200. package/dist/magic-string.es-CB2hOw9v.mjs +0 -1014
  201. package/dist/magic-string.es-CB2hOw9v.mjs.map +0 -1
  202. package/dist/test.DNmyFkvJ-YPzDR0Co.mjs +0 -10254
  203. package/dist/test.DNmyFkvJ-YPzDR0Co.mjs.map +0 -1
  204. package/dist/tool/Approval.d.mts +0 -113
  205. package/dist/tool/Approval.d.mts.map +0 -1
  206. package/dist/tool/Approval.mjs +0 -80
  207. package/dist/tool/Approval.mjs.map +0 -1
  208. package/dist/tool/Approval.test.mjs +0 -371
  209. package/dist/tool/Approval.test.mjs.map +0 -1
  210. package/dist/tool/ToolResult.d.mts +0 -2
  211. package/dist/tool/ToolResult.mjs +0 -85
  212. package/dist/tool/ToolResult.mjs.map +0 -1
  213. package/dist/utils-CiH_DjFW.mjs +0 -3918
  214. package/dist/utils-CiH_DjFW.mjs.map +0 -1
  215. package/src/tool/Approval.test.ts +0 -400
  216. package/src/tool/Approval.ts +0 -181
  217. package/src/tool/ToolResult.ts +0 -114
  218. /package/dist/{tool/Approval.test.d.mts → streaming/Multipart.test.d.mts} +0 -0
@@ -1,113 +0,0 @@
1
- import { y as ToolCall } from "../Items-C7MyrYPX.mjs";
2
- import { t as ToolResult } from "../ToolResult-BsYQRjmW.mjs";
3
- import { t as ToolEvent } from "../ToolEvent-Di4a723B.mjs";
4
- import { Data, Duration, Effect, Queue, Scope, Stream } from "effect";
5
- declare namespace Approval_d_exports {
6
- export { ApprovalDecision, ApprovalMapEntry, FromQueueOptions, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions };
7
- }
8
- type ToolCallPlan = {
9
- readonly approved: ReadonlyArray<ToolCall>;
10
- readonly rejected: ReadonlyArray<ToolResult>;
11
- };
12
- type ApprovalDecision = Data.TaggedEnum<{
13
- Approved: {
14
- readonly call: ToolCall;
15
- };
16
- Rejected: {
17
- readonly result: ToolResult;
18
- };
19
- }>;
20
- declare const ApprovalDecision: {
21
- readonly $is: <Tag extends "Approved" | "Rejected">(tag: Tag) => (u: unknown) => u is Extract<{
22
- readonly _tag: "Approved";
23
- readonly call: ToolCall;
24
- }, {
25
- readonly _tag: Tag;
26
- }> | Extract<{
27
- readonly _tag: "Rejected";
28
- readonly result: ToolResult;
29
- }, {
30
- readonly _tag: Tag;
31
- }>;
32
- readonly $match: {
33
- <Cases extends { readonly [Tag in "Approved" | "Rejected"]: (args: Extract<{
34
- readonly _tag: "Approved";
35
- readonly call: ToolCall;
36
- } | {
37
- readonly _tag: "Rejected";
38
- readonly result: ToolResult;
39
- }, {
40
- readonly _tag: Tag;
41
- }>) => any; }>(cases: Cases): (value: {
42
- readonly _tag: "Approved";
43
- readonly call: ToolCall;
44
- } | {
45
- readonly _tag: "Rejected";
46
- readonly result: ToolResult;
47
- }) => import("effect/Unify").Unify<ReturnType<Cases["Approved" | "Rejected"]>>;
48
- <Cases extends { readonly [Tag in "Approved" | "Rejected"]: (args: Extract<{
49
- readonly _tag: "Approved";
50
- readonly call: ToolCall;
51
- } | {
52
- readonly _tag: "Rejected";
53
- readonly result: ToolResult;
54
- }, {
55
- readonly _tag: Tag;
56
- }>) => any; }>(value: {
57
- readonly _tag: "Approved";
58
- readonly call: ToolCall;
59
- } | {
60
- readonly _tag: "Rejected";
61
- readonly result: ToolResult;
62
- }, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Approved" | "Rejected"]>>;
63
- };
64
- readonly Approved: Data.TaggedEnum.ConstructorFrom<{
65
- readonly _tag: "Approved";
66
- readonly call: ToolCall;
67
- }, "_tag">;
68
- readonly Rejected: Data.TaggedEnum.ConstructorFrom<{
69
- readonly _tag: "Rejected";
70
- readonly result: ToolResult;
71
- }, "_tag">;
72
- };
73
- declare const approve: (call: ToolCall) => ApprovalDecision;
74
- declare const reject: (result: ToolResult) => ApprovalDecision;
75
- declare const splitApprovalDecisions: (decisions: ReadonlyArray<ApprovalDecision>) => ToolCallPlan;
76
- declare const approvalRequested: (call: ToolCall) => ToolEvent;
77
- type Verdict = {
78
- readonly call_id: string;
79
- readonly decision: "approve" | "deny";
80
- readonly reason?: string;
81
- };
82
- type FromQueueOptions = {
83
- /**
84
- * Resolve any still-pending gated call as `cancelled` once this elapses,
85
- * so a lost or never-sent verdict cannot hang the decisions stream
86
- * forever. Omit to wait indefinitely.
87
- */
88
- readonly timeout?: Duration.Input;
89
- };
90
- /**
91
- * Queue-backed approval planner. Safe calls are returned immediately in
92
- * `approved`; gated calls emit `ApprovalRequested` events and later produce
93
- * one `ApprovalDecision` when their matching verdict arrives.
94
- *
95
- * The verdict queue is a single-consumer transport: run one approval round
96
- * at a time (the router retires once every gated call of the round is
97
- * resolved). For overlapping rounds, use one queue per round.
98
- */
99
- declare const fromQueue: (predicate: (call: ToolCall) => boolean, verdicts: Queue.Dequeue<Verdict>, options?: FromQueueOptions) => (calls: ReadonlyArray<ToolCall>) => Effect.Effect<{
100
- readonly approved: ReadonlyArray<ToolCall>;
101
- readonly decisions: Stream.Stream<ApprovalDecision>;
102
- readonly approvalRequests: Stream.Stream<ToolEvent>;
103
- }, never, Scope.Scope>;
104
- type ApprovalMapEntry = {
105
- readonly decision: "approve";
106
- } | {
107
- readonly decision: "deny";
108
- readonly reason?: string;
109
- };
110
- declare const fromMap: (predicate: (call: ToolCall) => boolean, approvals: ReadonlyMap<string, ApprovalMapEntry>) => (calls: ReadonlyArray<ToolCall>) => ToolCallPlan;
111
- //#endregion
112
- export { ApprovalDecision, ApprovalMapEntry, FromQueueOptions, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions, Approval_d_exports as t };
113
- //# sourceMappingURL=Approval.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Approval.d.mts","names":[],"sources":["../../src/tool/Approval.ts"],"mappings":";;;;;;;KAYY;WACD,UAAU,cAAc;WACxB,UAAU,cAAc;;KAGvB,mBAAmB,KAAK;EAClC;aAAqB,MAAM;;EAC3B;aAAqB,QAAQ;;;cAGlB;;;aAJgB,MAAA;;;;;aACE,QAAA;;;;;;;eADF,MAAA;;;eACE,QAAA;;;;;;;;;;;;eADF,MAAA;;;eACE,QAAA;;;;;;;;;;;;;aADF,MAAA;;;;aACE,QAAA;;;cAKlB,UAAO,MAAU,aAAW;cAE5B,SAAM,QAAY,eAAa;cAG/B,yBAAsB,WAAe,cAAc,sBAAoB;cASvE,oBAAiB,MAAU,aAAW;KAWvC;WACD;WACA;WACA;;KAGC;;;;;;WAMD,UAAU,SAAS;;;;;;;;;;;cAYjB,YAAS,YAEN,MAAM,sBAAoB,UAC5B,MAAM,QAAQ,UAAQ,UACtB,sBAAgB,OAGnB,cAAc,cACpB,OAAO;WAEG,UAAU,cAAc;WACxB,WAAW,OAAO,OAAO;WACzB,kBAAkB,OAAO,OAAO;UAG3C,MAAM;KA8EE;WACG;;WACA;WAA2B;;cAE7B,UAAO,YACL,MAAM,sBAAoB,WAAa,oBAAoB,uBAAiB,OACjF,cAAc,cAAY"}
@@ -1,80 +0,0 @@
1
- import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
- import { cancelled, denied } from "./ToolResult.mjs";
3
- import { ToolEvent } from "./ToolEvent.mjs";
4
- import { Data, Deferred, Effect, Queue, Ref, Stream } from "effect";
5
- //#region src/tool/Approval.ts
6
- /**
7
- * Approval helpers for the two transport flavors.
8
- *
9
- * These helpers only decide which calls are approved and which synthetic
10
- * results must be returned to the model. Tool execution stays explicit at
11
- * the recipe boundary via `Toolkit.run`.
12
- */
13
- var Approval_exports = /* @__PURE__ */ __exportAll({
14
- ApprovalDecision: () => ApprovalDecision,
15
- approvalRequested: () => approvalRequested,
16
- approve: () => approve,
17
- fromMap: () => fromMap,
18
- fromQueue: () => fromQueue,
19
- reject: () => reject,
20
- splitApprovalDecisions: () => splitApprovalDecisions
21
- });
22
- const ApprovalDecision = Data.taggedEnum();
23
- const approve = (call) => ApprovalDecision.Approved({ call });
24
- const reject = (result) => ApprovalDecision.Rejected({ result });
25
- const splitApprovalDecisions = (decisions) => decisions.reduce((acc, decision) => decision._tag === "Approved" ? {
26
- ...acc,
27
- approved: [...acc.approved, decision.call]
28
- } : {
29
- ...acc,
30
- rejected: [...acc.rejected, decision.result]
31
- }, {
32
- approved: [],
33
- rejected: []
34
- });
35
- const approvalRequested = (call) => ToolEvent.ApprovalRequested({
36
- call_id: call.call_id,
37
- tool: call.name,
38
- arguments: call.arguments
39
- });
40
- /**
41
- * Queue-backed approval planner. Safe calls are returned immediately in
42
- * `approved`; gated calls emit `ApprovalRequested` events and later produce
43
- * one `ApprovalDecision` when their matching verdict arrives.
44
- *
45
- * The verdict queue is a single-consumer transport: run one approval round
46
- * at a time (the router retires once every gated call of the round is
47
- * resolved). For overlapping rounds, use one queue per round.
48
- */
49
- const fromQueue = (predicate, verdicts, options) => (calls) => Effect.gen(function* () {
50
- const gated = calls.filter(predicate);
51
- const approved = calls.filter((call) => !predicate(call));
52
- const entries = yield* Effect.forEach(gated, (call) => Deferred.make().pipe(Effect.map((d) => [call.call_id, d])));
53
- const deferreds = new Map(entries);
54
- const callById = new Map(gated.map((c) => [c.call_id, c]));
55
- const outstanding = yield* Ref.make(gated.length);
56
- const resolve = (call, decision) => Deferred.succeed(deferreds.get(call.call_id), decision).pipe(Effect.flatMap((won) => won ? Ref.update(outstanding, (n) => n - 1) : Effect.void));
57
- const route = Effect.suspend(() => Ref.get(outstanding).pipe(Effect.flatMap((n) => n <= 0 ? Effect.void : Queue.take(verdicts).pipe(Effect.flatMap((v) => {
58
- const call = callById.get(v.call_id);
59
- return (call === void 0 ? Effect.void : resolve(call, v.decision === "approve" ? approve(call) : reject(denied(call, v.reason)))).pipe(Effect.andThen(route));
60
- })))));
61
- if (gated.length > 0) {
62
- yield* Effect.forkScoped(route);
63
- if (options?.timeout !== void 0) yield* Effect.forkScoped(Effect.sleep(options.timeout).pipe(Effect.andThen(Effect.forEach(gated, (call) => resolve(call, reject(cancelled(call, "approval timed out")))))));
64
- }
65
- return {
66
- approved,
67
- decisions: Stream.fromIterable(gated).pipe(Stream.flatMap((call) => Stream.fromEffect(Deferred.await(deferreds.get(call.call_id))), { concurrency: "unbounded" })),
68
- approvalRequests: Stream.fromIterable(gated.map(approvalRequested))
69
- };
70
- });
71
- const fromMap = (predicate, approvals) => (calls) => splitApprovalDecisions(calls.map((call) => {
72
- if (!predicate(call)) return approve(call);
73
- const v = approvals.get(call.call_id);
74
- if (v === void 0) return reject(cancelled(call));
75
- return v.decision === "approve" ? approve(call) : reject(denied(call, v.reason));
76
- }));
77
- //#endregion
78
- export { ApprovalDecision, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions, Approval_exports as t };
79
-
80
- //# sourceMappingURL=Approval.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Approval.mjs","names":[],"sources":["../../src/tool/Approval.ts"],"sourcesContent":["/**\n * Approval helpers for the two transport flavors.\n *\n * These helpers only decide which calls are approved and which synthetic\n * results must be returned to the model. Tool execution stays explicit at\n * the recipe boundary via `Toolkit.run`.\n */\nimport { Data, Deferred, type Duration, Effect, Queue, Ref, Scope, Stream } from \"effect\"\nimport type { ToolCall } from \"../domain/Items.js\"\nimport { type ToolResult, cancelled, denied } from \"./ToolResult.js\"\nimport { ToolEvent } from \"./ToolEvent.js\"\n\nexport type ToolCallPlan = {\n readonly approved: ReadonlyArray<ToolCall>\n readonly rejected: ReadonlyArray<ToolResult>\n}\n\nexport type ApprovalDecision = Data.TaggedEnum<{\n Approved: { readonly call: ToolCall }\n Rejected: { readonly result: ToolResult }\n}>\n\nexport const ApprovalDecision = Data.taggedEnum<ApprovalDecision>()\n\nexport const approve = (call: ToolCall): ApprovalDecision => ApprovalDecision.Approved({ call })\n\nexport const reject = (result: ToolResult): ApprovalDecision =>\n ApprovalDecision.Rejected({ result })\n\nexport const splitApprovalDecisions = (decisions: ReadonlyArray<ApprovalDecision>): ToolCallPlan =>\n decisions.reduce<ToolCallPlan>(\n (acc, decision) =>\n decision._tag === \"Approved\"\n ? { ...acc, approved: [...acc.approved, decision.call] }\n : { ...acc, rejected: [...acc.rejected, decision.result] },\n { approved: [], rejected: [] },\n )\n\nexport const approvalRequested = (call: ToolCall): ToolEvent =>\n ToolEvent.ApprovalRequested({\n call_id: call.call_id,\n tool: call.name,\n arguments: call.arguments,\n })\n\n// ---------------------------------------------------------------------------\n// Verdict queue (WebSocket-style transport).\n// ---------------------------------------------------------------------------\n\nexport type Verdict = {\n readonly call_id: string\n readonly decision: \"approve\" | \"deny\"\n readonly reason?: string\n}\n\nexport type FromQueueOptions = {\n /**\n * Resolve any still-pending gated call as `cancelled` once this elapses,\n * so a lost or never-sent verdict cannot hang the decisions stream\n * forever. Omit to wait indefinitely.\n */\n readonly timeout?: Duration.Input\n}\n\n/**\n * Queue-backed approval planner. Safe calls are returned immediately in\n * `approved`; gated calls emit `ApprovalRequested` events and later produce\n * one `ApprovalDecision` when their matching verdict arrives.\n *\n * The verdict queue is a single-consumer transport: run one approval round\n * at a time (the router retires once every gated call of the round is\n * resolved). For overlapping rounds, use one queue per round.\n */\nexport const fromQueue =\n (\n predicate: (call: ToolCall) => boolean,\n verdicts: Queue.Dequeue<Verdict>,\n options?: FromQueueOptions,\n ) =>\n (\n calls: ReadonlyArray<ToolCall>,\n ): Effect.Effect<\n {\n readonly approved: ReadonlyArray<ToolCall>\n readonly decisions: Stream.Stream<ApprovalDecision>\n readonly approvalRequests: Stream.Stream<ToolEvent>\n },\n never,\n Scope.Scope\n > =>\n Effect.gen(function* () {\n const gated = calls.filter(predicate)\n const approved = calls.filter((call) => !predicate(call))\n\n const entries = yield* Effect.forEach(gated, (call) =>\n Deferred.make<ApprovalDecision>().pipe(Effect.map((d) => [call.call_id, d] as const)),\n )\n const deferreds: ReadonlyMap<string, Deferred.Deferred<ApprovalDecision>> = new Map(entries)\n const callById: ReadonlyMap<string, ToolCall> = new Map(gated.map((c) => [c.call_id, c]))\n const outstanding = yield* Ref.make(gated.length)\n\n // Complete a deferred once; decrement only on the winning transition so\n // a late verdict for an already-resolved call is a no-op.\n const resolve = (call: ToolCall, decision: ApprovalDecision) =>\n Deferred.succeed(deferreds.get(call.call_id)!, decision).pipe(\n Effect.flatMap((won) => (won ? Ref.update(outstanding, (n) => n - 1) : Effect.void)),\n )\n\n // Router retires once every gated call is resolved (by verdict or\n // timeout), so sequential rounds over one queue do not accumulate\n // racing routers. Recursive + suspended: stack-safe, no imperative loop.\n const route: Effect.Effect<void> = Effect.suspend(() =>\n Ref.get(outstanding).pipe(\n Effect.flatMap((n) =>\n n <= 0\n ? Effect.void\n : Queue.take(verdicts).pipe(\n Effect.flatMap((v) => {\n const call = callById.get(v.call_id)\n return (\n call === undefined\n ? Effect.void\n : resolve(\n call,\n v.decision === \"approve\"\n ? approve(call)\n : reject(denied(call, v.reason)),\n )\n ).pipe(Effect.andThen(route))\n }),\n ),\n ),\n ),\n )\n\n if (gated.length > 0) {\n yield* Effect.forkScoped(route)\n if (options?.timeout !== undefined) {\n yield* Effect.forkScoped(\n Effect.sleep(options.timeout).pipe(\n Effect.andThen(\n Effect.forEach(gated, (call) =>\n resolve(call, reject(cancelled(call, \"approval timed out\"))),\n ),\n ),\n ),\n )\n }\n }\n\n const decisions = Stream.fromIterable(gated).pipe(\n Stream.flatMap((call) => Stream.fromEffect(Deferred.await(deferreds.get(call.call_id)!)), {\n concurrency: \"unbounded\",\n }),\n )\n\n const approvalRequests = Stream.fromIterable<ToolEvent>(gated.map(approvalRequested))\n\n return { approved, decisions, approvalRequests }\n })\n\n// ---------------------------------------------------------------------------\n// Approval map (HTTP-style transport). Verdicts arrive synchronously\n// bundled in the request payload. Missing entries → cancelled.\n// ---------------------------------------------------------------------------\n\nexport type ApprovalMapEntry =\n | { readonly decision: \"approve\" }\n | { readonly decision: \"deny\"; readonly reason?: string }\n\nexport const fromMap =\n (predicate: (call: ToolCall) => boolean, approvals: ReadonlyMap<string, ApprovalMapEntry>) =>\n (calls: ReadonlyArray<ToolCall>): ToolCallPlan =>\n splitApprovalDecisions(\n calls.map((call) => {\n if (!predicate(call)) return approve(call)\n const v = approvals.get(call.call_id)\n if (v === undefined) return reject(cancelled(call))\n return v.decision === \"approve\" ? approve(call) : reject(denied(call, v.reason))\n }),\n )\n"],"mappings":"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"}
@@ -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 { m as it, o as describe } from "../utils-CiH_DjFW.mjs";
9
- import { n as globalExpect } from "../test.DNmyFkvJ-YPzDR0Co.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":";;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,YAAYA,KAAS;CACzB,MAAM;CACN,aAAa;CACb,aAAa,iBAAiB,OAAO,OAAO,EAAE,OAAO,OAAO,OAAO,CAAC,CAAC;CACrE,MAAM,EAAE,SAAS,SACf,OAAO,IAAI,aAAa;EACtB,MAAM,OAAO;GACX;IAAE,KAAK;IAAK,OAAO,GAAG,MAAM;GAAI;GAChC;IAAE,KAAK;IAAK,OAAO,GAAG,MAAM;GAAI;GAChC;IAAE,KAAK;IAAK,OAAO,GAAG,MAAM;GAAI;EAClC;EACA,OAAO,OAAO,WAAW,OAAO,aAAa,IAAI,GAAG,IAAI;EACxD,OAAO,EAAE,OAAO,KAAK,OAAO;CAC9B,CAAC;AACL,CAAC;AAED,MAAM,YAAYA,KAAS;CACzB,MAAM;CACN,aAAa;CACb,aAAa,iBACX,OAAO,OAAO;EAAE,YAAY,OAAO,MAAM,OAAO,MAAM;EAAG,SAAS,OAAO;CAAO,CAAC,CACnF;CACA,MAAM,EAAE,cAAc,SACpB,OAAO,IAAI,aAAa;EACtB,OAAO,OAAO,QAAQ,aAAa,GAAG,MACpC,KAAK;GAAE,MAAM;GAAqB,MAAM,IAAI;GAAG,OAAO,WAAW;EAAO,CAAC,CAC3E;EACA,OAAO;GAAE,QAAQ;GAAiB,WAAW,WAAW;EAAO;CACjE,CAAC;AACL,CAAC;AAED,MAAM,iBAAiBA,KAAS;CAC9B,MAAM;CACN,aAAa;CACb,aAAa,iBAAiB,OAAO,OAAO,EAAE,MAAM,OAAO,OAAO,CAAC,CAAC;CACpE,MAAM,EAAE,WAAW,OAAO,QAAQ;EAAE,QAAQ;EAAW;CAAK,CAAC;AAC/D,CAAC;AAED,MAAM,WAAWC,OAAY,WAAW,WAAW,cAAc;AACjE,MAAM,4BAAY,IAAI,IAAI,CAAC,cAAc,iBAAiB,CAAC;AAC3D,MAAM,eAAe,SAAyB,UAAU,IAAI,KAAK,IAAI;AAErE,MAAM,MAAM,SAAiB,MAAc,UAAmC;CAC5E,MAAM;CACN;CACA;CACA,WAAW,KAAK,UAAU,IAAI;AAChC;AAEA,MAAM,QAAQ;CACZ,GAAG,MAAM,cAAc,EAAE,OAAO,SAAS,CAAC;CAC1C,GAAG,MAAM,cAAc;EAAE,YAAY,CAAC,OAAO,KAAK;EAAG,SAAS;CAAK,CAAC;CACpE,GAAG,MAAM,mBAAmB,EAAE,MAAM,OAAO,CAAC;AAC9C;AAEA,MAAM,eAAe,cACnB,UAAU,OAAO,QAAQ,CAAC,CAAC,KAAK,MAAM,EAAE,MAAM;AAEhD,MAAM,YAAY,YAAuC,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AAEnG,MAAM,kBAAkB,aACtB,OAAO,aAAa,SAAS,KAAK,WAAW,UAAU,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAE5E,MAAM,yBAAyB,cAAqD;CAClF,MAAM,OAAO,QAAQ,aAAa,SAAS,CAAC,CAAC,KAAK;CAClD,OAAO,OAAO,MAAM,IAAI,UAAU,KAAK,QAAQ,GAAG,eAAe,KAAK,QAAQ,CAAC;AACjF;AAEA,MAAM,sBAAsB,aAC1B,SAAS,SAAS,aACd,IAAI,UAAU,CAAC,SAAS,IAAI,CAAC,IAC7B,OAAO,QAAQ,UAAU,OAAO,EAAE,QAAQ,SAAS,OAAO,CAAC,CAAC;AAMlE,SAAS,uBAAuB;CAC9B,GAAG,mEAAmE,YAAY;EAChF,MAAM,4BAAY,IAAI,IAA8B,CAClD,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,GAC9B,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,CAChC,CAAC;EACD,MAAM,YAAY,MAAM,OAAO,WAAW,OAAO,WAAW,sBAAsB,SAAS,CAAC,CAAC;EAC7F,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,OAAO,EAAE;EAAE,CAAC;EACtE,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,OAAO;IAAE,QAAQ;IAAQ,WAAW;GAAE;EACxC,CAAC;EACD,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,OAAO;IAAE,QAAQ;IAAW,MAAM;GAAO;EAC3C,CAAC;EAGD,aAAO,UAAU,OAAO,mBAAmB,CAAC,CAAC,CAAC,aAAa,CAAC;CAC9D,CAAC;CAED,GAAG,wDAAwD,YAAY;EACrE,MAAM,4BAAY,IAAI,IAA8B,CAClD,CAAC,MAAM;GAAE,UAAU;GAAQ,QAAQ;EAAe,CAAC,GACnD,CAAC,MAAM;GAAE,UAAU;GAAQ,QAAQ;EAAiB,CAAC,CACvD,CAAC;EACD,MAAM,YAAY,MAAM,OAAO,WAAW,OAAO,WAAW,sBAAsB,SAAS,CAAC,CAAC;EAG7F,aAAO,UAAU,OAAO,UAAU,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;EAE1F,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,MAAM;GACN,QAAQ;EACV,CAAC;EACD,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,MAAM;GACN,QAAQ;EACV,CAAC;CACH,CAAC;CAED,GAAG,uDAAuD,YAAY;EACpE,MAAM,YAAY,MAAM,OAAO,WAAW,OAAO,WAAW,sCAAsB,IAAI,IAAI,CAAC,CAAC,CAAC;EAC7F,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,OAAO,EAAE;EAAE,CAAC;EACtE,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAW,MAAM;EAAY,CAAC;EACzE,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAW,MAAM;EAAY,CAAC;CAC3E,CAAC;CAED,GAAG,kDAAkD,YAAY;EAC/D,MAAM,4BAAY,IAAI,IAA8B,CAClD,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,CAEhC,CAAC;EACD,MAAM,YAAY,MAAM,OAAO,WAAW,OAAO,WAAW,sBAAsB,SAAS,CAAC,CAAC;EAC7F,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,OAAO,EAAE;EAAE,CAAC;EACtE,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,QAAQ,OAAO;EAAE,CAAC;EAC5E,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAW,MAAM;EAAY,CAAC;CAC3E,CAAC;AACH,CAAC;AAMD,SAAS,mCAAmC;CAC1C,GAAG,wEAAwE,YAAY;EACrF,MAAM,iBAAiB;GACrB,GAAG,MAAM,cAAc,EAAE,OAAO,IAAI,CAAC;GACrC,GAAG,MAAM,kBAAkB,CAAC,CAAC;GAC7B,GAAG,MAAM,mBAAmB,EAAE,MAAM,OAAO,CAAC;EAC9C;EACA,MAAM,YAAY,MAAM,OAAO,WAC7B,OAAO,kBACE;GACL,MAAM,OAAO,QACX,6BACA,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,UAAU,CAAC,CAAC,CAAC,CAC3C,CAAC,CAAC,cAAc;GAChB,OAAO,OAAO,MAAM,IAAI,UAAU,KAAK,QAAQ,GAAG,eAAe,KAAK,QAAQ,CAAC;EACjF,EAAA,CAAG,CACL,CACF;EACA,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc,EAAE,MAAM,KAAK,CAAC;EACjD,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAW,MAAM;EAAe,CAAC;EAC5E,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,QAAQ,UAAU;EAAE,CAAC;CACjF,CAAC;AACH,CAAC;AAMD,SAAS,yBAAyB;CAChC,GAAG,gFAAgF,YAAY;EAC7F,MAAM,YAAY,MAAM,OAAO,WAC7B,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,OAAO,MAAM,UAI3B;GACH,OAAO,MAAM,MAAM,UAAU;IAAE,SAAS;IAAM,UAAU;GAAU,CAAC;GACnE,OAAO,MAAM,MAAM,UAAU;IAC3B,SAAS;IACT,UAAU;IACV,QAAQ;GACV,CAAC;GAGD,MAAM,SAAS,OAAO,OACpB,OAAO,IAAI,aAAa;IACtB,MAAM,EAAE,UAAU,WAAW,qBAAqB,OAAO,UACvD,aACA,QACF,CAAC,CAAC,KAAK;IACP,OAAO,OAAO,MACZ,kBACA,OAAO,MACL,IAAI,UAAU,QAAQ,GACtB,UAAU,KAAK,OAAO,QAAQ,kBAAkB,CAAC,CACnD,CACF;GACF,CAAC,CACH;GACA,OAAO,OAAO,OAAO,WAAW,MAAM;EACxC,CAAC,CACH;EAEA,aAAO,UAAU,OAAO,mBAAmB,CAAC,CAAC,CAAC,aAAa,CAAC;EAE5D,MAAM,KAAK,SAAS,YAAY,SAAS,CAAC;EAC1C,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GAAE,MAAM;GAAM,OAAO,EAAE,QAAQ,OAAO;EAAE,CAAC;EAC5E,aAAO,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,MAAM;GACN,QAAQ;EACV,CAAC;CACH,CAAC;CAED,GAAG,8EAA8E,YAAY;EAI3F,MAAM,YAAY,MAAM,OAAO,WAC7B,OAAO,OACL,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,OAAO,MAAM,UAAmB;GACjD,MAAM,SAAS,OAAe,CAAC,GAAG,IAAI,mBAAmB,EAAE,MAAM,OAAO,CAAC,CAAC;GAE1E,MAAM,KAAK,OAAO,UAAU,aAAa,QAAQ,CAAC,CAAC,MAAM,IAAI,CAAC;GAC9D,OAAO,MAAM,MAAM,UAAU;IAAE,SAAS;IAAM,UAAU;GAAU,CAAC;GACnE,MAAM,KAAK,MAAM,KAAK,OAAO,OAAO,WAAW,GAAG,SAAS,CAAC;GAE5D,MAAM,KAAK,OAAO,UAAU,aAAa,QAAQ,CAAC,CAAC,MAAM,IAAI,CAAC;GAC9D,OAAO,MAAM,MAAM,UAAU;IAAE,SAAS;IAAM,UAAU;IAAQ,QAAQ;GAAK,CAAC;GAG9E,OAAO;IAAE;IAAI,IAFF,MAAM,KAAK,OAAO,OAAO,WAAW,GAAG,SAAS,CAE7C;GAAE;EAClB,CAAC,CACH,CACF;EACA,aAAO,UAAU,GAAG,EAAE,CAAC,CAAC,cAAc,EAAE,MAAM,WAAW,CAAC;EAC1D,aAAO,UAAU,GAAG,EAAE,CAAC,CAAC,cAAc;GAAE,MAAM;GAAY,QAAQ,EAAE,MAAM,SAAS;EAAE,CAAC;CACxF,CAAC;CAED,GAAG,0DAA0D,YAAY;EACvE,MAAM,YAAY,MAAM,OAAO,WAC7B,OAAO,OACL,OAAO,IAAI,aAAa;GACtB,MAAM,WAAW,OAAO,MAAM,UAAmB;GACjD,MAAM,EAAE,cAAc,OAAO,UAAU,aAAa,UAAU,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,CACtF,GAAG,MAAM,mBAAmB,EAAE,MAAM,OAAO,CAAC,CAC9C,CAAC;GACD,OAAO,MAAM,KAAK,OAAO,OAAO,WAAW,SAAS,CAAC;EACvD,CAAC,CACH,CACF;EACA,aAAO,UAAU,EAAE,CAAC,CAAC,cAAc;GACjC,MAAM;GACN,QAAQ;IAAE,MAAM;IAAW,MAAM;GAAY;EAC/C,CAAC;CACH,CAAC;AACH,CAAC;AAMD,SAAS,+DAA+D;CACtE,MAAM,SAAS,GAAG,OAAO,mBAAmB,EAAE,MAAM,OAAO,CAAC;CAC5D,MAAM,WAAW,GAAG,OAAO,cAAc,EAAE,OAAO,IAAI,CAAC;CACvD,MAAM,iBAAiBC,eAAqB,OAAO,KAAK,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;CAE/E,GAAG,kDAAkD;EACnD,MAAM,UAAU;GAACC,SAAe,IAAI;GAAG;GAAU;GAAQ;EAAc;EACvE,MAAM,aAAa,oBAAoB,OAAO;EAC9C,aAAO,UAAU,CAAC,CAAC,aAAa,CAAC;EACjC,aAAO,WAAW,EAAE,CAAE,OAAO,CAAC,CAAC,KAAK,KAAK;CAC3C,CAAC;CAED,GAAG,kEAAkE;EACnE,MAAM,QAAQ,CAACA,SAAe,IAAI,GAAG,MAAM;EAC3C,aAAO,aAAa,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK;EACtC,MAAM,aAAa,CAAC,GAAG,OAAO,GAAG,iBAAiB,KAAK,CAAC,CAAC,IAAI,gBAAgB,CAAC;EAC9E,aAAO,aAAa,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI;CAC5C,CAAC;CAED,GAAG,wEAAwE;EACzE,MAAM,UAAU;GAACA,SAAe,IAAI;GAAG;GAAU;GAAQ;EAAc;EACvE,MAAM,WAAW,iBAAiB,SAAS,eAAe;EAC1D,aAAO,QAAQ,CAAC,CAAC,aAAa,CAAC;EAC/B,MAAM,IAAI,SAAS;EACnB,aAAO,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;EAC9B,aAAO,CAAC,CAAC,CAAC,cAAc;GACtB,MAAM;GACN,SAAS;GACT,MAAM;GACN,QAAQ;EACV,CAAC;CACH,CAAC;CAED,GAAG,qFAAqF;EACtF,MAAM,QAAQ,CAACA,SAAe,eAAe,GAAG,MAAM;EACtD,MAAM,WAAW,iBAAiB,OAAO,iBAAiB;EAC1D,MAAM,aAAa;GAAC,GAAG;GAAO,GAAG,SAAS,IAAI,gBAAgB;GAAGA,SAAe,YAAY;EAAC;EAC7F,aAAO,oBAAoB,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC;CACxD,CAAC;AACH,CAAC;AAMD,SAAS,0BAA0B;CACjC,GAAG,oCAAoC;EAOrC,MAAM,MAAM,iBAAiB;GAL3B,MAAM;GACN,SAAS;GACT,MAAM;GACN,OAAO,EAAE,OAAO,EAAE;EAES,CAAC;EAC9B,aAAO,IAAI,OAAO,CAAC,CAAC,KAAK,IAAI;EAC7B,aAAO,KAAK,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrD,CAAC;CAED,GAAG,gEAAgE;EAQjE,MAAM,MAAM,iBAAiB;GAN3B,MAAM;GACN,SAAS;GACT,MAAM;GACN,MAAM;GACN,QAAQ;EAEmB,CAAC;EAC9B,aAAO,KAAK,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO;GAAE,MAAM;GAAU,SAAS;EAAe,EAAE,CAAC;CAC/F,CAAC;CAED,GAAG,0DAA0D;EAO3D,MAAM,MAAM,iBAAiB;GAL3B,MAAM;GACN,SAAS;GACT,MAAM;GACN,MAAM;EAEqB,CAAC;EAC9B,aAAO,KAAK,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,EAAE,CAAC;CACzE,CAAC;AACH,CAAC;AAMD,SAAS,aAAa;CACpB,GAAG,+BAA+B,YAAY;EAC5C,MAAM,YAAY,MAAM,OAAO,WAAW,OAAO,WAAW,IAAI,UAAU,KAAK,CAAC,CAAC;EACjF,aAAO,UAAU,OAAO,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC;EACjD,aAAO,UAAU,OAAO,QAAQ,CAAC,CAAC,OAAO,MAAM,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;CAC3E,CAAC;AACH,CAAC"}
@@ -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 };