@effect-uai/core 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/dist/{Items-BH8xUkoR.d.mts → Items-pXR5BAgW.d.mts} +12 -12
  3. package/dist/Items-pXR5BAgW.d.mts.map +1 -0
  4. package/dist/SandboxError-BvKypkJm.d.mts +158 -0
  5. package/dist/SandboxError-BvKypkJm.d.mts.map +1 -0
  6. package/dist/{Tool-87ViKCCO.d.mts → Tool-DP-AwKOf.d.mts} +11 -11
  7. package/dist/{Tool-87ViKCCO.d.mts.map → Tool-DP-AwKOf.d.mts.map} +1 -1
  8. package/dist/domain/Items.d.mts +2 -2
  9. package/dist/domain/Items.mjs +15 -15
  10. package/dist/domain/Items.mjs.map +1 -1
  11. package/dist/domain/Turn.d.mts +388 -2
  12. package/dist/domain/Turn.d.mts.map +1 -0
  13. package/dist/domain/Turn.mjs +13 -13
  14. package/dist/domain/Turn.mjs.map +1 -1
  15. package/dist/index.d.mts +11 -6
  16. package/dist/index.mjs +8 -3
  17. package/dist/language-model/LanguageModel.d.mts +5 -5
  18. package/dist/language-model/LanguageModel.d.mts.map +1 -1
  19. package/dist/language-model/LanguageModel.mjs.map +1 -1
  20. package/dist/loop/Loop.d.mts +37 -90
  21. package/dist/loop/Loop.d.mts.map +1 -1
  22. package/dist/loop/Loop.mjs +39 -93
  23. package/dist/loop/Loop.mjs.map +1 -1
  24. package/dist/loop/Loop.test.mjs +46 -54
  25. package/dist/loop/Loop.test.mjs.map +1 -1
  26. package/dist/sandbox/Memory.d.mts +53 -0
  27. package/dist/sandbox/Memory.d.mts.map +1 -0
  28. package/dist/sandbox/Memory.mjs +68 -0
  29. package/dist/sandbox/Memory.mjs.map +1 -0
  30. package/dist/sandbox/Sandbox.d.mts +717 -0
  31. package/dist/sandbox/Sandbox.d.mts.map +1 -0
  32. package/dist/sandbox/Sandbox.mjs +192 -0
  33. package/dist/sandbox/Sandbox.mjs.map +1 -0
  34. package/dist/sandbox/SandboxError.d.mts +2 -0
  35. package/dist/sandbox/SandboxError.mjs +89 -0
  36. package/dist/sandbox/SandboxError.mjs.map +1 -0
  37. package/dist/sandbox/SandboxImage.d.mts +28 -0
  38. package/dist/sandbox/SandboxImage.d.mts.map +1 -0
  39. package/dist/sandbox/SandboxImage.mjs +32 -0
  40. package/dist/sandbox/SandboxImage.mjs.map +1 -0
  41. package/dist/sandbox/SandboxNetwork.d.mts +34 -0
  42. package/dist/sandbox/SandboxNetwork.d.mts.map +1 -0
  43. package/dist/sandbox/SandboxNetwork.mjs +36 -0
  44. package/dist/sandbox/SandboxNetwork.mjs.map +1 -0
  45. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +98 -2
  46. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
  47. package/dist/speech-synthesizer/SpeechSynthesizer.mjs +32 -2
  48. package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
  49. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +56 -2
  50. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
  51. package/dist/testing/MockProvider.d.mts +3 -3
  52. package/dist/testing/MockProvider.d.mts.map +1 -1
  53. package/dist/testing/MockProvider.mjs.map +1 -1
  54. package/dist/testing/MockSandbox.d.mts +604 -0
  55. package/dist/testing/MockSandbox.d.mts.map +1 -0
  56. package/dist/testing/MockSandbox.mjs +160 -0
  57. package/dist/testing/MockSandbox.mjs.map +1 -0
  58. package/dist/testing/MockSpeechSynthesizer.d.mts +26 -11
  59. package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -1
  60. package/dist/testing/MockSpeechSynthesizer.mjs +75 -51
  61. package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -1
  62. package/dist/tool/{Resolvers.d.mts → Approval.d.mts} +27 -27
  63. package/dist/tool/Approval.d.mts.map +1 -0
  64. package/dist/tool/{Resolvers.mjs → Approval.mjs} +18 -18
  65. package/dist/tool/Approval.mjs.map +1 -0
  66. package/dist/tool/{Resolvers.test.mjs → Approval.test.mjs} +40 -40
  67. package/dist/tool/Approval.test.mjs.map +1 -0
  68. package/dist/tool/HistoryCheck.d.mts +6 -6
  69. package/dist/tool/HistoryCheck.d.mts.map +1 -1
  70. package/dist/tool/HistoryCheck.mjs +5 -5
  71. package/dist/tool/HistoryCheck.mjs.map +1 -1
  72. package/dist/tool/Tool.d.mts +2 -2
  73. package/dist/tool/Tool.mjs +4 -4
  74. package/dist/tool/Tool.mjs.map +1 -1
  75. package/dist/tool/ToolEvent.d.mts +19 -19
  76. package/dist/tool/ToolEvent.d.mts.map +1 -1
  77. package/dist/tool/ToolEvent.mjs +8 -8
  78. package/dist/tool/ToolEvent.mjs.map +1 -1
  79. package/dist/tool/{Outcome.d.mts → ToolResult.d.mts} +29 -29
  80. package/dist/tool/ToolResult.d.mts.map +1 -0
  81. package/dist/tool/ToolResult.mjs +61 -0
  82. package/dist/tool/ToolResult.mjs.map +1 -0
  83. package/dist/tool/Toolkit.d.mts +58 -26
  84. package/dist/tool/Toolkit.d.mts.map +1 -1
  85. package/dist/tool/Toolkit.mjs +62 -34
  86. package/dist/tool/Toolkit.mjs.map +1 -1
  87. package/dist/tool/Toolkit.test.mjs +13 -13
  88. package/dist/tool/Toolkit.test.mjs.map +1 -1
  89. package/package.json +31 -7
  90. package/src/domain/Items.ts +9 -9
  91. package/src/domain/Turn.ts +16 -16
  92. package/src/index.ts +7 -2
  93. package/src/language-model/LanguageModel.ts +3 -3
  94. package/src/loop/Loop.test.ts +93 -91
  95. package/src/loop/Loop.ts +72 -170
  96. package/src/sandbox/Memory.ts +128 -0
  97. package/src/sandbox/Sandbox.ts +639 -0
  98. package/src/sandbox/SandboxError.ts +140 -0
  99. package/src/sandbox/SandboxImage.ts +26 -0
  100. package/src/sandbox/SandboxNetwork.ts +34 -0
  101. package/src/speech-synthesizer/SpeechSynthesizer.test.ts +89 -0
  102. package/src/speech-synthesizer/SpeechSynthesizer.ts +124 -0
  103. package/src/testing/MockProvider.ts +21 -20
  104. package/src/testing/MockSandbox.ts +342 -0
  105. package/src/testing/MockSpeechSynthesizer.ts +137 -77
  106. package/src/tool/{Resolvers.test.ts → Approval.test.ts} +45 -54
  107. package/src/tool/{Resolvers.ts → Approval.ts} +25 -25
  108. package/src/tool/HistoryCheck.ts +10 -13
  109. package/src/tool/Tool.ts +13 -15
  110. package/src/tool/ToolEvent.ts +7 -7
  111. package/src/tool/{Outcome.ts → ToolResult.ts} +20 -20
  112. package/src/tool/Toolkit.test.ts +13 -13
  113. package/src/tool/Toolkit.ts +97 -73
  114. package/dist/Items-BH8xUkoR.d.mts.map +0 -1
  115. package/dist/Turn-0CwCAyVe.d.mts +0 -388
  116. package/dist/Turn-0CwCAyVe.d.mts.map +0 -1
  117. package/dist/tool/Outcome.d.mts.map +0 -1
  118. package/dist/tool/Outcome.mjs +0 -61
  119. package/dist/tool/Outcome.mjs.map +0 -1
  120. package/dist/tool/Resolvers.d.mts.map +0 -1
  121. package/dist/tool/Resolvers.mjs.map +0 -1
  122. package/dist/tool/Resolvers.test.mjs.map +0 -1
  123. /package/dist/tool/{Resolvers.test.d.mts → Approval.test.d.mts} +0 -0
