@gjermundgaraba/effect-actions 0.19.0 → 0.21.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 (326) hide show
  1. package/README.md +19 -41
  2. package/dist/Action.d.mts +140 -48
  3. package/dist/Action.mjs +88 -44
  4. package/dist/ActionCli.d.mts +70 -35
  5. package/dist/ActionCli.mjs +53 -34
  6. package/dist/ActionHttp.d.mts +70 -38
  7. package/dist/ActionHttp.mjs +76 -25
  8. package/dist/ActionMcp.d.mts +56 -24
  9. package/dist/ActionMcp.mjs +104 -61
  10. package/dist/ActionRpc.d.mts +222 -57
  11. package/dist/ActionRpc.mjs +301 -49
  12. package/dist/ActionToolkit.d.mts +22 -7
  13. package/dist/ActionToolkit.mjs +37 -14
  14. package/dist/Authentication.d.mts +42 -12
  15. package/dist/Authentication.mjs +38 -32
  16. package/dist/Event.d.mts +370 -0
  17. package/dist/Event.mjs +103 -0
  18. package/dist/Testing.d.mts +1 -3
  19. package/dist/Testing.mjs +2 -3
  20. package/dist/Webhook.d.mts +146 -0
  21. package/dist/Webhook.mjs +72 -0
  22. package/dist/authentication/provider.d.mts +35 -12
  23. package/dist/authentication/provider.mjs +2 -3
  24. package/dist/authentication/refusal.mjs +3 -4
  25. package/dist/cli/commands.d.mts +9 -7
  26. package/dist/cli/commands.mjs +85 -35
  27. package/dist/contract/ast.mjs +5 -3
  28. package/dist/contract/authorizer.d.mts +8 -0
  29. package/dist/contract/authorizer.mjs +20 -0
  30. package/dist/contract/call.d.mts +9 -8
  31. package/dist/contract/call.mjs +49 -8
  32. package/dist/contract/callback.d.mts +7 -0
  33. package/dist/contract/callback.mjs +27 -0
  34. package/dist/contract/caller.d.mts +12 -3
  35. package/dist/contract/caller.mjs +23 -5
  36. package/dist/contract/canonical.mjs +10 -0
  37. package/dist/contract/codec.d.mts +9 -0
  38. package/dist/contract/codec.mjs +9 -0
  39. package/dist/contract/codes.mjs +81 -0
  40. package/dist/contract/declared.d.mts +0 -2
  41. package/dist/contract/declared.mjs +26 -11
  42. package/dist/contract/digest.mjs +6 -0
  43. package/dist/contract/errors.d.mts +9 -5
  44. package/dist/contract/errors.mjs +2 -3
  45. package/dist/contract/feed.d.mts +127 -0
  46. package/dist/contract/feed.mjs +249 -0
  47. package/dist/contract/hints.mjs +1 -2
  48. package/dist/contract/hub.d.mts +94 -0
  49. package/dist/contract/hub.mjs +133 -0
  50. package/dist/contract/idempotency.d.mts +47 -0
  51. package/dist/contract/idempotency.mjs +99 -0
  52. package/dist/contract/implementation.d.mts +73 -51
  53. package/dist/contract/implementation.mjs +94 -60
  54. package/dist/contract/input-check.d.mts +15 -0
  55. package/dist/contract/input-check.mjs +16 -0
  56. package/dist/contract/lease.mjs +28 -0
  57. package/dist/contract/listed.d.mts +4 -0
  58. package/dist/contract/listed.mjs +22 -0
  59. package/dist/contract/log.d.mts +41 -0
  60. package/dist/contract/log.mjs +144 -0
  61. package/dist/contract/media.d.mts +1 -3
  62. package/dist/contract/media.mjs +1 -2
  63. package/dist/contract/names.mjs +15 -6
  64. package/dist/contract/occurrence.d.mts +29 -0
  65. package/dist/contract/occurrence.mjs +45 -0
  66. package/dist/contract/pages.d.mts +13 -0
  67. package/dist/contract/pages.mjs +39 -0
  68. package/dist/contract/progress.d.mts +26 -0
  69. package/dist/contract/progress.mjs +72 -0
  70. package/dist/contract/push.d.mts +20 -0
  71. package/dist/contract/push.mjs +96 -0
  72. package/dist/contract/receiving.d.mts +29 -0
  73. package/dist/contract/receiving.mjs +42 -0
  74. package/dist/contract/recent.mjs +12 -0
  75. package/dist/contract/reported.mjs +16 -0
  76. package/dist/contract/serving.mjs +63 -0
  77. package/dist/contract/status.mjs +15 -7
  78. package/dist/contract/stream.d.mts +34 -0
  79. package/dist/contract/stream.mjs +31 -0
  80. package/dist/contract/subscribe.d.mts +45 -0
  81. package/dist/contract/subscribe.mjs +50 -0
  82. package/dist/contract/transient.mjs +28 -0
  83. package/dist/contract/watch.d.mts +18 -0
  84. package/dist/contract/watch.mjs +105 -0
  85. package/dist/contract/webhooks.d.mts +42 -0
  86. package/dist/contract/webhooks.mjs +53 -0
  87. package/dist/http/client.d.mts +37 -14
  88. package/dist/http/client.mjs +52 -14
  89. package/dist/http/events.d.mts +56 -0
  90. package/dist/http/events.mjs +102 -0
  91. package/dist/http/placed.d.mts +36 -0
  92. package/dist/http/placed.mjs +71 -0
  93. package/dist/http/schema-errors.mjs +2 -3
  94. package/dist/http/streamed.d.mts +21 -0
  95. package/dist/http/streamed.mjs +94 -0
  96. package/dist/http/watch.mjs +50 -0
  97. package/dist/mcp/bridge.mjs +283 -0
  98. package/dist/mcp/client.d.mts +30 -99
  99. package/dist/mcp/client.mjs +60 -134
  100. package/dist/mcp/events.mjs +142 -0
  101. package/dist/mcp/progress.mjs +34 -0
  102. package/dist/mcp/protocol.mjs +40 -15
  103. package/dist/mcp/reply.d.mts +79 -0
  104. package/dist/mcp/reply.mjs +99 -0
  105. package/dist/mcp/request.d.mts +40 -0
  106. package/dist/mcp/request.mjs +51 -0
  107. package/dist/mcp/watch.mjs +115 -0
  108. package/dist/rpc/watch.mjs +39 -0
  109. package/dist/stdio/console.mjs +14 -7
  110. package/dist/testing/memory.d.mts +1 -3
  111. package/dist/testing/memory.mjs +4 -5
  112. package/dist/webhook/address.mjs +47 -0
  113. package/dist/webhook/attempt.mjs +65 -0
  114. package/dist/webhook/receiver.mjs +67 -0
  115. package/dist/webhook/signing.mjs +28 -0
  116. package/dist/webhook/stored.mjs +57 -0
  117. package/dist/webhook/subscriptions.mjs +342 -0
  118. package/docs/Action-failures.md +2 -2
  119. package/docs/Action-streams.md +115 -0
  120. package/docs/Action.md +58 -124
  121. package/docs/ActionCli-failures.md +1 -2
  122. package/docs/ActionCli.md +83 -88
  123. package/docs/ActionHttp-failures.md +3 -3
  124. package/docs/ActionHttp.md +90 -70
  125. package/docs/ActionMcp-failures.md +2 -1
  126. package/docs/ActionMcp.md +120 -92
  127. package/docs/ActionRpc-events.md +126 -0
  128. package/docs/ActionRpc-failures.md +1 -2
  129. package/docs/ActionRpc.md +108 -79
  130. package/docs/ActionToolkit-failures.md +1 -2
  131. package/docs/ActionToolkit.md +33 -90
  132. package/docs/Authentication-advanced.md +57 -61
  133. package/docs/Authentication-failures.md +5 -4
  134. package/docs/Authentication.md +23 -58
  135. package/docs/Event-failures.md +15 -0
  136. package/docs/Event.md +281 -0
  137. package/docs/README.md +51 -82
  138. package/docs/Testing-failures.md +1 -5
  139. package/docs/Testing.md +126 -86
  140. package/docs/Webhook-failures.md +8 -0
  141. package/docs/Webhook.md +319 -0
  142. package/docs/setup-failures.md +2 -3
  143. package/docs/setup.md +32 -53
  144. package/examples/README.md +12 -37
  145. package/examples/assistant/README.md +21 -26
  146. package/examples/assistant/src/agent.ts +66 -24
  147. package/examples/assistant/src/cli.ts +7 -4
  148. package/examples/assistant/src/commands.ts +9 -4
  149. package/examples/assistant/src/identity/Identity.ts +5 -4
  150. package/examples/assistant/src/identity/authorize.ts +13 -5
  151. package/examples/assistant/src/identity/handlers.ts +3 -2
  152. package/examples/assistant/src/identity/permissions.ts +9 -3
  153. package/examples/assistant/src/platform/tasks-file.ts +15 -11
  154. package/examples/assistant/src/scripted.ts +7 -4
  155. package/examples/assistant/src/tasks/Tasks.ts +20 -6
  156. package/examples/assistant/src/tasks/handlers.ts +9 -5
  157. package/examples/assistant/src/tasks/store.ts +38 -11
  158. package/examples/assistant/src/toolkit.ts +11 -7
  159. package/examples/assistant/test/commands.test.ts +53 -27
  160. package/examples/assistant/test/identity/identity.test.ts +27 -11
  161. package/examples/assistant/test/platform/tasks-file.test.ts +39 -23
  162. package/examples/assistant/test/tasks/tasks.test.ts +62 -34
  163. package/examples/assistant/test/toolkit.test.ts +104 -75
  164. package/examples/build-events/README.md +83 -0
  165. package/examples/build-events/src/Api.ts +27 -0
  166. package/examples/build-events/src/Rpc.ts +26 -0
  167. package/examples/build-events/src/builds/Builds.ts +108 -0
  168. package/examples/build-events/src/builds/events.ts +54 -0
  169. package/examples/build-events/src/builds/handlers.ts +58 -0
  170. package/examples/build-events/src/builds/log.ts +30 -0
  171. package/examples/build-events/src/builds/status.ts +28 -0
  172. package/examples/build-events/src/builds/store.ts +130 -0
  173. package/examples/build-events/src/cli.ts +18 -0
  174. package/examples/build-events/src/commands.ts +32 -0
  175. package/examples/build-events/src/deploys/Deploys.ts +44 -0
  176. package/examples/build-events/src/deploys/approvers.ts +55 -0
  177. package/examples/build-events/src/deploys/events.ts +32 -0
  178. package/examples/build-events/src/deploys/handlers.ts +38 -0
  179. package/examples/build-events/src/identity/Identity.ts +29 -0
  180. package/examples/build-events/src/identity/members.ts +7 -0
  181. package/examples/build-events/src/identity/verifier.ts +22 -0
  182. package/examples/build-events/src/notifier/Notices.ts +26 -0
  183. package/examples/build-events/src/notifier/routes.ts +71 -0
  184. package/examples/build-events/src/notifier.ts +48 -0
  185. package/examples/build-events/src/routes.ts +71 -0
  186. package/examples/build-events/src/server.ts +62 -0
  187. package/examples/build-events/test/builds/events.test.ts +68 -0
  188. package/examples/build-events/test/builds/log.test.ts +71 -0
  189. package/examples/build-events/test/builds/progress.test.ts +57 -0
  190. package/examples/build-events/test/builds/status.test.ts +42 -0
  191. package/examples/build-events/test/commands.test.ts +103 -0
  192. package/examples/build-events/test/deploys/events.test.ts +52 -0
  193. package/examples/build-events/test/http.test.ts +95 -0
  194. package/examples/build-events/test/notifier.test.ts +80 -0
  195. package/examples/build-events/test/routes.test.ts +96 -0
  196. package/examples/build-events/test/rpc.test.ts +84 -0
  197. package/examples/build-events/test/support/as.ts +8 -0
  198. package/examples/build-events/test/support/memory.ts +43 -0
  199. package/examples/build-events/test/webhooks.test.ts +143 -0
  200. package/examples/kitchen-sink/README.md +178 -166
  201. package/examples/kitchen-sink/src/Actions.ts +11 -3
  202. package/examples/kitchen-sink/src/Api.ts +12 -6
  203. package/examples/kitchen-sink/src/Rpc.ts +7 -5
  204. package/examples/kitchen-sink/src/admin/Admin.ts +21 -10
  205. package/examples/kitchen-sink/src/admin/authenticate.ts +8 -4
  206. package/examples/kitchen-sink/src/admin/authorize.ts +13 -5
  207. package/examples/kitchen-sink/src/admin/grants.ts +5 -3
  208. package/examples/kitchen-sink/src/admin/handlers.ts +9 -5
  209. package/examples/kitchen-sink/src/agent.ts +14 -6
  210. package/examples/kitchen-sink/src/assistant/Assistant.ts +21 -15
  211. package/examples/kitchen-sink/src/assistant/conversations.ts +14 -9
  212. package/examples/kitchen-sink/src/assistant/demo-model.ts +2 -2
  213. package/examples/kitchen-sink/src/assistant/handlers.ts +42 -22
  214. package/examples/kitchen-sink/src/attachments/Attachments.ts +6 -6
  215. package/examples/kitchen-sink/src/attachments/download.ts +7 -6
  216. package/examples/kitchen-sink/src/attachments/handlers.ts +9 -4
  217. package/examples/kitchen-sink/src/attachments/store.ts +25 -18
  218. package/examples/kitchen-sink/src/browser.ts +10 -4
  219. package/examples/kitchen-sink/src/cli.ts +28 -12
  220. package/examples/kitchen-sink/src/config.ts +1 -1
  221. package/examples/kitchen-sink/src/identity/Identity.ts +24 -10
  222. package/examples/kitchen-sink/src/identity/authenticate.ts +19 -11
  223. package/examples/kitchen-sink/src/identity/authorize.ts +14 -4
  224. package/examples/kitchen-sink/src/identity/directory.ts +16 -7
  225. package/examples/kitchen-sink/src/identity/handlers.ts +1 -0
  226. package/examples/kitchen-sink/src/identity/members.ts +7 -2
  227. package/examples/kitchen-sink/src/identity/quota.ts +23 -20
  228. package/examples/kitchen-sink/src/issues/Issues.ts +91 -15
  229. package/examples/kitchen-sink/src/issues/assistant.ts +4 -2
  230. package/examples/kitchen-sink/src/issues/events.ts +46 -0
  231. package/examples/kitchen-sink/src/issues/export.ts +35 -0
  232. package/examples/kitchen-sink/src/issues/guide.ts +4 -3
  233. package/examples/kitchen-sink/src/issues/handlers.ts +20 -7
  234. package/examples/kitchen-sink/src/issues/store.ts +156 -67
  235. package/examples/kitchen-sink/src/issues/write-limit.ts +44 -18
  236. package/examples/kitchen-sink/src/memory.ts +11 -2
  237. package/examples/kitchen-sink/src/ops.ts +10 -5
  238. package/examples/kitchen-sink/src/request-policy.ts +25 -9
  239. package/examples/kitchen-sink/src/routes.ts +67 -27
  240. package/examples/kitchen-sink/src/sdk.ts +22 -12
  241. package/examples/kitchen-sink/src/seed.ts +15 -9
  242. package/examples/kitchen-sink/src/server.ts +24 -10
  243. package/examples/kitchen-sink/src/stdio.ts +9 -3
  244. package/examples/kitchen-sink/src/workspaces/Workspaces.ts +10 -7
  245. package/examples/kitchen-sink/src/workspaces/handlers.ts +3 -2
  246. package/examples/kitchen-sink/src/workspaces/registry.ts +16 -10
  247. package/examples/kitchen-sink/src/workspaces/tenant.ts +12 -5
  248. package/examples/kitchen-sink/test/admin/admin.test.ts +38 -23
  249. package/examples/kitchen-sink/test/assistant/assistant.test.ts +471 -299
  250. package/examples/kitchen-sink/test/attachments/attachments.test.ts +135 -94
  251. package/examples/kitchen-sink/test/identity/identity.test.ts +110 -82
  252. package/examples/kitchen-sink/test/identity/session.test.ts +65 -43
  253. package/examples/kitchen-sink/test/issues/assistant.test.ts +50 -31
  254. package/examples/kitchen-sink/test/issues/events.test.ts +273 -0
  255. package/examples/kitchen-sink/test/issues/export.test.ts +120 -0
  256. package/examples/kitchen-sink/test/issues/issues.test.ts +105 -75
  257. package/examples/kitchen-sink/test/request-policy.test.ts +130 -33
  258. package/examples/kitchen-sink/test/routes.test.ts +102 -57
  259. package/examples/kitchen-sink/test/support/as.ts +9 -0
  260. package/examples/kitchen-sink/test/support/scripted.ts +6 -5
  261. package/examples/kitchen-sink/test/support/services.ts +17 -0
  262. package/examples/live-rpc/README.md +33 -35
  263. package/examples/live-rpc/src/Rpc.ts +9 -5
  264. package/examples/live-rpc/src/browser.ts +39 -11
  265. package/examples/live-rpc/src/chat/Chat.ts +30 -16
  266. package/examples/live-rpc/src/chat/authorize.ts +2 -1
  267. package/examples/live-rpc/src/chat/events.ts +74 -0
  268. package/examples/live-rpc/src/chat/handlers.ts +13 -5
  269. package/examples/live-rpc/src/chat/room.ts +9 -5
  270. package/examples/live-rpc/src/chat/store.ts +51 -14
  271. package/examples/live-rpc/src/client.ts +22 -5
  272. package/examples/live-rpc/src/identity/Identity.ts +3 -3
  273. package/examples/live-rpc/src/identity/verifier.ts +8 -4
  274. package/examples/live-rpc/src/page.ts +58 -12
  275. package/examples/live-rpc/src/routes.ts +14 -10
  276. package/examples/live-rpc/src/server.ts +15 -5
  277. package/examples/live-rpc/test/browser.test.ts +127 -50
  278. package/examples/live-rpc/test/chat/chat.test.ts +215 -23
  279. package/examples/live-rpc/test/routes.test.ts +130 -46
  280. package/examples/live-rpc/test/server.test.ts +94 -33
  281. package/examples/mcp-tools/README.md +24 -38
  282. package/examples/mcp-tools/src/platform/fake-farm.ts +63 -24
  283. package/examples/mcp-tools/src/platform/print-farm.ts +59 -29
  284. package/examples/mcp-tools/src/printers/Printers.ts +18 -7
  285. package/examples/mcp-tools/src/printers/farm.ts +15 -5
  286. package/examples/mcp-tools/src/printers/handlers.ts +12 -6
  287. package/examples/mcp-tools/src/routes.ts +8 -6
  288. package/examples/mcp-tools/src/server.ts +12 -5
  289. package/examples/mcp-tools/src/stdio.ts +3 -2
  290. package/examples/mcp-tools/test/printers/printers.test.ts +132 -89
  291. package/examples/mcp-tools/test/routes.test.ts +140 -81
  292. package/examples/notes-service/README.md +30 -45
  293. package/examples/notes-service/src/Api.ts +1 -0
  294. package/examples/notes-service/src/browser.ts +15 -14
  295. package/examples/notes-service/src/cli.ts +8 -3
  296. package/examples/notes-service/src/commands.ts +46 -20
  297. package/examples/notes-service/src/identity/Identity.ts +2 -2
  298. package/examples/notes-service/src/identity/authorize.ts +6 -2
  299. package/examples/notes-service/src/identity/handlers.ts +3 -2
  300. package/examples/notes-service/src/identity/users.ts +2 -1
  301. package/examples/notes-service/src/identity/verifier.ts +12 -6
  302. package/examples/notes-service/src/notes/Notes.ts +46 -22
  303. package/examples/notes-service/src/notes/handlers.ts +18 -9
  304. package/examples/notes-service/src/notes/store.ts +70 -43
  305. package/examples/notes-service/src/routes.ts +7 -5
  306. package/examples/notes-service/src/server.ts +12 -5
  307. package/examples/notes-service/src/status/Status.ts +1 -1
  308. package/examples/notes-service/src/status/handlers.ts +5 -3
  309. package/examples/notes-service/src/stdio.ts +7 -3
  310. package/examples/notes-service/src/worker.ts +6 -3
  311. package/examples/notes-service/test/commands.test.ts +164 -91
  312. package/examples/notes-service/test/notes/as.ts +3 -1
  313. package/examples/notes-service/test/notes/idempotency.test.ts +97 -67
  314. package/examples/notes-service/test/notes/in-process.test.ts +26 -15
  315. package/examples/notes-service/test/notes/notes.test.ts +49 -31
  316. package/examples/notes-service/test/routes.test.ts +106 -71
  317. package/examples/notes-service/test/worker.test.ts +21 -13
  318. package/examples/quickstart/README.md +2 -6
  319. package/examples/quickstart/src/Api.ts +1 -0
  320. package/examples/quickstart/src/client.ts +6 -3
  321. package/examples/quickstart/src/greeting/Greeting.ts +1 -1
  322. package/examples/quickstart/src/greeting/handlers.ts +5 -2
  323. package/examples/quickstart/src/routes.ts +3 -2
  324. package/examples/quickstart/src/server.ts +6 -2
  325. package/examples/quickstart/test/routes.test.ts +3 -2
  326. package/package.json +19 -17