@@ -1,28 +1,28 @@
1
1
  import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
2
- import { cancelled, denied } from "./Outcome.mjs";
2
+ import { cancelled, denied } from "./ToolResult.mjs";
3
3
  import { ToolEvent } from "./ToolEvent.mjs";
4
4
  import { Data, Deferred, Effect, Queue, Stream } from "effect";
5
- //#region src/tool/Resolvers.ts
5
+ //#region src/tool/Approval.ts
6
6
  /**
7
7
  * Approval helpers for the two transport flavors.
8
8
  *
9
9
  * These helpers only decide which calls are approved and which synthetic
10
10
  * results must be returned to the model. Tool execution stays explicit at
11
- * the recipe boundary via `Toolkit.executeAll`.
11
+ * the recipe boundary via `Toolkit.run`.
12
12
  */
13
- var Resolvers_exports = /* @__PURE__ */ __exportAll({
14
- ToolCallDecision: () => ToolCallDecision,
13
+ var Approval_exports = /* @__PURE__ */ __exportAll({
14
+ ApprovalDecision: () => ApprovalDecision,
15
15
  approvalRequested: () => approvalRequested,
16
16
  approve: () => approve,
17
- fromApprovalMap: () => fromApprovalMap,
18
- fromVerdictQueue: () => fromVerdictQueue,
17
+ fromMap: () => fromMap,
18
+ fromQueue: () => fromQueue,
19
19
  reject: () => reject,
20
- splitToolCallDecisions: () => splitToolCallDecisions
20
+ splitApprovalDecisions: () => splitApprovalDecisions
21
21
  });
22
- const ToolCallDecision = Data.taggedEnum();
23
- const approve = (call) => ToolCallDecision.Approved({ call });
24
- const reject = (result) => ToolCallDecision.Rejected({ result });
25
- const splitToolCallDecisions = (decisions) => decisions.reduce((acc, decision) => decision._tag === "Approved" ? {
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
26
  ...acc,
27
27
  approved: [...acc.approved, decision.call]
28
28
  } : {
@@ -40,9 +40,9 @@ const approvalRequested = (call) => ToolEvent.ApprovalRequested({
40
40
  /**
41
41
  * Queue-backed approval planner. Safe calls are returned immediately in
42
42
  * `approved`; gated calls emit `ApprovalRequested` events and later produce
43
- * one `ToolCallDecision` when their matching verdict arrives.
43
+ * one `ApprovalDecision` when their matching verdict arrives.
44
44
  */
45
- const fromVerdictQueue = (predicate, verdicts) => (calls) => Effect.gen(function* () {
45
+ const fromQueue = (predicate, verdicts) => (calls) => Effect.gen(function* () {
46
46
  const gated = calls.filter(predicate);
47
47
  const approved = calls.filter((call) => !predicate(call));
48
48
  const entries = yield* Effect.forEach(gated, (call) => Deferred.make().pipe(Effect.map((d) => [call.call_id, d])));
@@ -58,16 +58,16 @@ const fromVerdictQueue = (predicate, verdicts) => (calls) => Effect.gen(function
58
58
  const d = deferreds.get(call.call_id);
59
59
  return Stream.fromEffect(Deferred.await(d).pipe(Effect.map((v) => v.decision === "approve" ? approve(call) : reject(denied(call, v.reason)))));
60
60
  }, { concurrency: "unbounded" })),
61
- announce: Stream.fromIterable(gated.map(approvalRequested))
61
+ approvalRequests: Stream.fromIterable(gated.map(approvalRequested))
62
62
  };
63
63
  });
64
- const fromApprovalMap = (predicate, approvals) => (calls) => splitToolCallDecisions(calls.map((call) => {
64
+ const fromMap = (predicate, approvals) => (calls) => splitApprovalDecisions(calls.map((call) => {
65
65
  if (!predicate(call)) return approve(call);
66
66
  const v = approvals.get(call.call_id);
67
67
  if (v === void 0) return reject(cancelled(call));
68
68
  return v.decision === "approve" ? approve(call) : reject(denied(call, v.reason));
69
69
  }));
70
70
  //#endregion
71
- export { ToolCallDecision, approvalRequested, approve, fromApprovalMap, fromVerdictQueue, reject, splitToolCallDecisions, Resolvers_exports as t };
71
+ export { ApprovalDecision, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions, Approval_exports as t };
72
72
 
73
- //# sourceMappingURL=Resolvers.mjs.map
73
+ //# sourceMappingURL=Approval.mjs.map
@@ -0,0 +1 @@
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, Effect, Queue, 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\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 */\nexport const fromQueue =\n (predicate: (call: ToolCall) => boolean, verdicts: Queue.Dequeue<Verdict>) =>\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<Verdict>().pipe(Effect.map((d) => [call.call_id, d] as const)),\n )\n const deferreds: ReadonlyMap<string, Deferred.Deferred<Verdict>> = new Map(entries)\n\n // Router is forked into the surrounding Scope so it lives as long\n // as the consumer is pulling events. Recipes typically supply the\n // scope by wrapping the events construction in `Stream.unwrap`.\n yield* Effect.forkScoped(\n Effect.forever(\n Effect.gen(function* () {\n const v = yield* Queue.take(verdicts)\n const d = deferreds.get(v.call_id)\n if (d !== undefined) yield* Deferred.succeed(d, v)\n }),\n ),\n )\n\n const decisions = Stream.fromIterable(gated).pipe(\n Stream.flatMap(\n (call) => {\n const d = deferreds.get(call.call_id)!\n return Stream.fromEffect(\n Deferred.await(d).pipe(\n Effect.map((v) =>\n v.decision === \"approve\" ? approve(call) : reject(denied(call, v.reason)),\n ),\n ),\n )\n },\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":";;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,mBAAmB,KAAK,YAA8B;AAEnE,MAAa,WAAW,SAAqC,iBAAiB,SAAS,EAAE,MAAM,CAAC;AAEhG,MAAa,UAAU,WACrB,iBAAiB,SAAS,EAAE,QAAQ,CAAC;AAEvC,MAAa,0BAA0B,cACrC,UAAU,QACP,KAAK,aACJ,SAAS,SAAS,aACd;CAAE,GAAG;CAAK,UAAU,CAAC,GAAG,IAAI,UAAU,SAAS,KAAK;CAAE,GACtD;CAAE,GAAG;CAAK,UAAU,CAAC,GAAG,IAAI,UAAU,SAAS,OAAO;CAAE,EAC9D;CAAE,UAAU,EAAE;CAAE,UAAU,EAAE;CAAE,CAC/B;AAEH,MAAa,qBAAqB,SAChC,UAAU,kBAAkB;CAC1B,SAAS,KAAK;CACd,MAAM,KAAK;CACX,WAAW,KAAK;CACjB,CAAC;;;;;;AAiBJ,MAAa,aACV,WAAwC,cAEvC,UAUA,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,MAAM,OAAO,UAAU;CACrC,MAAM,WAAW,MAAM,QAAQ,SAAS,CAAC,UAAU,KAAK,CAAC;CAEzD,MAAM,UAAU,OAAO,OAAO,QAAQ,QAAQ,SAC5C,SAAS,MAAe,CAAC,KAAK,OAAO,KAAK,MAAM,CAAC,KAAK,SAAS,EAAE,CAAU,CAAC,CAC7E;CACD,MAAM,YAA6D,IAAI,IAAI,QAAQ;AAKnF,QAAO,OAAO,WACZ,OAAO,QACL,OAAO,IAAI,aAAa;EACtB,MAAM,IAAI,OAAO,MAAM,KAAK,SAAS;EACrC,MAAM,IAAI,UAAU,IAAI,EAAE,QAAQ;AAClC,MAAI,MAAM,KAAA,EAAW,QAAO,SAAS,QAAQ,GAAG,EAAE;GAClD,CACH,CACF;AAoBD,QAAO;EAAE;EAAU,WAlBD,OAAO,aAAa,MAAM,CAAC,KAC3C,OAAO,SACJ,SAAS;GACR,MAAM,IAAI,UAAU,IAAI,KAAK,QAAQ;AACrC,UAAO,OAAO,WACZ,SAAS,MAAM,EAAE,CAAC,KAChB,OAAO,KAAK,MACV,EAAE,aAAa,YAAY,QAAQ,KAAK,GAAG,OAAO,OAAO,MAAM,EAAE,OAAO,CAAC,CAC1E,CACF,CACF;KAEH,EAAE,aAAa,aAAa,CAC7B,CAKyB;EAAE,kBAFL,OAAO,aAAwB,MAAM,IAAI,kBAAkB,CAEtC;EAAE;EAChD;AAWN,MAAa,WACV,WAAwC,eACxC,UACC,uBACE,MAAM,KAAK,SAAS;AAClB,KAAI,CAAC,UAAU,KAAK,CAAE,QAAO,QAAQ,KAAK;CAC1C,MAAM,IAAI,UAAU,IAAI,KAAK,QAAQ;AACrC,KAAI,MAAM,KAAA,EAAW,QAAO,OAAO,UAAU,KAAK,CAAC;AACnD,QAAO,EAAE,aAAa,YAAY,QAAQ,KAAK,GAAG,OAAO,OAAO,MAAM,EAAE,OAAO,CAAC;EAChF,CACH"}
@@ -1,17 +1,17 @@
1
- import { functionCallOutput, userText } from "../domain/Items.mjs";
1
+ import { toolCallOutput, userText } from "../domain/Items.mjs";
2
2
  import { fromEffectSchema, make, streaming } from "./Tool.mjs";
3
- import { isFailure, isValue, toFunctionCallOutput } from "./Outcome.mjs";
4
- import { ToolEvent, isApprovalRequested, isIntermediate, isOutput } from "./ToolEvent.mjs";
5
- import { executeAll } from "./Toolkit.mjs";
6
- import { fromApprovalMap, fromVerdictQueue } from "./Resolvers.mjs";
3
+ import { isFailure, isOk, toToolCallOutput } from "./ToolResult.mjs";
4
+ import { ToolEvent, isApprovalRequested, isOutput, isProgress } from "./ToolEvent.mjs";
5
+ import { run } from "./Toolkit.mjs";
6
+ import { fromMap, fromQueue } from "./Approval.mjs";
7
7
  import { cancelAllPending, findUnansweredCalls, isReconciled } from "./HistoryCheck.mjs";
8
8
  import { i as it, n as globalExpect, r as describe } from "../dist-DV5ISja1.mjs";
9
9
  import { Effect, Queue, Schema, Stream } from "effect";
10
- //#region src/tool/Resolvers.test.ts
10
+ //#region src/tool/Approval.test.ts
11
11
  /**
12
12
  * Tests for approval planners + history-reconciliation primitives. Exercises
13
13
  * the full HITL + streaming-tool stack end-to-end by composing approval plans
14
- * with `executeAll`, with the four wire-shaped scenarios:
14
+ * with `run`, with the four wire-shaped scenarios:
15
15
  *
16
16
  * 1. Approval : gated calls approved → tools execute, structured Values
17
17
  * 2. Denial : gated calls denied → Failure(denied) results
@@ -88,28 +88,28 @@ const resultsFrom = (collected) => collected.filter(isOutput).map((e) => e.resul
88
88
  const byCallId = (results) => new Map(results.map((r) => [r.call_id, r]));
89
89
  const rejectedStream = (rejected) => Stream.fromIterable(rejected.map((result) => ToolEvent.Output({ result })));
90
90
  const eventsFromApprovalMap = (approvals) => {
91
- const plan = fromApprovalMap(isSensitive, approvals)(calls);
92
- return Stream.merge(executeAll(allTools, plan.approved), rejectedStream(plan.rejected));
91
+ const plan = fromMap(isSensitive, approvals)(calls);
92
+ return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected));
93
93
  };
94
- const eventsFromDecision = (decision) => decision._tag === "Approved" ? executeAll(allTools, [decision.call]) : Stream.succeed(ToolEvent.Output({ result: decision.result }));
95
- describe("fromApprovalMap + executeAll", () => {
94
+ const eventsFromDecision = (decision) => decision._tag === "Approved" ? run(allTools, [decision.call]) : Stream.succeed(ToolEvent.Output({ result: decision.result }));
95
+ describe("fromMap + run", () => {
96
96
  it("approval: all gated approved → tools execute, structured Values", async () => {
97
97
  const approvals = new Map([["c2", { decision: "approve" }], ["c3", { decision: "approve" }]]);
98
98
  const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)));
99
99
  const by = byCallId(resultsFrom(collected));
100
100
  globalExpect(by.get("c1")).toMatchObject({
101
- _tag: "Value",
101
+ _tag: "Ok",
102
102
  value: { count: 3 }
103
103
  });
104
104
  globalExpect(by.get("c2")).toMatchObject({
105
- _tag: "Value",
105
+ _tag: "Ok",
106
106
  value: {
107
107
  status: "sent",
108
108
  delivered: 2
109
109
  }
110
110
  });
111
111
  globalExpect(by.get("c3")).toMatchObject({
112
- _tag: "Value",
112
+ _tag: "Ok",
113
113
  value: {
114
114
  status: "dropped",
115
115
  name: "prod"
@@ -126,7 +126,7 @@ describe("fromApprovalMap + executeAll", () => {
126
126
  reason: "prod is sacred"
127
127
  }]]);
128
128
  const collected = await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)));
129
- globalExpect(collected.filter(isIntermediate).filter((e) => e.tool === "bulk_email")).toHaveLength(0);
129
+ globalExpect(collected.filter(isProgress).filter((e) => e.tool === "bulk_email")).toHaveLength(0);
130
130
  const by = byCallId(resultsFrom(collected));
131
131
  globalExpect(by.get("c2")).toMatchObject({
132
132
  _tag: "Failure",
@@ -142,7 +142,7 @@ describe("fromApprovalMap + executeAll", () => {
142
142
  it("cancellation: missing verdicts → Failure(cancelled)", async () => {
143
143
  const by = byCallId(resultsFrom(await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(/* @__PURE__ */ new Map())))));
144
144
  globalExpect(by.get("c1")).toMatchObject({
145
- _tag: "Value",
145
+ _tag: "Ok",
146
146
  value: { count: 3 }
147
147
  });
148
148
  globalExpect(by.get("c2")).toMatchObject({
@@ -158,11 +158,11 @@ describe("fromApprovalMap + executeAll", () => {
158
158
  const approvals = new Map([["c2", { decision: "approve" }]]);
159
159
  const by = byCallId(resultsFrom(await Effect.runPromise(Stream.runCollect(eventsFromApprovalMap(approvals)))));
160
160
  globalExpect(by.get("c1")).toMatchObject({
161
- _tag: "Value",
161
+ _tag: "Ok",
162
162
  value: { count: 3 }
163
163
  });
164
164
  globalExpect(by.get("c2")).toMatchObject({
165
- _tag: "Value",
165
+ _tag: "Ok",
166
166
  value: { status: "sent" }
167
167
  });
168
168
  globalExpect(by.get("c3")).toMatchObject({
@@ -171,7 +171,7 @@ describe("fromApprovalMap + executeAll", () => {
171
171
  });
172
172
  });
173
173
  });
174
- describe("executeAll: graceful degradation", () => {
174
+ describe("run: graceful degradation", () => {
175
175
  it("unknown tool name → Failure(unknown_tool); other calls still execute", async () => {
176
176
  const callsWithBogus = [
177
177
  fc("c1", "web_search", { query: "x" }),
@@ -179,21 +179,21 @@ describe("executeAll: graceful degradation", () => {
179
179
  fc("c3", "delete_database", { name: "prod" })
180
180
  ];
181
181
  const by = byCallId(resultsFrom(await Effect.runPromise(Stream.runCollect((() => {
182
- const plan = fromApprovalMap(isSensitive, new Map([["c3", { decision: "approve" }]]))(callsWithBogus);
183
- return Stream.merge(executeAll(allTools, plan.approved), rejectedStream(plan.rejected));
182
+ const plan = fromMap(isSensitive, new Map([["c3", { decision: "approve" }]]))(callsWithBogus);
183
+ return Stream.merge(run(allTools, plan.approved), rejectedStream(plan.rejected));
184
184
  })()))));
185
- globalExpect(by.get("c1")).toMatchObject({ _tag: "Value" });
185
+ globalExpect(by.get("c1")).toMatchObject({ _tag: "Ok" });
186
186
  globalExpect(by.get("c2")).toMatchObject({
187
187
  _tag: "Failure",
188
188
  kind: "unknown_tool"
189
189
  });
190
190
  globalExpect(by.get("c3")).toMatchObject({
191
- _tag: "Value",
191
+ _tag: "Ok",
192
192
  value: { status: "dropped" }
193
193
  });
194
194
  });
195
195
  });
196
- describe("fromVerdictQueue + executeAll", () => {
196
+ describe("fromQueue + run", () => {
197
197
  it("queue-driven: approve + deny resolve correctly with ApprovalRequested events", async () => {
198
198
  const collected = await Effect.runPromise(Effect.gen(function* () {
199
199
  const verdicts = yield* Queue.unbounded();
@@ -207,15 +207,15 @@ describe("fromVerdictQueue + executeAll", () => {
207
207
  reason: "too risky"
208
208
  });
209
209
  const events = Stream.unwrap(Effect.gen(function* () {
210
- const { approved, decisions, announce } = yield* fromVerdictQueue(isSensitive, verdicts)(calls);
211
- return Stream.merge(announce, Stream.merge(executeAll(allTools, approved), decisions.pipe(Stream.flatMap(eventsFromDecision))));
210
+ const { approved, decisions, approvalRequests } = yield* fromQueue(isSensitive, verdicts)(calls);
211
+ return Stream.merge(approvalRequests, Stream.merge(run(allTools, approved), decisions.pipe(Stream.flatMap(eventsFromDecision))));
212
212
  }));
213
213
  return yield* Stream.runCollect(events);
214
214
  }));
215
215
  globalExpect(collected.filter(isApprovalRequested)).toHaveLength(2);
216
216
  const by = byCallId(resultsFrom(collected));
217
217
  globalExpect(by.get("c2")).toMatchObject({
218
- _tag: "Value",
218
+ _tag: "Ok",
219
219
  value: { status: "sent" }
220
220
  });
221
221
  globalExpect(by.get("c3")).toMatchObject({
@@ -228,7 +228,7 @@ describe("fromVerdictQueue + executeAll", () => {
228
228
  describe("findUnansweredCalls / cancelAllPending / isReconciled", () => {
229
229
  const orphan = fc("c99", "delete_database", { name: "prod" });
230
230
  const answered = fc("c98", "web_search", { query: "x" });
231
- const answeredOutput = functionCallOutput("c98", JSON.stringify({ count: 0 }));
231
+ const answeredOutput = toolCallOutput("c98", JSON.stringify({ count: 0 }));
232
232
  it("findUnansweredCalls returns only orphans", () => {
233
233
  const unanswered = findUnansweredCalls([
234
234
  userText("hi"),
@@ -242,7 +242,7 @@ describe("findUnansweredCalls / cancelAllPending / isReconciled", () => {
242
242
  it("isReconciled is false when orphans exist, true otherwise", () => {
243
243
  const stale = [userText("hi"), orphan];
244
244
  globalExpect(isReconciled(stale)).toBe(false);
245
- globalExpect(isReconciled([...stale, ...cancelAllPending(stale).map(toFunctionCallOutput)])).toBe(true);
245
+ globalExpect(isReconciled([...stale, ...cancelAllPending(stale).map(toToolCallOutput)])).toBe(true);
246
246
  });
247
247
  it("cancelAllPending synthesizes one Failure(cancelled) per orphan", () => {
248
248
  const closures = cancelAllPending([
@@ -261,20 +261,20 @@ describe("findUnansweredCalls / cancelAllPending / isReconciled", () => {
261
261
  reason: "user moved on"
262
262
  });
263
263
  });
264
- it("follow-up: map closures to FunctionCallOutput before appending new user message", () => {
264
+ it("follow-up: map closures to ToolCallOutput before appending new user message", () => {
265
265
  const stale = [userText("first request"), orphan];
266
266
  const closures = cancelAllPending(stale, "user redirected");
267
267
  globalExpect(findUnansweredCalls([
268
268
  ...stale,
269
- ...closures.map(toFunctionCallOutput),
269
+ ...closures.map(toToolCallOutput),
270
270
  userText("never mind")
271
271
  ])).toHaveLength(0);
272
272
  });
273
273
  });
274
- describe("toFunctionCallOutput", () => {
274
+ describe("toToolCallOutput", () => {
275
275
  it("round-trips a Value result", () => {
276
- const out = toFunctionCallOutput({
277
- _tag: "Value",
276
+ const out = toToolCallOutput({
277
+ _tag: "Ok",
278
278
  call_id: "c1",
279
279
  tool: "web_search",
280
280
  value: { count: 3 }
@@ -283,7 +283,7 @@ describe("toFunctionCallOutput", () => {
283
283
  globalExpect(JSON.parse(out.output)).toEqual({ count: 3 });
284
284
  });
285
285
  it("round-trips a Failure result with reason", () => {
286
- const out = toFunctionCallOutput({
286
+ const out = toToolCallOutput({
287
287
  _tag: "Failure",
288
288
  call_id: "c2",
289
289
  tool: "bulk_email",
@@ -296,7 +296,7 @@ describe("toFunctionCallOutput", () => {
296
296
  });
297
297
  });
298
298
  it("round-trips a Failure result without reason (omits the field)", () => {
299
- const out = toFunctionCallOutput({
299
+ const out = toToolCallOutput({
300
300
  _tag: "Failure",
301
301
  call_id: "c3",
302
302
  tool: "delete_database",
@@ -305,14 +305,14 @@ describe("toFunctionCallOutput", () => {
305
305
  globalExpect(JSON.parse(out.output)).toEqual({ kind: "cancelled" });
306
306
  });
307
307
  });
308
- describe("executeAll", () => {
308
+ describe("run", () => {
309
309
  it("runs all calls passed to it", async () => {
310
- const collected = await Effect.runPromise(Stream.runCollect(executeAll(allTools, calls)));
310
+ const collected = await Effect.runPromise(Stream.runCollect(run(allTools, calls)));
311
311
  globalExpect(collected.filter(isOutput)).toHaveLength(3);
312
- globalExpect(collected.filter(isOutput).every((e) => isValue(e.result))).toBe(true);
312
+ globalExpect(collected.filter(isOutput).every((e) => isOk(e.result))).toBe(true);
313
313
  });
314
314
  });
315
315
  //#endregion
316
316
  export {};
317
317
 
318
- //# sourceMappingURL=Resolvers.test.mjs.map
318
+ //# sourceMappingURL=Approval.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Approval.test.mjs","names":["makeTool","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 { type ApprovalMapEntry, type ApprovalDecision, fromMap, fromQueue } from \"./Approval.js\"\nimport { fromEffectSchema, make as makeTool, streaming } from \"./Tool.js\"\nimport { 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 = streaming({\n name: \"web_search\",\n description: \"search\",\n inputSchema: fromEffectSchema(Schema.Struct({ query: Schema.String })),\n run: ({ query }) =>\n Stream.fromIterable([\n { url: \"a\", title: `${query} 1` },\n { url: \"b\", title: `${query} 2` },\n { url: \"c\", title: `${query} 3` },\n ]),\n finalize: (hits) => ({ count: hits.length }),\n})\n\nconst bulkEmail = streaming({\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 }) =>\n Stream.fromIterable(\n recipients.map((_, i) => ({\n type: \"progress\" as const,\n sent: i + 1,\n total: recipients.length,\n })),\n ),\n finalize: (events) => ({ status: \"sent\" as const, delivered: events.length }),\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 = [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\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(\"round-trips a Failure result with reason\", () => {\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({ kind: \"denied\", reason: \"spam concern\" })\n })\n\n it(\"round-trips a Failure result without reason (omits the field)\", () => {\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({ 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,5 +1,5 @@
1
- import { d as Item, o as FunctionCall } from "../Items-BH8xUkoR.mjs";
2
- import { ToolResult } from "./Outcome.mjs";
1
+ import { o as HistoryItem, v as ToolCall } from "../Items-pXR5BAgW.mjs";
2
+ import { ToolResult } from "./ToolResult.mjs";
3
3
 
4
4
  //#region src/tool/HistoryCheck.d.ts
5
5
  declare namespace HistoryCheck_d_exports {
@@ -10,18 +10,18 @@ declare namespace HistoryCheck_d_exports {
10
10
  * `function_call_output` later in `history` (correlated by `call_id`).
11
11
  * Empty result = history is provider-submittable from this invariant.
12
12
  */
13
- declare const findUnansweredCalls: (history: ReadonlyArray<Item>) => ReadonlyArray<FunctionCall>;
13
+ declare const findUnansweredCalls: (history: ReadonlyArray<HistoryItem>) => ReadonlyArray<ToolCall>;
14
14
  /** Cheap predicate: is this history submittable to a provider? */
15
- declare const isReconciled: (history: ReadonlyArray<Item>) => boolean;
15
+ declare const isReconciled: (history: ReadonlyArray<HistoryItem>) => boolean;
16
16
  /**
17
17
  * Synthesize cancellation results for every unanswered call. Caller maps
18
- * via `toFunctionCallOutput` and appends to history before submitting.
18
+ * via `toToolCallOutput` and appends to history before submitting.
19
19
  *
20
20
  * Use when: a new user message arrives mid-approval; an approval timer
21
21
  * fires; a persisted checkpoint contains orphans (crash recovery); a
22
22
  * stateless HTTP server reconstructed history from a stale checkpoint.
23
23
  */
24
- declare const cancelAllPending: (history: ReadonlyArray<Item>, reason?: string) => ReadonlyArray<ToolResult>;
24
+ declare const cancelAllPending: (history: ReadonlyArray<HistoryItem>, reason?: string) => ReadonlyArray<ToolResult>;
25
25
  //#endregion
26
26
  export { cancelAllPending, findUnansweredCalls, isReconciled, HistoryCheck_d_exports as t };
27
27
  //# sourceMappingURL=HistoryCheck.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HistoryCheck.d.mts","names":[],"sources":["../../src/tool/HistoryCheck.ts"],"mappings":";;;;;;;;;;;;cAyBa,mBAAA,GAAuB,OAAA,EAAS,aAAA,CAAc,IAAA,MAAQ,aAAA,CAAc,YAAA;;cAMpE,YAAA,GAAgB,OAAA,EAAS,aAAA,CAAc,IAAA;;;;;AAApD;;;;cAWa,gBAAA,GACX,OAAA,EAAS,aAAA,CAAc,IAAA,GACvB,MAAA,cACC,aAAA,CAAc,UAAA"}
1
+ {"version":3,"file":"HistoryCheck.d.mts","names":[],"sources":["../../src/tool/HistoryCheck.ts"],"mappings":";;;;;;;;;;;;cAoBa,mBAAA,GACX,OAAA,EAAS,aAAA,CAAc,WAAA,MACtB,aAAA,CAAc,QAAA;;cAMJ,YAAA,GAAgB,OAAA,EAAS,aAAA,CAAc,WAAA;;;;;AAApD;;;;cAWa,gBAAA,GACX,OAAA,EAAS,aAAA,CAAc,WAAA,GACvB,MAAA,cACC,aAAA,CAAc,UAAA"}
@@ -1,6 +1,6 @@
1
1
  import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
2
- import { isFunctionCall, isFunctionCallOutput } from "../domain/Items.mjs";
3
- import { cancelled } from "./Outcome.mjs";
2
+ import { isToolCall, isToolCallOutput } from "../domain/Items.mjs";
3
+ import { cancelled } from "./ToolResult.mjs";
4
4
  //#region src/tool/HistoryCheck.ts
5
5
  /**
6
6
  * History-consistency primitives. Useful even WITHOUT HITL.
@@ -25,14 +25,14 @@ var HistoryCheck_exports = /* @__PURE__ */ __exportAll({
25
25
  * Empty result = history is provider-submittable from this invariant.
26
26
  */
27
27
  const findUnansweredCalls = (history) => {
28
- const answered = new Set(history.filter(isFunctionCallOutput).map((o) => o.call_id));
29
- return history.filter(isFunctionCall).filter((c) => !answered.has(c.call_id));
28
+ const answered = new Set(history.filter(isToolCallOutput).map((o) => o.call_id));
29
+ return history.filter(isToolCall).filter((c) => !answered.has(c.call_id));
30
30
  };
31
31
  /** Cheap predicate: is this history submittable to a provider? */
32
32
  const isReconciled = (history) => findUnansweredCalls(history).length === 0;
33
33
  /**
34
34
  * Synthesize cancellation results for every unanswered call. Caller maps
35
- * via `toFunctionCallOutput` and appends to history before submitting.
35
+ * via `toToolCallOutput` and appends to history before submitting.
36
36
  *
37
37
  * Use when: a new user message arrives mid-approval; an approval timer
38
38
  * fires; a persisted checkpoint contains orphans (crash recovery); a
@@ -1 +1 @@
1
- {"version":3,"file":"HistoryCheck.mjs","names":[],"sources":["../../src/tool/HistoryCheck.ts"],"sourcesContent":["/**\n * History-consistency primitives. Useful even WITHOUT HITL.\n *\n * Every provider rejects a new request if any prior `function_call` lacks\n * a matching `function_call_output`. Multi-turn flows that can be\n * interrupted, restarted, or branched (HITL, mid-stream abort, persisted\n * checkpoints, stateless HTTP servers) need to detect orphans and\n * synthesize closing outputs before submitting.\n *\n * Recipe author calls these at known transition points (right before the\n * next provider request). Not invoked from inside the loop.\n */\nimport {\n type FunctionCall,\n type Item,\n isFunctionCall,\n isFunctionCallOutput,\n} from \"../domain/Items.js\"\nimport { type ToolResult, cancelled } from \"./Outcome.js\"\n\n/**\n * Return every `function_call` in `history` that does not have a matching\n * `function_call_output` later in `history` (correlated by `call_id`).\n * Empty result = history is provider-submittable from this invariant.\n */\nexport const findUnansweredCalls = (history: ReadonlyArray<Item>): ReadonlyArray<FunctionCall> => {\n const answered = new Set(history.filter(isFunctionCallOutput).map((o) => o.call_id))\n return history.filter(isFunctionCall).filter((c) => !answered.has(c.call_id))\n}\n\n/** Cheap predicate: is this history submittable to a provider? */\nexport const isReconciled = (history: ReadonlyArray<Item>): boolean =>\n findUnansweredCalls(history).length === 0\n\n/**\n * Synthesize cancellation results for every unanswered call. Caller maps\n * via `toFunctionCallOutput` and appends to history before submitting.\n *\n * Use when: a new user message arrives mid-approval; an approval timer\n * fires; a persisted checkpoint contains orphans (crash recovery); a\n * stateless HTTP server reconstructed history from a stale checkpoint.\n */\nexport const cancelAllPending = (\n history: ReadonlyArray<Item>,\n reason?: string,\n): ReadonlyArray<ToolResult> => findUnansweredCalls(history).map((call) => cancelled(call, reason))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,uBAAuB,YAA8D;CAChG,MAAM,WAAW,IAAI,IAAI,QAAQ,OAAO,qBAAqB,CAAC,KAAK,MAAM,EAAE,QAAQ,CAAC;AACpF,QAAO,QAAQ,OAAO,eAAe,CAAC,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,QAAQ,CAAC;;;AAI/E,MAAa,gBAAgB,YAC3B,oBAAoB,QAAQ,CAAC,WAAW;;;;;;;;;AAU1C,MAAa,oBACX,SACA,WAC8B,oBAAoB,QAAQ,CAAC,KAAK,SAAS,UAAU,MAAM,OAAO,CAAC"}
1
+ {"version":3,"file":"HistoryCheck.mjs","names":[],"sources":["../../src/tool/HistoryCheck.ts"],"sourcesContent":["/**\n * History-consistency primitives. Useful even WITHOUT HITL.\n *\n * Every provider rejects a new request if any prior `function_call` lacks\n * a matching `function_call_output`. Multi-turn flows that can be\n * interrupted, restarted, or branched (HITL, mid-stream abort, persisted\n * checkpoints, stateless HTTP servers) need to detect orphans and\n * synthesize closing outputs before submitting.\n *\n * Recipe author calls these at known transition points (right before the\n * next provider request). Not invoked from inside the loop.\n */\nimport { type ToolCall, type HistoryItem, isToolCall, isToolCallOutput } from \"../domain/Items.js\"\nimport { type ToolResult, cancelled } from \"./ToolResult.js\"\n\n/**\n * Return every `function_call` in `history` that does not have a matching\n * `function_call_output` later in `history` (correlated by `call_id`).\n * Empty result = history is provider-submittable from this invariant.\n */\nexport const findUnansweredCalls = (\n history: ReadonlyArray<HistoryItem>,\n): ReadonlyArray<ToolCall> => {\n const answered = new Set(history.filter(isToolCallOutput).map((o) => o.call_id))\n return history.filter(isToolCall).filter((c) => !answered.has(c.call_id))\n}\n\n/** Cheap predicate: is this history submittable to a provider? */\nexport const isReconciled = (history: ReadonlyArray<HistoryItem>): boolean =>\n findUnansweredCalls(history).length === 0\n\n/**\n * Synthesize cancellation results for every unanswered call. Caller maps\n * via `toToolCallOutput` and appends to history before submitting.\n *\n * Use when: a new user message arrives mid-approval; an approval timer\n * fires; a persisted checkpoint contains orphans (crash recovery); a\n * stateless HTTP server reconstructed history from a stale checkpoint.\n */\nexport const cancelAllPending = (\n history: ReadonlyArray<HistoryItem>,\n reason?: string,\n): ReadonlyArray<ToolResult> => findUnansweredCalls(history).map((call) => cancelled(call, reason))\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,MAAa,uBACX,YAC4B;CAC5B,MAAM,WAAW,IAAI,IAAI,QAAQ,OAAO,iBAAiB,CAAC,KAAK,MAAM,EAAE,QAAQ,CAAC;AAChF,QAAO,QAAQ,OAAO,WAAW,CAAC,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAE,QAAQ,CAAC;;;AAI3E,MAAa,gBAAgB,YAC3B,oBAAoB,QAAQ,CAAC,WAAW;;;;;;;;;AAU1C,MAAa,oBACX,SACA,WAC8B,oBAAoB,QAAQ,CAAC,KAAK,SAAS,UAAU,MAAM,OAAO,CAAC"}
@@ -1,2 +1,2 @@
1
- import { a as Tool, c as ToolInputSchema, d as fromEffectSchema, f as fromStandardSchema, g as toDescriptors, h as streaming, i as StreamingTool, m as make, n as AnyPlainTool, o as ToolDescriptor, p as isStreamingTool, r as AnyStreamingTool, s as ToolError, t as AnyKindTool, u as execute } from "../Tool-87ViKCCO.mjs";
2
- export { AnyKindTool, AnyPlainTool, AnyStreamingTool, StreamingTool, Tool, ToolDescriptor, ToolError, ToolInputSchema, execute, fromEffectSchema, fromStandardSchema, isStreamingTool, make, streaming, toDescriptors };
1
+ import { a as Tool, c as ToolInputSchema, d as fromEffectSchema, f as fromStandardSchema, g as toDescriptors, h as streaming, i as StreamingTool, m as make, n as AnyStreamingTool, o as ToolDescriptor, p as isStreamingTool, r as AnyTool, s as ToolError, t as AnyPlainTool, u as execute } from "../Tool-DP-AwKOf.mjs";
2
+ export { AnyPlainTool, AnyStreamingTool, AnyTool, StreamingTool, Tool, ToolDescriptor, ToolError, ToolInputSchema, execute, fromEffectSchema, fromStandardSchema, isStreamingTool, make, streaming, toDescriptors };
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
2
- import { functionCallOutput } from "../domain/Items.mjs";
2
+ import { toolCallOutput } from "../domain/Items.mjs";
3
3
  import { Effect, Schema } from "effect";
4
4
  //#region src/tool/Tool.ts
5
5
  var Tool_exports = /* @__PURE__ */ __exportAll({
@@ -48,8 +48,8 @@ const streaming = (spec) => ({
48
48
  const isStreamingTool = (t) => "_kind" in t && t._kind === "streaming";
49
49
  /**
50
50
  * Render any-kind tools (mixed plain and streaming) to provider-agnostic
51
- * descriptors. Mirrors `Toolkit.toDescriptors` but accepts the union type
52
- * so a single list can carry both kinds.
51
+ * descriptors. Accepts the union type so a single list can carry both
52
+ * plain and streaming tools.
53
53
  */
54
54
  const toDescriptors = (tools) => tools.map((tool) => {
55
55
  const inputSchema = tool.inputSchema["~standard"].jsonSchema.input({ target: "draft-2020-12" });
@@ -88,7 +88,7 @@ const execute = (tool, call) => Effect.gen(function* () {
88
88
  cause: result.issues
89
89
  });
90
90
  const output = yield* tool.run(result.value).pipe(Effect.mapError(toToolError(call, tool.name, "Tool execution failed")));
91
- return functionCallOutput(call.call_id, JSON.stringify(output));
91
+ return toolCallOutput(call.call_id, JSON.stringify(output));
92
92
  });
93
93
  //#endregion
94
94
  export { ToolError, execute, fromEffectSchema, fromStandardSchema, isStreamingTool, make, streaming, Tool_exports as t, toDescriptors };
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.mjs","names":[],"sources":["../../src/tool/Tool.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from \"@standard-schema/spec\"\nimport { Effect, Schema, Stream } from \"effect\"\nimport type { FunctionCall, FunctionCallOutput } from \"../domain/Items.js\"\nimport { functionCallOutput } from \"../domain/Items.js\"\n\nexport class ToolError extends Schema.TaggedErrorClass<ToolError>(\"@betalyra/effect-uai/ToolError\")(\n \"ToolError\",\n {\n call_id: Schema.String,\n tool: Schema.String,\n message: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n },\n) {}\n\n/**\n * Schemas accepted on `Tool.inputSchema`. Must implement both Standard\n * Schema validation and JSON Schema conversion (for rendering tool\n * descriptors to provider request bodies).\n *\n * Any Standard-Schema-compliant library that exposes both interfaces\n * works directly: Zod 4+, Valibot, ArkType, Effect Schema (after\n * `fromEffectSchema`), etc.\n */\nexport type ToolInputSchema<Input = unknown> = StandardSchemaV1<unknown, Input> &\n StandardJSONSchemaV1<unknown, Input>\n\n/**\n * Convenience wrapper for Effect Schema users - adds both the\n * `validate` and `jsonSchema` extensions to a plain Effect Schema so it\n * can be used as a `Tool.inputSchema`.\n */\nexport const fromEffectSchema = <S extends Schema.Codec<any, any, never, any>>(\n schema: S,\n): S & ToolInputSchema<S[\"Type\"]> =>\n Schema.toStandardJSONSchemaV1(Schema.toStandardSchemaV1(schema)) as unknown as S &\n ToolInputSchema<S[\"Type\"]>\n\n/**\n * Use any schema library that implements both Standard Schema (validation)\n * and Standard JSON Schema (JSON Schema generation) as a `Tool.inputSchema`.\n * Covers Zod 4.2+, Valibot 1.2+, and ArkType 2.1.28+ in one helper.\n *\n * Effect Schema doesn't implement Standard JSON Schema natively — use\n * `fromEffectSchema` for those.\n *\n * The intersection constraint catches missing interfaces at compile time:\n * a Zod v3 schema (no Standard JSON Schema) produces a precise type error\n * pointing at the missing interface rather than a runtime surprise. The\n * helper itself is a thin type-narrowing identity — schemas that satisfy\n * both standards already structurally satisfy `ToolInputSchema`; the\n * helper makes the input type inference explicit at the call site.\n */\nexport const fromStandardSchema = <S extends StandardSchemaV1 & StandardJSONSchemaV1>(\n schema: S,\n): S & ToolInputSchema<StandardSchemaV1.InferOutput<S>> =>\n schema as S & ToolInputSchema<StandardSchemaV1.InferOutput<S>>\n\nexport type Tool<Name extends string, Input, Output, R = never> = {\n readonly name: Name\n readonly description: string\n readonly inputSchema: ToolInputSchema<Input>\n readonly run: (input: Input) => Effect.Effect<Output, unknown, R>\n /**\n * Whether the provider should render this tool with its strict-mode\n * flag (OpenAI's `strict: true`, etc). Default: true. The framework\n * never rewrites the schema; if the rendered JSON Schema isn't\n * compatible, the provider returns an error.\n */\n readonly strict?: boolean\n}\n\n/**\n * Provider-agnostic tool descriptor. Each provider maps `inputSchema`\n * to its own wire field (OpenAI → `parameters`, Anthropic →\n * `input_schema`). Built from a `Tool` by `Toolkit.toDescriptors`.\n */\nexport type ToolDescriptor = {\n readonly name: string\n readonly description: string\n readonly inputSchema: Record<string, unknown>\n readonly strict?: boolean\n}\n\nexport const make = <Name extends string, Input, Output, R = never>(\n spec: Tool<Name, Input, Output, R>,\n): Tool<Name, Input, Output, R> => spec\n\n// ---------------------------------------------------------------------------\n// Streaming tools\n//\n// `run` returns a `Stream<Event>` instead of an `Effect<Output>`. Events\n// flow through to the consumer as `ToolEvent.Intermediate`s in real time;\n// at end-of-stream `finalize(events)` reduces them to the model-facing\n// `Output`. Sub-agents, slow downloads with progress, recipe streamers.\n// ---------------------------------------------------------------------------\n\nexport type StreamingTool<Name extends string, Input, Event, Output, R = never> = {\n readonly _kind: \"streaming\"\n readonly name: Name\n readonly description: string\n readonly inputSchema: ToolInputSchema<Input>\n readonly run: (input: Input) => Stream.Stream<Event, unknown, R>\n readonly finalize: (events: ReadonlyArray<Event>) => Output\n readonly strict?: boolean\n}\n\nexport const streaming = <Name extends string, Input, Event, Output, R = never>(\n spec: Omit<StreamingTool<Name, Input, Event, Output, R>, \"_kind\">,\n): StreamingTool<Name, Input, Event, Output, R> => ({ _kind: \"streaming\", ...spec })\n\nexport type AnyStreamingTool<R = any> = StreamingTool<string, any, any, any, R>\nexport type AnyPlainTool<R = any> = Tool<string, any, any, R>\nexport type AnyKindTool<R = any> = AnyStreamingTool<R> | AnyPlainTool<R>\n\nexport const isStreamingTool = <R>(t: AnyKindTool<R>): t is AnyStreamingTool<R> =>\n \"_kind\" in t && t._kind === \"streaming\"\n\n/**\n * Render any-kind tools (mixed plain and streaming) to provider-agnostic\n * descriptors. Mirrors `Toolkit.toDescriptors` but accepts the union type\n * so a single list can carry both kinds.\n */\nexport const toDescriptors = <R>(\n tools: ReadonlyArray<AnyKindTool<R>>,\n): ReadonlyArray<ToolDescriptor> =>\n tools.map((tool) => {\n const inputSchema = tool.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n })\n return tool.strict !== undefined\n ? { name: tool.name, description: tool.description, inputSchema, strict: tool.strict }\n : { name: tool.name, description: tool.description, inputSchema }\n })\n\nconst toToolError = (call: FunctionCall, toolName: string, message: string) => (cause: unknown) =>\n new ToolError({ call_id: call.call_id, tool: toolName, message, cause })\n\n/**\n * Decode and validate the JSON arguments of a function_call against the\n * tool's input schema, run the tool, and serialize the output into a\n * function_call_output item.\n */\nexport const execute = <Name extends string, Input, Output, R>(\n tool: Tool<Name, Input, Output, R>,\n call: FunctionCall,\n): Effect.Effect<FunctionCallOutput, ToolError, R> =>\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: toToolError(call, tool.name, \"Failed to parse JSON arguments\"),\n })\n\n const result = yield* Effect.promise(() =>\n Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n )\n if (result.issues !== undefined) {\n return yield* new ToolError({\n call_id: call.call_id,\n tool: tool.name,\n message: \"Tool input failed schema validation\",\n cause: result.issues,\n })\n }\n\n const output = yield* tool\n .run(result.value)\n .pipe(Effect.mapError(toToolError(call, tool.name, \"Tool execution failed\")))\n return functionCallOutput(call.call_id, JSON.stringify(output))\n })\n"],"mappings":";;;;;;;;;;;;;;AAKA,IAAa,YAAb,cAA+B,OAAO,iBAA4B,iCAAiC,CACjG,aACA;CACE,SAAS,OAAO;CAChB,MAAM,OAAO;CACb,SAAS,OAAO;CAChB,OAAO,OAAO,SAAS,OAAO,QAAQ;CACvC,CACF,CAAC;;;;;;AAmBF,MAAa,oBACX,WAEA,OAAO,uBAAuB,OAAO,mBAAmB,OAAO,CAAC;;;;;;;;;;;;;;;;AAkBlE,MAAa,sBACX,WAEA;AA4BF,MAAa,QACX,SACiC;AAqBnC,MAAa,aACX,UACkD;CAAE,OAAO;CAAa,GAAG;CAAM;AAMnF,MAAa,mBAAsB,MACjC,WAAW,KAAK,EAAE,UAAU;;;;;;AAO9B,MAAa,iBACX,UAEA,MAAM,KAAK,SAAS;CAClB,MAAM,cAAc,KAAK,YAAY,aAAa,WAAW,MAAM,EACjE,QAAQ,iBACT,CAAC;AACF,QAAO,KAAK,WAAW,KAAA,IACnB;EAAE,MAAM,KAAK;EAAM,aAAa,KAAK;EAAa;EAAa,QAAQ,KAAK;EAAQ,GACpF;EAAE,MAAM,KAAK;EAAM,aAAa,KAAK;EAAa;EAAa;EACnE;AAEJ,MAAM,eAAe,MAAoB,UAAkB,aAAqB,UAC9E,IAAI,UAAU;CAAE,SAAS,KAAK;CAAS,MAAM;CAAU;CAAS;CAAO,CAAC;;;;;;AAO1E,MAAa,WACX,MACA,SAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,OAAO,IAAI;EAC/B,WAAW,KAAK,MAAM,KAAK,UAAU;EACrC,OAAO,YAAY,MAAM,KAAK,MAAM,iCAAiC;EACtE,CAAC;CAEF,MAAM,SAAS,OAAO,OAAO,cAC3B,QAAQ,QAAQ,KAAK,YAAY,aAAa,SAAS,OAAO,CAAC,CAChE;AACD,KAAI,OAAO,WAAW,KAAA,EACpB,QAAO,OAAO,IAAI,UAAU;EAC1B,SAAS,KAAK;EACd,MAAM,KAAK;EACX,SAAS;EACT,OAAO,OAAO;EACf,CAAC;CAGJ,MAAM,SAAS,OAAO,KACnB,IAAI,OAAO,MAAM,CACjB,KAAK,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,wBAAwB,CAAC,CAAC;AAC/E,QAAO,mBAAmB,KAAK,SAAS,KAAK,UAAU,OAAO,CAAC;EAC/D"}
1
+ {"version":3,"file":"Tool.mjs","names":[],"sources":["../../src/tool/Tool.ts"],"sourcesContent":["import type { StandardJSONSchemaV1, StandardSchemaV1 } from \"@standard-schema/spec\"\nimport { Effect, Schema, Stream } from \"effect\"\nimport type { ToolCall, ToolCallOutput } from \"../domain/Items.js\"\nimport { toolCallOutput } from \"../domain/Items.js\"\n\nexport class ToolError extends Schema.TaggedErrorClass<ToolError>(\"@betalyra/effect-uai/ToolError\")(\n \"ToolError\",\n {\n call_id: Schema.String,\n tool: Schema.String,\n message: Schema.String,\n cause: Schema.optional(Schema.Unknown),\n },\n) {}\n\n/**\n * Schemas accepted on `Tool.inputSchema`. Must implement both Standard\n * Schema validation and JSON Schema conversion (for rendering tool\n * descriptors to provider request bodies).\n *\n * Any Standard-Schema-compliant library that exposes both interfaces\n * works directly: Zod 4+, Valibot, ArkType, Effect Schema (after\n * `fromEffectSchema`), etc.\n */\nexport type ToolInputSchema<Input = unknown> = StandardSchemaV1<unknown, Input> &\n StandardJSONSchemaV1<unknown, Input>\n\n/**\n * Convenience wrapper for Effect Schema users - adds both the\n * `validate` and `jsonSchema` extensions to a plain Effect Schema so it\n * can be used as a `Tool.inputSchema`.\n */\nexport const fromEffectSchema = <S extends Schema.Codec<any, any, never, any>>(\n schema: S,\n): S & ToolInputSchema<S[\"Type\"]> =>\n Schema.toStandardJSONSchemaV1(Schema.toStandardSchemaV1(schema)) as unknown as S &\n ToolInputSchema<S[\"Type\"]>\n\n/**\n * Use any schema library that implements both Standard Schema (validation)\n * and Standard JSON Schema (JSON Schema generation) as a `Tool.inputSchema`.\n * Covers Zod 4.2+, Valibot 1.2+, and ArkType 2.1.28+ in one helper.\n *\n * Effect Schema doesn't implement Standard JSON Schema natively — use\n * `fromEffectSchema` for those.\n *\n * The intersection constraint catches missing interfaces at compile time:\n * a Zod v3 schema (no Standard JSON Schema) produces a precise type error\n * pointing at the missing interface rather than a runtime surprise. The\n * helper itself is a thin type-narrowing identity — schemas that satisfy\n * both standards already structurally satisfy `ToolInputSchema`; the\n * helper makes the input type inference explicit at the call site.\n */\nexport const fromStandardSchema = <S extends StandardSchemaV1 & StandardJSONSchemaV1>(\n schema: S,\n): S & ToolInputSchema<StandardSchemaV1.InferOutput<S>> =>\n schema as S & ToolInputSchema<StandardSchemaV1.InferOutput<S>>\n\nexport type Tool<Name extends string, Input, Output, R = never> = {\n readonly name: Name\n readonly description: string\n readonly inputSchema: ToolInputSchema<Input>\n readonly run: (input: Input) => Effect.Effect<Output, unknown, R>\n /**\n * Whether the provider should render this tool with its strict-mode\n * flag (OpenAI's `strict: true`, etc). Default: true. The framework\n * never rewrites the schema; if the rendered JSON Schema isn't\n * compatible, the provider returns an error.\n */\n readonly strict?: boolean\n}\n\n/**\n * Provider-agnostic tool descriptor. Each provider maps `inputSchema`\n * to its own wire field (OpenAI → `parameters`, Anthropic →\n * `input_schema`). Built from a `Tool` by `Tool.toDescriptors`.\n */\nexport type ToolDescriptor = {\n readonly name: string\n readonly description: string\n readonly inputSchema: Record<string, unknown>\n readonly strict?: boolean\n}\n\nexport const make = <Name extends string, Input, Output, R = never>(\n spec: Tool<Name, Input, Output, R>,\n): Tool<Name, Input, Output, R> => spec\n\n// ---------------------------------------------------------------------------\n// Streaming tools\n//\n// `run` returns a `Stream<Event>` instead of an `Effect<Output>`. Events\n// flow through to the consumer as `ToolEvent.Progress`s in real time;\n// at end-of-stream `finalize(events)` reduces them to the model-facing\n// `Output`. Sub-agents, slow downloads with progress, recipe streamers.\n// ---------------------------------------------------------------------------\n\nexport type StreamingTool<Name extends string, Input, Event, Output, R = never> = {\n readonly _kind: \"streaming\"\n readonly name: Name\n readonly description: string\n readonly inputSchema: ToolInputSchema<Input>\n readonly run: (input: Input) => Stream.Stream<Event, unknown, R>\n readonly finalize: (events: ReadonlyArray<Event>) => Output\n readonly strict?: boolean\n}\n\nexport const streaming = <Name extends string, Input, Event, Output, R = never>(\n spec: Omit<StreamingTool<Name, Input, Event, Output, R>, \"_kind\">,\n): StreamingTool<Name, Input, Event, Output, R> => ({ _kind: \"streaming\", ...spec })\n\nexport type AnyStreamingTool<R = any> = StreamingTool<string, any, any, any, R>\nexport type AnyPlainTool<R = any> = Tool<string, any, any, R>\nexport type AnyTool<R = any> = AnyStreamingTool<R> | AnyPlainTool<R>\n\nexport const isStreamingTool = <R>(t: AnyTool<R>): t is AnyStreamingTool<R> =>\n \"_kind\" in t && t._kind === \"streaming\"\n\n/**\n * Render any-kind tools (mixed plain and streaming) to provider-agnostic\n * descriptors. Accepts the union type so a single list can carry both\n * plain and streaming tools.\n */\nexport const toDescriptors = <R>(tools: ReadonlyArray<AnyTool<R>>): ReadonlyArray<ToolDescriptor> =>\n tools.map((tool) => {\n const inputSchema = tool.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n })\n return tool.strict !== undefined\n ? { name: tool.name, description: tool.description, inputSchema, strict: tool.strict }\n : { name: tool.name, description: tool.description, inputSchema }\n })\n\nconst toToolError = (call: ToolCall, toolName: string, message: string) => (cause: unknown) =>\n new ToolError({ call_id: call.call_id, tool: toolName, message, cause })\n\n/**\n * Decode and validate the JSON arguments of a function_call against the\n * tool's input schema, run the tool, and serialize the output into a\n * function_call_output item.\n */\nexport const execute = <Name extends string, Input, Output, R>(\n tool: Tool<Name, Input, Output, R>,\n call: ToolCall,\n): Effect.Effect<ToolCallOutput, ToolError, R> =>\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: toToolError(call, tool.name, \"Failed to parse JSON arguments\"),\n })\n\n const result = yield* Effect.promise(() =>\n Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n )\n if (result.issues !== undefined) {\n return yield* new ToolError({\n call_id: call.call_id,\n tool: tool.name,\n message: \"Tool input failed schema validation\",\n cause: result.issues,\n })\n }\n\n const output = yield* tool\n .run(result.value)\n .pipe(Effect.mapError(toToolError(call, tool.name, \"Tool execution failed\")))\n return toolCallOutput(call.call_id, JSON.stringify(output))\n })\n"],"mappings":";;;;;;;;;;;;;;AAKA,IAAa,YAAb,cAA+B,OAAO,iBAA4B,iCAAiC,CACjG,aACA;CACE,SAAS,OAAO;CAChB,MAAM,OAAO;CACb,SAAS,OAAO;CAChB,OAAO,OAAO,SAAS,OAAO,QAAQ;CACvC,CACF,CAAC;;;;;;AAmBF,MAAa,oBACX,WAEA,OAAO,uBAAuB,OAAO,mBAAmB,OAAO,CAAC;;;;;;;;;;;;;;;;AAkBlE,MAAa,sBACX,WAEA;AA4BF,MAAa,QACX,SACiC;AAqBnC,MAAa,aACX,UACkD;CAAE,OAAO;CAAa,GAAG;CAAM;AAMnF,MAAa,mBAAsB,MACjC,WAAW,KAAK,EAAE,UAAU;;;;;;AAO9B,MAAa,iBAAoB,UAC/B,MAAM,KAAK,SAAS;CAClB,MAAM,cAAc,KAAK,YAAY,aAAa,WAAW,MAAM,EACjE,QAAQ,iBACT,CAAC;AACF,QAAO,KAAK,WAAW,KAAA,IACnB;EAAE,MAAM,KAAK;EAAM,aAAa,KAAK;EAAa;EAAa,QAAQ,KAAK;EAAQ,GACpF;EAAE,MAAM,KAAK;EAAM,aAAa,KAAK;EAAa;EAAa;EACnE;AAEJ,MAAM,eAAe,MAAgB,UAAkB,aAAqB,UAC1E,IAAI,UAAU;CAAE,SAAS,KAAK;CAAS,MAAM;CAAU;CAAS;CAAO,CAAC;;;;;;AAO1E,MAAa,WACX,MACA,SAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,OAAO,IAAI;EAC/B,WAAW,KAAK,MAAM,KAAK,UAAU;EACrC,OAAO,YAAY,MAAM,KAAK,MAAM,iCAAiC;EACtE,CAAC;CAEF,MAAM,SAAS,OAAO,OAAO,cAC3B,QAAQ,QAAQ,KAAK,YAAY,aAAa,SAAS,OAAO,CAAC,CAChE;AACD,KAAI,OAAO,WAAW,KAAA,EACpB,QAAO,OAAO,IAAI,UAAU;EAC1B,SAAS,KAAK;EACd,MAAM,KAAK;EACX,SAAS;EACT,OAAO,OAAO;EACf,CAAC;CAGJ,MAAM,SAAS,OAAO,KACnB,IAAI,OAAO,MAAM,CACjB,KAAK,OAAO,SAAS,YAAY,MAAM,KAAK,MAAM,wBAAwB,CAAC,CAAC;AAC/E,QAAO,eAAe,KAAK,SAAS,KAAK,UAAU,OAAO,CAAC;EAC3D"}