@@ -1,37 +1,90 @@
1
1
  import { Anyone } from "./contract/caller.mjs";
2
- import { bindingErrors, bindingOf } from "./contract/declared.mjs";
3
- import { acquire, provideHandlers, servingOf } from "./contract/implementation.mjs";
2
+ import { InvalidInput, Unauthenticated } from "./contract/errors.mjs";
3
+ import { bindingErrors, bindingOf, eventErrors } from "./contract/declared.mjs";
4
+ import { reservedList, reservedNames } from "./contract/names.mjs";
5
+ import { Progressed, answerFrom, elementsFrom } from "./contract/progress.mjs";
6
+ import { elementOf } from "./contract/stream.mjs";
7
+ import { acquire, elementsOf } from "./contract/implementation.mjs";
8
+ import { checked, checkedStream, decodeInput, inputParseOptions, sendingJson, watchInput } from "./contract/call.mjs";
9
+ import { concealed } from "./contract/reported.mjs";
10
+ import { acquireFeeds } from "./contract/feed.mjs";
11
+ import { PushedOf, Subscription, SubscriptionOf, queuedPush } from "./contract/push.mjs";
4
12
  import { providerOf } from "./authentication/provider.mjs";
5
- import { checked, decodeInput } from "./contract/call.mjs";
6
- import { Cause, Effect, ErrorReporter, Layer, Predicate, Result, Schema, SchemaTransformation } from "effect";
13
+ import { listed } from "./contract/listed.mjs";
14
+ import { provideServed, servingOf } from "./contract/serving.mjs";
15
+ import { watching } from "./contract/watch.mjs";
16
+ import { transportOf } from "./rpc/watch.mjs";
17
+ import { Cause, Effect, ErrorReporter, Layer, Match, Option, Predicate, Queue, Result, Schedule, Schema, SchemaTransformation, Stream } from "effect";
7
18
  import { HttpClientRequest, HttpServerRequest } from "effect/http";
8
19
  import { HttpApiBuilder } from "effect/http-api";
9
- import { Rpc, RpcClient, RpcGroup, RpcSerialization, RpcServer } from "effect/rpc";
10
- //#region src/rpc/ActionRpc.ts
11
- function make(actions, options = {}) {
12
- const bound = bindingOf("ActionRpc binding", actions, options);
13
- const group = RpcGroup.make(...actions.map((action) => Rpc.make(action.name, {
20
+ import { Rpc, RpcClient, RpcGroup, RpcMiddleware, RpcSchema, RpcServer } from "effect/rpc";
21
+ // src/rpc/ActionRpc.ts
22
+ /**
23
+ * What the stream rpc of an action declaring `progress` sends: a `Progress` for each value its
24
+ * handler reports, then a `Final` with its answer.
25
+ */
26
+ const ProgressedOf = (progress, success) => Schema.Union([Schema.TaggedStruct("Progress", { value: progress }), Schema.TaggedStruct("Final", { value: success })]);
27
+ const elementedOf = (element, progress) => Schema.Union([Schema.TaggedStruct("Element", { value: element }), ...progress === void 0 ? [] : [Schema.TaggedStruct("Progress", { value: progress })]]);
28
+ /** Whether `action`'s rpc is a stream rpc: it answers a stream, or declares `progress`. */
29
+ const streams = (action) => action.progress !== void 0 || elementOf(action.success) !== void 0;
30
+ /** What `action`'s rpc answers, or each element it streams where it is a stream rpc. */
31
+ const sentOf = (action) => {
32
+ const element = elementOf(action.success);
33
+ if (element !== void 0) return elementedOf(element, action.progress);
34
+ return action.progress === void 0 ? action.success : ProgressedOf(action.progress, action.success);
35
+ };
36
+ const authenticatedKey = "effect-actions/ActionRpc/Authenticated";
37
+ const nativeOf = (group) => group;
38
+ const authenticatedOf = (authentication) => RpcMiddleware.Service()(authenticatedKey, { error: Schema.Union([Unauthenticated, ...authentication.error]) });
39
+ /** The middleware authenticating `rpc`, wherever among its middleware it runs. */
40
+ const authenticatedIn = (rpc) => [...rpc.middlewares].find(({ key }) => key === authenticatedKey);
41
+ function make(contracts, options = {}) {
42
+ const { actions, events } = listed(contracts);
43
+ const bound = bindingOf("ActionRpc binding", actions, events, options);
44
+ const rpcOf = (action) => Rpc.make(action.name, {
14
45
  payload: action.input,
15
- success: action.success,
16
- error: Schema.Union(bindingErrors(action, bound))
17
- })));
46
+ success: sentOf(action),
47
+ error: Schema.Union(bindingErrors(action, bound)),
48
+ stream: streams(action)
49
+ });
50
+ const given = options.rpcs === void 0 ? RpcGroup.make() : nativeOf(options.rpcs);
51
+ for (const tag of given.requests.keys()) if (reservedNames.includes(tag) || contracts.some((contract) => contract.name === tag)) throw new Error(`ActionRpc binding: the rpc "${tag}" of rpcs is named after an action, an event type or a name the client keeps, ${reservedList} (rename it)`);
52
+ const rpcs = bound.authentication === void 0 ? given : given.middleware(authenticatedOf(bound.authentication));
53
+ const group = RpcGroup.make(...actions.map(rpcOf), ...events.map((event) => Rpc.make(event.name, {
54
+ payload: SubscriptionOf(event),
55
+ success: PushedOf(event),
56
+ error: Schema.Union(eventErrors(event, bound)),
57
+ stream: true
58
+ }))).merge(rpcs);
18
59
  return {
19
60
  actions,
61
+ events,
20
62
  ...bound,
63
+ rpcs,
21
64
  group
22
65
  };
23
66
  }
24
- const credentialFromRpcHeaders = (security, headers) => HttpApiBuilder.securityDecode(security).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromClientRequest(HttpClientRequest.get("/", { headers }))), Effect.provideService(HttpServerRequest.ParsedSearchParams, {}));
25
- const authenticationStepOf = (authentication) => Effect.map(providerOf(authentication), (provider) => (effect, { headers }) => credentialFromRpcHeaders(authentication.security, headers).pipe(Effect.flatMap(provider.authenticate), Effect.flatMap((actor) => Effect.provideService(effect, authentication.identity, actor))));
26
- var InternalError = class extends Error {
27
- [ErrorReporter.ignore] = true;
28
- constructor() {
29
- super("Internal server error");
67
+ const withRequestOf = (headers) => (effect) => effect.pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, HttpServerRequest.fromClientRequest(HttpClientRequest.get("/", { headers }))), Effect.provideService(HttpServerRequest.ParsedSearchParams, {}));
68
+ /** `call` inside `steps`, the first innermost, each given the call's options. */
69
+ const throughSteps = (steps, call, options) => {
70
+ let wrapped = call;
71
+ for (const step of steps) {
72
+ const inner = wrapped;
73
+ wrapped = Effect.suspend(() => step(inner, options));
30
74
  }
75
+ return wrapped;
31
76
  };
77
+ /**
78
+ * The step authenticating a call with `authentication`'s provider: the identity its verification
79
+ * answers is provided to the call, the verification run as `verifying` runs it.
80
+ */
81
+ const authenticatingOf = (authentication) => Effect.map(providerOf(authentication), (provider) => {
82
+ const verify = Effect.flatMap(HttpApiBuilder.securityDecode(authentication.security), provider.authenticate);
83
+ return (verifying) => (effect, options) => Effect.flatMap(verifying(verify, options), (actor) => Effect.provideService(effect, authentication.identity, actor));
84
+ });
32
85
  var DeclaredFailure = class {
33
- json;
34
86
  [ErrorReporter.ignore] = true;
87
+ json;
35
88
  constructor(json) {
36
89
  this.json = json;
37
90
  }
@@ -39,18 +92,32 @@ var DeclaredFailure = class {
39
92
  return Predicate.isString(this.json) ? this.json : JSON.stringify(this.json);
40
93
  }
41
94
  };
42
- const DeclaredFromJson = Schema.Json.pipe(Schema.decodeTo(Schema.instanceOf(DeclaredFailure), SchemaTransformation.transform({
95
+ const asJson = () => Schema.link()(Schema.Json, SchemaTransformation.transform({
43
96
  decode: (json) => new DeclaredFailure(json),
44
97
  encode: (failure) => failure.json
45
- })));
98
+ }));
99
+ const Declared = Schema.instanceOf(DeclaredFailure, {
100
+ toCodecJson: asJson,
101
+ toCodecIso: asJson
102
+ });
103
+ const streamOf = (name) => Rpc.make(name, {
104
+ payload: Schema.Json,
105
+ success: Schema.Json,
106
+ error: Declared,
107
+ stream: true
108
+ });
109
+ const erasedOf = (actions, events) => RpcGroup.make(...actions.map((action) => streams(action) ? streamOf(action.name) : Rpc.make(action.name, {
110
+ payload: Schema.Json,
111
+ success: Schema.Json,
112
+ error: Declared
113
+ })), ...events.map((event) => streamOf(event.name)));
46
114
  const callBoundaryOf = (error) => {
47
115
  const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(error));
48
- const internal = (cause) => Effect.logError(cause).pipe(Effect.andThen(ErrorReporter.report(cause)), Effect.andThen(Effect.die(new InternalError())));
49
116
  return (effect) => Effect.catchCause(effect, (cause) => {
50
- if (Cause.hasDies(cause)) return internal(cause);
117
+ if (Cause.hasDies(cause)) return concealed(cause);
51
118
  const failure = Cause.findFail(cause);
52
119
  return Result.isSuccess(failure) ? encode(failure.success.error).pipe(Effect.matchCauseEffect({
53
- onFailure: (unencoded) => internal(Cause.combine(cause, unencoded)),
120
+ onFailure: (unencoded) => concealed(Cause.combine(cause, unencoded)),
54
121
  onSuccess: (encoded) => Effect.fail(new DeclaredFailure(encoded))
55
122
  })) : Effect.failCause(cause);
56
123
  });
@@ -61,42 +128,227 @@ const handlerOf = (action, run, steps, error) => {
61
128
  const boundary = callBoundaryOf(error);
62
129
  return (payload, options) => {
63
130
  const call = Effect.suspend(() => decode(payload)).pipe(Effect.flatMap(run), Effect.flatMap(encode));
64
- const wrapped = steps.reduce((inner, step) => Effect.suspend(() => step(inner, {
131
+ const wrapped = throughSteps(steps, call, {
132
+ ...options,
133
+ payload
134
+ });
135
+ return boundary(wrapped);
136
+ };
137
+ };
138
+ /**
139
+ * The handler of the stream rpc of an action declaring `progress` or answering a stream: its call
140
+ * authenticates before its stream starts, then runs in the call's scope, what `sending` sends sent
141
+ * one at a time, so a caller reading slowly holds up the call, whose progress then drops the
142
+ * oldest values. A failure ends the stream as a call's ends it.
143
+ */
144
+ const streamingHandlerOf = (action, sending, steps, error) => {
145
+ const decode = decodeInput(action);
146
+ const boundary = callBoundaryOf(error);
147
+ const encodeProgress = Schema.encodeUnknownEffect(Schema.toCodecJson(action.progress ?? Schema.Never));
148
+ return (payload, options) => {
149
+ const call = Effect.gen(function* () {
150
+ const input = yield* decode(payload);
151
+ const sent = yield* Queue.bounded(1);
152
+ const send = (each) => Effect.asVoid(Queue.offer(sent, each));
153
+ yield* sending(input, { progress: (value) => Effect.flatMap(Effect.orDie(encodeProgress(value)), (json) => send(Progressed.Progress({ value: json }))) }, send).pipe(boundary, Queue.into(sent), Effect.forkScoped({ startImmediately: true }));
154
+ return sent;
155
+ });
156
+ const wrapped = throughSteps(steps, call, {
157
+ ...options,
158
+ payload
159
+ });
160
+ return boundary(wrapped);
161
+ };
162
+ };
163
+ /** Each value `run` reports sent as a `Progress`, then its answer as a `Final`. */
164
+ const sendingAnswer = (success, run) => {
165
+ const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(success));
166
+ return (input, reporting, send) => run(input, reporting).pipe(Effect.flatMap(encode), Effect.flatMap((value) => send(Progressed.Final({ value }))));
167
+ };
168
+ /** Each element of the stream `run` answers sent as an `Element`, beside its progress. */
169
+ const sendingElements = (element, run) => {
170
+ const encode = Schema.encodeUnknownEffect(Schema.toCodecJson(element));
171
+ return (input, reporting, send) => Stream.runForEach(elementsOf(run(input, reporting)), (each) => Effect.flatMap(encode(each), (value) => send(Progressed.Element({ value }))));
172
+ };
173
+ /** The key a protocol sets to its `SubscriptionKeeper` on its `RpcServer.Protocol` service. */
174
+ const subscriptionKeeper = Symbol.for("effect-actions/ActionRpc/subscriptionKeeper");
175
+ const keeperOf = (protocol) => {
176
+ return Predicate.hasProperty(protocol, subscriptionKeeper) ? protocol[subscriptionKeeper] : void 0;
177
+ };
178
+ /**
179
+ * The handler of one event type's stream rpc: the subscription's request authenticates and is
180
+ * admitted before its stream starts, so a refusal fails the call; the feed is then read from the
181
+ * call, in its scope, into a bounded queue, each element encoded, and read on through a source's
182
+ * failure, before its first `Checked` too. A later refusal ends the stream as a declared failure.
183
+ * Over a protocol with a `keeper`, the admitted subscription is kept, and every position sent.
184
+ */
185
+ const subscriptionHandlerOf = (feed, steps, error, keeper) => {
186
+ const decode = Schema.decodeUnknownEffect(Subscription, inputParseOptions);
187
+ const decodeEventInput = decodeInput(feed.event);
188
+ const sent = Schema.toCodecJson(PushedOf(feed.event));
189
+ const encode = Schema.encodeEffect(sent);
190
+ const decodeSent = Schema.decodeUnknownOption(sent);
191
+ const boundary = callBoundaryOf(error);
192
+ return (payload, options) => {
193
+ const call = Effect.gen(function* () {
194
+ const { input, cursor } = yield* Effect.mapError(decode(payload), InvalidInput.fromSchemaError);
195
+ const open = yield* feed.stream(yield* decodeEventInput(input));
196
+ if (keeper === void 0) return yield* queuedPush(open, cursor, encode, { ending: boundary });
197
+ yield* keeper.keep({
198
+ clientId: options.client.id,
199
+ requestId: options.requestId,
200
+ tag: feed.event.name,
201
+ payload,
202
+ headers: Object.entries(options.headers),
203
+ resumeFrom: (values) => {
204
+ const reached = values.flatMap((value) => Option.toArray(decodeSent(value))).map((pushed) => Match.value(pushed).pipe(Match.tag("Failed", () => null), Match.orElse((positioned) => positioned.cursor))).findLast(Predicate.isNotNull);
205
+ return reached === void 0 ? void 0 : {
206
+ input,
207
+ cursor: reached
208
+ };
209
+ }
210
+ });
211
+ return yield* queuedPush(open, cursor, encode, {
212
+ ending: boundary,
213
+ inOrder: true
214
+ });
215
+ });
216
+ const wrapped = throughSteps(steps, call, {
65
217
  ...options,
66
218
  payload
67
- })), call);
219
+ });
68
220
  return boundary(wrapped);
69
221
  };
70
222
  };
223
+ /**
224
+ * The key a protocol answering each call once sets to `true` on its `RpcServer.Protocol`
225
+ * service, as the Cloudflare package's stub protocol does, since Effect's protocol has no flag of
226
+ * its own for it: `layer` refuses to serve a streaming action, an event type or a native stream
227
+ * rpc on it, where a stream is answered with a defect at its first element and one with none
228
+ * waits for good.
229
+ */
230
+ const unaryProtocol = Symbol.for("effect-actions/ActionRpc/unary");
231
+ const verifyingNative = (verify, { headers }) => withRequestOf(headers)(verify).pipe(Effect.catchCauseIf(Cause.hasDies, concealed));
232
+ const requested = (effect, { headers }) => withRequestOf(headers)(effect);
233
+ const isUnary = (protocol) => Predicate.hasProperty(protocol, unaryProtocol) && protocol[unaryProtocol] === true;
71
234
  function layer(binding, served, options = {}) {
72
- const { apps, actions, authentication } = servingOf("RPC binding", binding, served, options);
235
+ const selected = servingOf("RPC binding", binding, served, options);
236
+ const { apps, actions, events, eventTypes } = selected;
73
237
  const middleware = options.middleware ?? [];
74
- const jsonGroup = RpcGroup.make(...actions.map((action) => Rpc.make(action.name, {
75
- payload: Schema.Json,
76
- success: Schema.Json,
77
- error: DeclaredFromJson
78
- })));
238
+ const jsonGroup = erasedOf(actions, eventTypes);
239
+ const natives = [...nativeOf(binding.rpcs).requests.values()];
240
+ const authentication = natives.length === 0 ? selected.authentication : binding.authentication;
79
241
  const handlers = Layer.unwrap(Effect.gen(function* () {
80
- const { codecFor } = yield* RpcServer.Protocol;
81
- if (codecFor !== RpcSerialization.json.codecFor) return yield* Effect.die(/* @__PURE__ */ new Error("ActionRpc serves a protocol speaking JSON: RpcSerialization.layerJson, layerNdjson, layerJsonRpc or layerNdJsonRpc, not another codec, such as schema-binary's"));
242
+ const unauthenticated = authentication === void 0 ? void 0 : natives.find((rpc) => authenticatedIn(rpc) === void 0);
243
+ if (unauthenticated !== void 0) return yield* Effect.die(/* @__PURE__ */ new Error(`ActionRpc.layer: the native rpc ${unauthenticated._tag} of a protected binding runs without its authentication: add it to the group ActionRpc.make is given as its rpcs, not to the binding's rpcs after`));
244
+ const protocol = yield* RpcServer.Protocol;
245
+ const keeper = keeperOf(protocol);
246
+ if (isUnary(protocol)) {
247
+ const streamed = [...actions.filter(streams), ...eventTypes].map(({ name }) => name);
248
+ const nativeStreams = natives.flatMap((rpc) => RpcSchema.isStreamSchema(rpc.successSchema) ? [rpc._tag] : []);
249
+ if (streamed.length + nativeStreams.length > 0) return yield* Effect.die(/* @__PURE__ */ new Error(`ActionRpc.layer serves a protocol answering each call once, and ${[...streamed, ...nativeStreams].join(", ")} stream: serve them over a socket or HTTP, and on this protocol the others, ${nativeStreams.length > 0 ? "from a binding whose rpcs do not stream, " : ""}listed in actions`));
250
+ }
82
251
  const listedMiddleware = yield* Effect.forEach(middleware, (key) => Effect.service(key));
83
- const authenticate = authentication === void 0 ? void 0 : yield* authenticationStepOf(authentication);
84
- const bound = yield* acquire(apps);
85
- const byName = Object.fromEntries(bound.map(([action, run]) => [action.name, handlerOf(action, run, action.caller === Anyone || authenticate === void 0 ? listedMiddleware : [...listedMiddleware, authenticate], Schema.Union(bindingErrors(action, binding)))]));
86
- return jsonGroup.toLayer(byName);
87
- })).pipe(provideHandlers(apps));
88
- return RpcServer.layer(jsonGroup, { disableFatalDefects: true }).pipe(Layer.provide(handlers));
252
+ const authenticating = authentication === void 0 ? void 0 : yield* authenticatingOf(authentication);
253
+ const bound = yield* acquire(apps, authentication);
254
+ const feeds = yield* acquireFeeds(events);
255
+ const stepsOf = (caller) => caller === Anyone || authenticating === void 0 ? [...listedMiddleware, requested] : [
256
+ ...listedMiddleware,
257
+ authenticating((verify) => verify),
258
+ requested
259
+ ];
260
+ const handlerFor = (action, run, steps, error) => {
261
+ const element = elementOf(action.success);
262
+ if (element !== void 0) return streamingHandlerOf(action, sendingElements(element, run), steps, error);
263
+ return action.progress === void 0 ? handlerOf(action, run, steps, error) : streamingHandlerOf(action, sendingAnswer(action.success, run), steps, error);
264
+ };
265
+ const byName = {
266
+ ...Object.fromEntries(bound.map(([action, run]) => [action.name, handlerFor(action, run, stepsOf(action.caller), Schema.Union(bindingErrors(action, binding)))])),
267
+ ...Object.fromEntries(feeds.map((feed) => [feed.event.name, subscriptionHandlerOf(feed, stepsOf(feed.event.caller), Schema.Union(eventErrors(feed.event, binding)), keeper)]))
268
+ };
269
+ const servedActions = jsonGroup.toLayer(byName);
270
+ const authenticated = natives.map(authenticatedIn).find(Predicate.isNotUndefined);
271
+ if (authenticated === void 0 || authenticating === void 0) return servedActions;
272
+ const authenticate = authenticating(verifyingNative);
273
+ return Layer.merge(servedActions, Layer.succeed(authenticated)(authenticate));
274
+ })).pipe(provideServed(selected));
275
+ return RpcServer.layer(jsonGroup.merge(binding.rpcs), { disableFatalDefects: true }).pipe(Layer.provide(handlers));
89
276
  }
277
+ const isDeclared = (failure) => failure instanceof DeclaredFailure;
278
+ /** Fail with the declared failure a wrapper holds, decoded by `error`, or die where it does not. */
279
+ const failedOf = (error) => {
280
+ const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(error));
281
+ return (failure) => Effect.flatMap(Effect.orDie(decode(failure.json)), Effect.fail);
282
+ };
283
+ /**
284
+ * A client's method of `action`, calling `method`, its rpc of `erasedOf`, with its input's JSON:
285
+ * what the server sends, its answer or each element it streams, and its declared failure, decoded
286
+ * by the action's schemas; one that does not decode is a defect, as the native client makes it.
287
+ */
288
+ const actionMethodOf = (action, binding, method) => {
289
+ const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(sentOf(action)));
290
+ const failed = failedOf(Schema.Union(bindingErrors(action, binding)));
291
+ const sent = (json) => Effect.orDie(decode(json));
292
+ if (!streams(action)) {
293
+ const call = method;
294
+ return checked(action, sendingJson, (json) => call(json).pipe(Effect.catchIf(isDeclared, failed), Effect.flatMap(sent)));
295
+ }
296
+ const call = method;
297
+ const progressed = (json) => call(json).pipe(Stream.catchIf(isDeclared, (failure) => Stream.fromEffect(failed(failure))), Stream.mapEffect(sent));
298
+ return elementOf(action.success) === void 0 ? checked(action, sendingJson, (json, onProgress) => answerFrom(progressed(json), onProgress)) : checkedStream(action, sendingJson, (json, onProgress) => elementsFrom(progressed(json), onProgress));
299
+ };
300
+ /**
301
+ * A client's subscription to `event`, calling `method`, its stream rpc of `erasedOf`, with its
302
+ * input's JSON and the cursor it resumes from: what the server sends, and its declared failure,
303
+ * decoded by the event's schemas; one that does not decode is a defect, as for an action.
304
+ */
305
+ const eventMethodOf = (event, binding, method) => {
306
+ const decode = Schema.decodeUnknownEffect(Schema.toCodecJson(PushedOf(event)));
307
+ const failed = failedOf(Schema.Union(eventErrors(event, binding)));
308
+ const call = method;
309
+ return (input) => (cursor) => call({
310
+ input,
311
+ cursor
312
+ }).pipe(Stream.catchIf(isDeclared, (failure) => Stream.fromEffect(failed(failure))), Stream.mapEffect((json) => Effect.orDie(decode(json))));
313
+ };
90
314
  function client(binding, options) {
91
- const group = binding.group;
92
- return Effect.map(RpcClient.make(group, options), (client) => {
93
- const native = client;
94
- return Object.fromEntries(binding.actions.map((action) => {
95
- const method = native[action.name];
96
- if (method === void 0) throw new Error(`No client method for ${action.name} (use the binding \`ActionRpc.make\` returned, whose group holds this action's rpc)`);
97
- return [action.name, checked(action, (payload) => method(payload))];
315
+ const actions = new Map(binding.actions.map((action) => [action.name, action]));
316
+ const events = new Map(binding.events.map((event) => [event.name, event]));
317
+ const clients = Effect.all([RpcClient.make(erasedOf(binding.actions, binding.events).merge(nativeOf(binding.rpcs)), options), Effect.map(Effect.service(RpcClient.Protocol), ({ supportsAck }) => !supportsAck)]);
318
+ return Effect.map(clients, ([native, silenceEnds]) => {
319
+ const rpcs = Object.entries(native);
320
+ const methods = Object.fromEntries(rpcs.filter(([tag]) => !events.has(tag)).map(([tag, method]) => {
321
+ const action = actions.get(tag);
322
+ return [tag, action === void 0 ? method : actionMethodOf(action, binding, method)];
323
+ }));
324
+ if (events.size === 0) return methods;
325
+ const feeds = new Map(rpcs.flatMap(([tag, method]) => {
326
+ const event = events.get(tag);
327
+ return event === void 0 ? [] : [[tag, eventMethodOf(event, binding, method)]];
98
328
  }));
329
+ const watch = (event, input, watchOptions) => {
330
+ const feed = feeds.get(event.name);
331
+ if (feed === void 0) return Stream.die(/* @__PURE__ */ new Error(`ActionRpc.client: the binding holds no event type ${event.name} (watch one the binding ActionRpc.make returned lists)`));
332
+ return Stream.unwrap(Effect.map(watchInput(event, input), ({ json }) => watching(transportOf(event, feed(json), silenceEnds), watchOptions)));
333
+ };
334
+ return {
335
+ ...methods,
336
+ watch
337
+ };
99
338
  });
100
339
  }
101
- //#endregion
102
- export { client, layer, make };
340
+ /**
341
+ * A socket client's `retryPolicy`, for `RpcClient.layerProtocolSocket`, that stops connecting
342
+ * again once the server closes the socket with one of `codes`, such as a refusal of its upgrade:
343
+ * until then it is Effect's own default, `Schedule.min([Schedule.exponential(500, 1.5),
344
+ * Schedule.spaced(5000)])`, which Effect does not export. A page refused with its code stops, its
345
+ * calls and streams failing with an `RpcClientError` whose `reason` is that `SocketCloseError`,
346
+ * its `code` the one it closed with.
347
+ *
348
+ * **Gotchas**
349
+ *
350
+ * - A client without it connects again after a refusal as after any close, every five seconds for good.
351
+ * - Any other socket error, a drop included, is retried as Effect's default retries it.
352
+ */
353
+ const reconnectUntil = (...codes) => Schedule.min([Schedule.exponential(500, 1.5), Schedule.spaced(5e3)]).pipe(Schedule.setInputType(), Schedule.while(({ input: { reason } }) => !(Predicate.isTagged(reason, "SocketCloseError") && Predicate.hasProperty(reason, "code") && codes.some((code) => code === reason.code))));
354
+ export { client, layer, make, reconnectUntil, subscriptionKeeper, unaryProtocol };
@@ -1,12 +1,13 @@
1
+ import { Codec } from "./contract/codec.mjs";
1
2
  import { BuiltIns } from "./contract/errors.mjs";
2
- import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Offered, OptionalUnless, RequestOf, SelectedOf, Served, Serving } from "./contract/implementation.mjs";
3
+ import { ActionOf, BuildError, BuildServices, Holding, Known, Member, Offered, OptionalUnless, RequestOf, SelectedOf, ServedActions, Serving } from "./contract/implementation.mjs";
4
+ import { ToolSuccess } from "./contract/stream.mjs";
3
5
  import { Any } from "./Action.mjs";
4
6
  import { Effect, Layer, Schema } from "effect";
5
7
  import { Prompt, Tool, Toolkit } from "effect/ai";
6
- //#region src/toolkit/ActionToolkit.d.ts
7
8
  type NativeTool<A extends Any, R> = Tool.Tool<A["name"], {
8
9
  readonly parameters: Schema.toCodecJson<A["input"]>;
9
- readonly success: Schema.toCodecJson<A["success"]>;
10
+ readonly success: A["progress"] extends Codec ? Schema.toCodecJson<Schema.Union<readonly [ProgressOf<A["progress"]>, ToolSuccess<A["success"]>]>> : Schema.toCodecJson<ToolSuccess<A["success"]>>;
10
11
  readonly failure: Schema.toCodecJson<Schema.Union<ReadonlyArray<A["error"][number] | BuiltIns>>>;
11
12
  readonly failureMode: "return";
12
13
  }, R>;
@@ -48,7 +49,7 @@ export interface Options<A extends Any> {
48
49
  * Each keeps its implementation's authorization and builder. Defaults to every action of them,
49
50
  * one added to an implementation later included.
50
51
  */
51
- readonly actions?: ReadonlyArray<A> | undefined;
52
+ readonly actions?: readonly A[] | undefined;
52
53
  /**
53
54
  * Whether a model's call needs approval before it runs: a boolean, or an Effect of one, for
54
55
  * each call, with Effect's native approval context. `LanguageModel` asks for approval
@@ -64,6 +65,19 @@ export interface Options<A extends Any> {
64
65
  */
65
66
  readonly needsApproval?: (call: ToolCall<A>, context: Tool.NeedsApprovalContext) => boolean | Effect.Effect<boolean, unknown>;
66
67
  }
68
+ /** A value an action reported, as a toolkit's preliminary result holds it. */
69
+ declare class Reported<A> {
70
+ readonly _tag = "Progress";
71
+ readonly value: A;
72
+ constructor(value: A);
73
+ }
74
+ /** A preliminary result of a tool whose action declares `progress`: one value it reported. */
75
+ declare const ProgressOf: (progress: Codec) => Schema.decodeTo<Schema.declare<Reported<unknown>, Reported<unknown>>, Schema.TaggedStruct<"Progress", {
76
+ readonly value: Codec;
77
+ }>, never, never>;
78
+ type ProgressOf<P> = P extends Codec ? Schema.decodeTo<Schema.declare<Reported<P["Type"]>>, Schema.TaggedStruct<"Progress", {
79
+ readonly value: P;
80
+ }>> : never;
67
81
  /**
68
82
  * Project implementations into Effect's native AI toolkit: one tool per action, keyed by its
69
83
  * name, of the listed `actions` or, without them, of every action of the implementations.
@@ -74,11 +88,13 @@ export interface Options<A extends Any> {
74
88
  *
75
89
  * **Gotchas**
76
90
  *
91
+ * - An action answering a stream, `Action.Stream`, answers its elements collected into one array once the stream ends, its tool's success that array.
92
+ * - An action declaring `progress` streams each value as a result with `preliminary: true` before its success, its `result` `{ _tag: "Progress", value }`, so its tool's success schema is that or `success`; a model reads the success alone.
77
93
  * - A success is a result with `isFailure: false`, `result` the value and `encodedResult` its JSON; a declared error a result with `isFailure: true` and `result` the error, which the model reads, never a raised failure; a refusal as `Action.Unauthenticated` and `Action.Forbidden` say.
78
94
  * - Arguments that do not decode are a result with `isFailure: true` whose `result` is an `AiError` with `reason` a `ToolParameterValidationError`, never `InvalidInput`, and neither `authorize` nor the handler runs; undeclared arguments are dropped and the call runs.
79
95
  * - A defect, or a failure the action's errors reject, is the call's defect; a success or an error that does not encode fails the call with an `AiError`, `Failed to encode result for tool …`.
80
96
  */
81
- export declare function make<const Apps extends Served, const O extends Options<ActionOf<Member<Apps>>> = {}>(implementations: Apps, ...options: OptionalUnless<O, Options<ActionOf<Member<Apps>>> & O & NoInfer<Known<O, Options<Any>>>>): Tools<{ readonly [T in ToolFor<Member<Apps>, Offered<O, ActionOf<Member<Apps>>>> as T["name"]]: T; }, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>>;
97
+ export declare function make<const Apps extends ServedActions, const O extends Options<ActionOf<Member<Apps>>> = {}>(implementations: Apps, ...options: OptionalUnless<O, Options<ActionOf<Member<Apps>>> & O & NoInfer<Known<O, Options<Any>>>>): Tools<{ readonly [T in ToolFor<Member<Apps>, Offered<O, ActionOf<Member<Apps>>>> as T["name"]]: T; }, BuildError<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>, BuildServices<Holding<Member<Apps>, SelectedOf<O, Apps>>, SelectedOf<O, Apps>>>;
82
98
  /** A tool call a model asked to have approved, which no answer has resolved yet. */
83
99
  export interface Approval {
84
100
  /** What a `tool-approval-response` names to answer it. */
@@ -103,6 +119,5 @@ export interface Approval {
103
119
  * Guide: `docs/ActionToolkit.md` in this package; in an application:
104
120
  * `examples/kitchen-sink/src/assistant/handlers.ts`.
105
121
  */
106
- export declare const openApprovals: ({ content: messages }: Prompt.Prompt) => ReadonlyArray<Approval>;
107
- //#endregion
122
+ export declare const openApprovals: ({ content: messages }: Prompt.Prompt) => readonly Approval[];
108
123
  export {};
@@ -1,10 +1,12 @@
1
- import { assertOnce, heldOnce } from "./contract/names.mjs";
2
1
  import { projectedErrors } from "./contract/declared.mjs";
2
+ import { assertOnce, heldOnce } from "./contract/names.mjs";
3
+ import { progressSink } from "./contract/progress.mjs";
4
+ import { toolSuccessOf } from "./contract/stream.mjs";
5
+ import { Implementation, acquire, answeringWhole, provideHandlers, select, toList } from "./contract/implementation.mjs";
3
6
  import { hintsOf } from "./contract/hints.mjs";
4
- import { Implementation, acquire, provideHandlers, select, toList } from "./contract/implementation.mjs";
5
- import { Effect, Schema } from "effect";
7
+ import { Effect, Schema, SchemaGetter } from "effect";
6
8
  import { Tool, Toolkit } from "effect/ai";
7
- //#region src/toolkit/ActionToolkit.ts
9
+ // src/toolkit/ActionToolkit.ts
8
10
  const annotateIfGiven = (tool, key, value) => value === void 0 ? tool : tool.annotate(key, value);
9
11
  const annotateFromContract = (tool, action) => {
10
12
  const { readOnlyHint, destructiveHint, idempotentHint, openWorldHint } = hintsOf(action);
@@ -12,13 +14,32 @@ const annotateFromContract = (tool, action) => {
12
14
  const hinted = tool.annotate(Tool.Readonly, readOnlyHint).annotate(Tool.Destructive, destructiveHint).annotate(Tool.Idempotent, idempotentHint).annotate(Tool.OpenWorld, openWorldHint);
13
15
  return annotateIfGiven(annotateIfGiven(hinted, Tool.Title, title), Tool.Meta, _meta === void 0 ? void 0 : { ..._meta });
14
16
  };
15
- const jsonToolOf = (action) => Tool.make(action.name, {
16
- description: action.description,
17
- parameters: Schema.toCodecJson(action.input),
18
- success: Schema.toCodecJson(action.success),
19
- failure: Schema.toCodecJson(Schema.Union(projectedErrors(action))),
20
- failureMode: "return"
21
- });
17
+ /** A value an action reported, as a toolkit's preliminary result holds it. */
18
+ var Reported = class {
19
+ _tag = "Progress";
20
+ value;
21
+ constructor(value) {
22
+ this.value = value;
23
+ }
24
+ };
25
+ /** A preliminary result of a tool whose action declares `progress`: one value it reported. */
26
+ const ProgressOf = (progress) => Schema.TaggedStruct("Progress", { value: progress }).pipe(Schema.decodeTo(Schema.declare((input) => input instanceof Reported), {
27
+ decode: SchemaGetter.transform(({ value }) => new Reported(value)),
28
+ encode: SchemaGetter.transform((reported) => ({
29
+ _tag: "Progress",
30
+ value: reported.value
31
+ }))
32
+ }));
33
+ const jsonToolOf = (action) => {
34
+ const success = toolSuccessOf(action.success);
35
+ return Tool.make(action.name, {
36
+ description: action.description,
37
+ parameters: Schema.toCodecJson(action.input),
38
+ success: Schema.toCodecJson(action.progress === void 0 ? success : Schema.Union([ProgressOf(action.progress), success])),
39
+ failure: Schema.toCodecJson(Schema.Union(projectedErrors(action))),
40
+ failureMode: "return"
41
+ });
42
+ };
22
43
  function make(apps, options) {
23
44
  const served = select(toList(apps), options?.actions);
24
45
  const needsApproval = options?.needsApproval;
@@ -29,12 +50,15 @@ function make(apps, options) {
29
50
  action,
30
51
  input
31
52
  }, context);
32
- return Effect.isEffect(decided) ? Effect.catch(decided, () => Effect.succeed(true)) : decided;
53
+ return Effect.isEffect(decided) ? Effect.orElseSucceed(decided, () => true) : decided;
33
54
  }), { id: `${Implementation.runKey(app)}/${action.name}` });
34
55
  const toolkit = Toolkit.make(...served.flatMap((app) => app.actions.map((action) => annotateFromContract(toolOf(action, app), action))));
35
56
  return {
36
57
  toolkit,
37
- layer: toolkit.toLayer(Effect.map(acquire(served), (bound) => Object.fromEntries(bound.map(([action, run]) => [action.name, run])))).pipe(provideHandlers(served))
58
+ layer: toolkit.toLayer(Effect.map(acquire(served, void 0), (bound) => Object.fromEntries(bound.map(([action, handler]) => {
59
+ const run = answeringWhole(action, handler);
60
+ return [action.name, (input, { preliminary }) => run(input, progressSink((value) => preliminary(new Reported(value))))];
61
+ })))).pipe(provideHandlers(served))
38
62
  };
39
63
  }
40
64
  /**
@@ -64,5 +88,4 @@ const openApprovals = ({ content: messages }) => {
64
88
  }];
65
89
  });
66
90
  };
67
- //#endregion
68
91
  export { make, openApprovals };