@swirls/looms 0.0.2 → 0.2.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 (375) hide show
  1. package/CHANGELOG.md +24 -2
  2. package/dist/actor/cell.d.ts +9 -4
  3. package/dist/actor/cell.js +7 -11
  4. package/dist/actor/host.d.ts +7 -3
  5. package/dist/actor/host.js +57 -33
  6. package/dist/actor/index.d.ts +1 -1
  7. package/dist/actor/index.js +1 -1
  8. package/dist/actor/routing.d.ts +12 -1
  9. package/dist/actor/routing.js +57 -51
  10. package/dist/agent/agent-session.d.ts +5 -0
  11. package/dist/agent/agent-session.js +270 -0
  12. package/dist/agent/debugger/agent-chat.d.ts +2 -0
  13. package/dist/agent/debugger/agent-chat.js +176 -0
  14. package/dist/agent/debugger/index.d.ts +5 -0
  15. package/dist/agent/debugger/index.js +19 -0
  16. package/dist/agent/debugger/summarize.d.ts +2 -0
  17. package/dist/agent/debugger/summarize.js +51 -0
  18. package/dist/agent/debugger/token-usage.d.ts +3 -0
  19. package/dist/agent/debugger/token-usage.js +11 -0
  20. package/dist/agent/definitions-store.d.ts +2 -3
  21. package/dist/agent/definitions-store.js +2 -4
  22. package/dist/agent/definitions.d.ts +47 -29
  23. package/dist/agent/definitions.js +39 -12
  24. package/dist/agent/effects.d.ts +359 -2
  25. package/dist/agent/effects.js +60 -34
  26. package/dist/agent/events.d.ts +175 -19
  27. package/dist/agent/events.js +45 -10
  28. package/dist/agent/index.d.ts +1 -0
  29. package/dist/agent/index.js +1 -0
  30. package/dist/agent/llm.d.ts +11 -2
  31. package/dist/agent/llm.js +13 -3
  32. package/dist/agent/module.d.ts +812 -78
  33. package/dist/agent/module.js +16 -4
  34. package/dist/agent/scope.d.ts +174 -18
  35. package/dist/agent/signals.d.ts +26 -1
  36. package/dist/agent/signals.js +79 -0
  37. package/dist/agent/threads.d.ts +3 -33
  38. package/dist/agent/threads.js +20 -25
  39. package/dist/agent/tool-schema.d.ts +1 -17
  40. package/dist/agent/tool-schema.js +3 -43
  41. package/dist/agent/types.d.ts +49 -4
  42. package/dist/agent/types.js +5 -5
  43. package/dist/ai-vercel/index.d.ts +1 -0
  44. package/dist/ai-vercel/index.js +1 -0
  45. package/dist/ai-vercel/messages.js +22 -7
  46. package/dist/ai-vercel/vercel-evaluator.d.ts +36 -0
  47. package/dist/ai-vercel/vercel-evaluator.js +73 -0
  48. package/dist/ai-vercel/vercel-llm.js +64 -44
  49. package/dist/approval/debugger/approvals-view.d.ts +3 -0
  50. package/dist/approval/debugger/approvals-view.js +16 -0
  51. package/dist/approval/debugger/index.d.ts +4 -0
  52. package/dist/approval/debugger/index.js +11 -0
  53. package/dist/approval/debugger/summarize.d.ts +2 -0
  54. package/dist/approval/debugger/summarize.js +17 -0
  55. package/dist/approval/effects.d.ts +89 -0
  56. package/dist/approval/module.d.ts +179 -29
  57. package/dist/approval/projections.d.ts +0 -14
  58. package/dist/approval/projections.js +20 -3
  59. package/dist/approval/signals.d.ts +2 -0
  60. package/dist/approval/signals.js +18 -2
  61. package/dist/cli/cli.js +2 -0
  62. package/dist/cli/commands.js +12 -16
  63. package/dist/client/client.d.ts +1213 -18
  64. package/dist/client/client.js +120 -120
  65. package/dist/client/index.d.ts +1 -1
  66. package/dist/client/index.js +1 -1
  67. package/dist/client/sse.d.ts +13 -0
  68. package/dist/client/sse.js +38 -44
  69. package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
  70. package/dist/cloudflare/alarm-scheduler.js +34 -7
  71. package/dist/cloudflare/durable-object.d.ts +10 -2
  72. package/dist/cloudflare/durable-object.js +59 -28
  73. package/dist/cloudflare/event-store.d.ts +7 -2
  74. package/dist/cloudflare/event-store.js +8 -7
  75. package/dist/cloudflare/index.d.ts +2 -2
  76. package/dist/cloudflare/index.js +2 -2
  77. package/dist/cloudflare/router.d.ts +1 -1
  78. package/dist/cloudflare/router.js +15 -16
  79. package/dist/core/catalog.d.ts +3 -3
  80. package/dist/core/catalog.js +2 -5
  81. package/dist/core/coalesce.js +5 -3
  82. package/dist/core/compose.d.ts +2 -1
  83. package/dist/core/compose.js +19 -2
  84. package/dist/core/debugger-host.d.ts +16 -0
  85. package/dist/core/debugger-host.js +1 -0
  86. package/dist/core/definition-store.d.ts +11 -0
  87. package/dist/core/definition-store.js +6 -0
  88. package/dist/core/effects.d.ts +127 -15
  89. package/dist/core/effects.js +89 -17
  90. package/dist/core/envelope.d.ts +27 -7
  91. package/dist/core/envelope.js +20 -6
  92. package/dist/core/event-codec.d.ts +4 -2
  93. package/dist/core/event-codec.js +10 -5
  94. package/dist/core/event-index.js +1 -3
  95. package/dist/core/fold.d.ts +3 -0
  96. package/dist/core/fold.js +171 -24
  97. package/dist/core/ids.d.ts +5 -0
  98. package/dist/core/ids.js +35 -2
  99. package/dist/core/index.d.ts +7 -1
  100. package/dist/core/index.js +6 -1
  101. package/dist/core/kind.d.ts +22 -0
  102. package/dist/core/kind.js +17 -0
  103. package/dist/core/limits.d.ts +34 -0
  104. package/dist/core/limits.js +46 -0
  105. package/dist/core/match.d.ts +1 -0
  106. package/dist/core/match.js +10 -1
  107. package/dist/core/module-scope.d.ts +22 -20
  108. package/dist/core/module-scope.js +38 -28
  109. package/dist/core/module.d.ts +18 -5
  110. package/dist/core/module.js +4 -0
  111. package/dist/core/projection.d.ts +13 -13
  112. package/dist/core/projection.js +9 -5
  113. package/dist/core/protocol.d.ts +68 -11
  114. package/dist/core/protocol.js +82 -11
  115. package/dist/core/published.d.ts +43 -0
  116. package/dist/core/published.js +44 -0
  117. package/dist/core/replay.d.ts +312 -3
  118. package/dist/core/replay.js +11 -1
  119. package/dist/core/schema.d.ts +11 -20
  120. package/dist/core/schema.js +63 -80
  121. package/dist/core/serialize.js +2 -4
  122. package/dist/core/snapshot-store.d.ts +6 -1
  123. package/dist/core/snapshot-store.js +15 -2
  124. package/dist/core/snapshots.d.ts +5 -1
  125. package/dist/core/snapshots.js +10 -15
  126. package/dist/core/sqlite-store.js +95 -11
  127. package/dist/core/state.d.ts +142 -4
  128. package/dist/core/state.js +78 -0
  129. package/dist/core/store.d.ts +38 -3
  130. package/dist/core/store.js +77 -22
  131. package/dist/core/thread.d.ts +10 -9
  132. package/dist/core/types.d.ts +5 -8
  133. package/dist/core/types.js +28 -24
  134. package/dist/core/validate-event.js +14 -4
  135. package/dist/core/worker-callback.d.ts +17 -0
  136. package/dist/core/worker-callback.js +1 -0
  137. package/dist/debugger/app/assets/index-B3Ah8WAE.css +2 -0
  138. package/dist/debugger/app/assets/index-Qb371t_O.js +22 -0
  139. package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
  140. package/dist/debugger/app/assets/routes-CjtEHgPQ.js +47 -0
  141. package/dist/debugger/app/index.html +14 -0
  142. package/dist/debugger/components/composer.d.ts +17 -0
  143. package/dist/debugger/components/composer.js +25 -0
  144. package/dist/debugger/components/debugger-split.d.ts +2 -1
  145. package/dist/debugger/components/debugger-split.js +2 -2
  146. package/dist/debugger/components/event-inspector.d.ts +2 -1
  147. package/dist/debugger/components/event-inspector.js +25 -16
  148. package/dist/debugger/components/event-stream.d.ts +2 -1
  149. package/dist/debugger/components/event-stream.js +14 -74
  150. package/dist/debugger/components/follow-list.d.ts +19 -0
  151. package/dist/debugger/components/follow-list.js +96 -0
  152. package/dist/debugger/components/json-tree.d.ts +11 -0
  153. package/dist/debugger/components/json-tree.js +120 -0
  154. package/dist/debugger/components/json-view.js +2 -2
  155. package/dist/debugger/components/run-tree.js +53 -20
  156. package/dist/debugger/components/status-dot.d.ts +4 -0
  157. package/dist/debugger/components/status-dot.js +6 -2
  158. package/dist/debugger/context.d.ts +14 -0
  159. package/dist/debugger/context.js +17 -0
  160. package/dist/debugger/contracts.d.ts +7 -4
  161. package/dist/debugger/events/builtin-families.d.ts +3 -0
  162. package/dist/debugger/events/builtin-families.js +9 -0
  163. package/dist/debugger/events/catalog.d.ts +3 -4
  164. package/dist/debugger/events/catalog.js +27 -17
  165. package/dist/debugger/events/protocol-summarize.d.ts +1 -2
  166. package/dist/debugger/events/protocol-summarize.js +42 -24
  167. package/dist/debugger/index.d.ts +20 -6
  168. package/dist/debugger/index.js +19 -5
  169. package/dist/debugger/lib/cn.d.ts +1 -0
  170. package/dist/debugger/lib/cn.js +3 -0
  171. package/dist/debugger/lib/event-meta.d.ts +2 -3
  172. package/dist/debugger/lib/event-meta.js +6 -12
  173. package/dist/debugger/lib/json.d.ts +25 -0
  174. package/dist/debugger/lib/json.js +87 -0
  175. package/dist/debugger/lib/payload.d.ts +7 -0
  176. package/dist/debugger/lib/payload.js +11 -0
  177. package/dist/debugger/lib/time.d.ts +3 -0
  178. package/dist/debugger/lib/time.js +24 -0
  179. package/dist/debugger/plugin.d.ts +36 -0
  180. package/dist/debugger/plugin.js +14 -0
  181. package/dist/debugger/server/index.d.ts +14 -0
  182. package/dist/debugger/server/index.js +114 -0
  183. package/dist/debugger/shell/debugger-shell.d.ts +15 -0
  184. package/dist/debugger/shell/debugger-shell.js +43 -0
  185. package/dist/debugger/shell/definition-key.d.ts +3 -0
  186. package/dist/debugger/shell/definition-key.js +4 -0
  187. package/dist/debugger/shell/events-pane.d.ts +12 -0
  188. package/dist/debugger/shell/events-pane.js +25 -0
  189. package/dist/debugger/shell/projections-panel.d.ts +4 -0
  190. package/dist/debugger/shell/projections-panel.js +59 -0
  191. package/dist/debugger/shell/run-controls.d.ts +4 -0
  192. package/dist/debugger/shell/run-controls.js +72 -0
  193. package/dist/debugger/shell/run-search.d.ts +6 -0
  194. package/dist/debugger/shell/run-search.js +102 -0
  195. package/dist/debugger/shell/run-type-menu.d.ts +6 -0
  196. package/dist/debugger/shell/run-type-menu.js +11 -0
  197. package/dist/debugger/shell/schema-form.d.ts +31 -0
  198. package/dist/debugger/shell/schema-form.js +117 -0
  199. package/dist/debugger/shell/start-form.d.ts +3 -0
  200. package/dist/debugger/shell/start-form.js +75 -0
  201. package/dist/debugger/shell/use-start-run.d.ts +14 -0
  202. package/dist/debugger/shell/use-start-run.js +32 -0
  203. package/dist/debugger/shell/workspace.d.ts +6 -0
  204. package/dist/debugger/shell/workspace.js +15 -0
  205. package/dist/debugger/styles.css +111 -27
  206. package/dist/debugger/ui/button.d.ts +24 -0
  207. package/dist/debugger/ui/button.js +24 -0
  208. package/dist/debugger/ui/command.d.ts +9 -0
  209. package/dist/debugger/ui/command.js +24 -0
  210. package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
  211. package/dist/debugger/ui/dropdown-menu.js +37 -0
  212. package/dist/debugger/ui/panel.d.ts +14 -0
  213. package/dist/debugger/ui/panel.js +16 -0
  214. package/dist/debugger/ui/popover.d.ts +7 -0
  215. package/dist/debugger/ui/popover.js +17 -0
  216. package/dist/debugger/ui/resizable.d.ts +7 -0
  217. package/dist/debugger/ui/resizable.js +14 -0
  218. package/dist/debugger/ui/scroll-area.d.ts +2 -1
  219. package/dist/debugger/ui/scroll-area.js +5 -3
  220. package/dist/debugger/ui/select.d.ts +10 -0
  221. package/dist/debugger/ui/select.js +21 -0
  222. package/dist/debugger/ui/tabs.js +2 -2
  223. package/dist/debugger/ui/textarea.d.ts +3 -0
  224. package/dist/debugger/ui/textarea.js +7 -0
  225. package/dist/evaluator/definitions-store.d.ts +11 -0
  226. package/dist/evaluator/definitions-store.js +8 -0
  227. package/dist/evaluator/definitions.d.ts +18 -0
  228. package/dist/evaluator/definitions.js +4 -0
  229. package/dist/evaluator/effects.d.ts +226 -0
  230. package/dist/evaluator/effects.js +132 -0
  231. package/dist/evaluator/evaluator.d.ts +24 -0
  232. package/dist/evaluator/evaluator.js +67 -0
  233. package/dist/evaluator/events.d.ts +59 -0
  234. package/dist/evaluator/events.js +21 -0
  235. package/dist/evaluator/index.d.ts +11 -0
  236. package/dist/evaluator/index.js +11 -0
  237. package/dist/evaluator/module.d.ts +606 -0
  238. package/dist/evaluator/module.js +25 -0
  239. package/dist/evaluator/projections.d.ts +50 -0
  240. package/dist/evaluator/projections.js +20 -0
  241. package/dist/evaluator/scope.d.ts +59 -0
  242. package/dist/evaluator/scope.js +7 -0
  243. package/dist/evaluator/signals.d.ts +3 -0
  244. package/dist/evaluator/signals.js +17 -0
  245. package/dist/evaluator/threads.d.ts +62 -0
  246. package/dist/evaluator/threads.js +117 -0
  247. package/dist/evaluator/types.d.ts +125 -0
  248. package/dist/evaluator/types.js +51 -0
  249. package/dist/projectors/delivery.d.ts +83 -0
  250. package/dist/projectors/delivery.js +182 -0
  251. package/dist/projectors/index-model.d.ts +1 -1
  252. package/dist/projectors/index-model.js +4 -6
  253. package/dist/projectors/index.d.ts +2 -0
  254. package/dist/projectors/index.js +2 -0
  255. package/dist/projectors/memory.d.ts +1 -1
  256. package/dist/projectors/memory.js +10 -8
  257. package/dist/projectors/postgres.js +44 -58
  258. package/dist/projectors/projector.d.ts +17 -0
  259. package/dist/projectors/projector.js +26 -1
  260. package/dist/projectors/sqlite-delivery.d.ts +4 -0
  261. package/dist/projectors/sqlite-delivery.js +147 -0
  262. package/dist/projectors/sqlite.js +23 -23
  263. package/dist/projectors/with-projectors.d.ts +7 -2
  264. package/dist/projectors/with-projectors.js +27 -28
  265. package/dist/react/notify.js +11 -8
  266. package/dist/react/react.d.ts +2 -3
  267. package/dist/react/react.js +14 -30
  268. package/dist/react/sse.d.ts +1 -1
  269. package/dist/react/sse.js +1 -1
  270. package/dist/react/store.d.ts +3 -3
  271. package/dist/react/store.js +109 -119
  272. package/dist/runtime/cancellation.d.ts +16 -0
  273. package/dist/runtime/cancellation.js +129 -0
  274. package/dist/runtime/deadline-transition-planner.d.ts +31 -0
  275. package/dist/runtime/deadline-transition-planner.js +139 -0
  276. package/dist/runtime/dispatch-effect.d.ts +3 -2
  277. package/dist/runtime/dispatch-effect.js +7 -13
  278. package/dist/runtime/errors.d.ts +51 -0
  279. package/dist/runtime/errors.js +39 -0
  280. package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
  281. package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
  282. package/dist/runtime/index.d.ts +3 -3
  283. package/dist/runtime/index.js +3 -3
  284. package/dist/runtime/ingress.d.ts +15 -0
  285. package/dist/runtime/ingress.js +42 -0
  286. package/dist/runtime/local-effect-executor.d.ts +21 -0
  287. package/dist/runtime/local-effect-executor.js +137 -0
  288. package/dist/runtime/looms.d.ts +30 -4
  289. package/dist/runtime/looms.js +57 -37
  290. package/dist/runtime/observer.d.ts +45 -0
  291. package/dist/runtime/observer.js +11 -0
  292. package/dist/runtime/repository.d.ts +55 -0
  293. package/dist/runtime/repository.js +293 -0
  294. package/dist/runtime/retry-policy.d.ts +16 -0
  295. package/dist/runtime/retry-policy.js +29 -0
  296. package/dist/runtime/run-execution-context.d.ts +30 -0
  297. package/dist/runtime/run-execution-context.js +56 -0
  298. package/dist/runtime/run-start.d.ts +4 -0
  299. package/dist/runtime/run-start.js +44 -0
  300. package/dist/runtime/runtime.d.ts +6 -87
  301. package/dist/runtime/runtime.js +248 -470
  302. package/dist/runtime/server/auth.d.ts +41 -0
  303. package/dist/runtime/server/auth.js +45 -0
  304. package/dist/runtime/server/debugger-host.d.ts +11 -0
  305. package/dist/runtime/server/debugger-host.js +32 -0
  306. package/dist/runtime/server/fetch-handler.d.ts +18 -0
  307. package/dist/runtime/server/fetch-handler.js +62 -0
  308. package/dist/runtime/server/http.d.ts +38 -0
  309. package/dist/runtime/server/http.js +66 -0
  310. package/dist/runtime/server/route-table.d.ts +55 -0
  311. package/dist/runtime/server/route-table.js +74 -0
  312. package/dist/runtime/server/routes/definitions.d.ts +3 -0
  313. package/dist/runtime/server/routes/definitions.js +20 -0
  314. package/dist/runtime/server/routes/effects.d.ts +3 -0
  315. package/dist/runtime/server/routes/effects.js +59 -0
  316. package/dist/runtime/server/routes/operations.d.ts +3 -0
  317. package/dist/runtime/server/routes/operations.js +14 -0
  318. package/dist/runtime/server/routes/runs.d.ts +3 -0
  319. package/dist/runtime/server/routes/runs.js +199 -0
  320. package/dist/runtime/server.d.ts +4 -8
  321. package/dist/runtime/server.js +6 -211
  322. package/dist/runtime/sse.d.ts +2 -1
  323. package/dist/runtime/sse.js +24 -17
  324. package/dist/runtime/types.d.ts +117 -0
  325. package/dist/runtime/types.js +1 -0
  326. package/dist/runtime/wake-engine.d.ts +20 -0
  327. package/dist/runtime/wake-engine.js +311 -0
  328. package/dist/runtime/wake-scheduler.d.ts +3 -4
  329. package/dist/runtime/wake-scheduler.js +25 -31
  330. package/dist/runtime/worker-callbacks.d.ts +16 -0
  331. package/dist/runtime/worker-callbacks.js +167 -0
  332. package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
  333. package/dist/runtime/worker-effect-dispatch.js +126 -0
  334. package/dist/s2/config.d.ts +1 -1
  335. package/dist/s2/config.js +1 -1
  336. package/dist/s2/lite.js +70 -82
  337. package/dist/s2/pagination.d.ts +15 -0
  338. package/dist/s2/pagination.js +36 -0
  339. package/dist/s2/projector.js +6 -5
  340. package/dist/s2/snapshot-store.js +188 -161
  341. package/dist/s2/store.d.ts +3 -21
  342. package/dist/s2/store.js +71 -165
  343. package/dist/testing/index.d.ts +15 -0
  344. package/dist/testing/index.js +53 -16
  345. package/dist/workflow/debugger/index.d.ts +4 -0
  346. package/dist/workflow/debugger/index.js +11 -0
  347. package/dist/workflow/debugger/nodes-view.d.ts +3 -0
  348. package/dist/workflow/debugger/nodes-view.js +13 -0
  349. package/dist/workflow/debugger/summarize.d.ts +2 -0
  350. package/dist/workflow/debugger/summarize.js +30 -0
  351. package/dist/workflow/definitions-store.d.ts +2 -3
  352. package/dist/workflow/definitions-store.js +2 -2
  353. package/dist/workflow/definitions.d.ts +78 -22
  354. package/dist/workflow/definitions.js +80 -14
  355. package/dist/workflow/effects.d.ts +453 -107
  356. package/dist/workflow/effects.js +214 -115
  357. package/dist/workflow/events.d.ts +149 -5
  358. package/dist/workflow/events.js +27 -5
  359. package/dist/workflow/graph.d.ts +56 -0
  360. package/dist/workflow/graph.js +207 -0
  361. package/dist/workflow/index.d.ts +1 -0
  362. package/dist/workflow/index.js +1 -0
  363. package/dist/workflow/module.d.ts +1047 -266
  364. package/dist/workflow/projections.d.ts +14 -12
  365. package/dist/workflow/projections.js +30 -8
  366. package/dist/workflow/scope.d.ts +146 -4
  367. package/dist/workflow/threads.d.ts +383 -155
  368. package/dist/workflow/threads.js +308 -110
  369. package/package.json +26 -2
  370. package/dist/debugger/events/domain-summarize.d.ts +0 -3
  371. package/dist/debugger/events/domain-summarize.js +0 -107
  372. package/dist/debugger/events/family.d.ts +0 -8
  373. package/dist/debugger/events/family.js +0 -44
  374. package/dist/runtime/events-api.d.ts +0 -4
  375. package/dist/runtime/events-api.js +0 -84
@@ -0,0 +1,20 @@
1
+ import { type Context, type ManagedRuntime } from 'effect';
2
+ import { type ComposedRegistry, type RegisteredDefinition } from '@swirls/looms/core';
3
+ import { type RuntimeObserver } from './observer';
4
+ import type { RuntimeRepository } from './repository';
5
+ import type { RunExecutionContexts } from './run-execution-context';
6
+ import type { LoomsRuntime, EffectWorker } from './types';
7
+ import type { WakeScheduler } from './wake-scheduler';
8
+ export interface WakeEngineOptions {
9
+ readonly registry: ComposedRegistry;
10
+ readonly services: ManagedRuntime.ManagedRuntime<Context.Service.Any, never>;
11
+ readonly definitions: ReadonlyMap<string, RegisteredDefinition>;
12
+ readonly executionContexts: RunExecutionContexts;
13
+ readonly repository: RuntimeRepository;
14
+ readonly snapshotEvery: number;
15
+ readonly maxWakeIterations: number;
16
+ readonly worker?: EffectWorker;
17
+ readonly observer?: RuntimeObserver;
18
+ readonly scheduler: () => WakeScheduler;
19
+ }
20
+ export declare function createWakeEngine(options: WakeEngineOptions): LoomsRuntime['wake'];
@@ -0,0 +1,311 @@
1
+ import { Clock, Effect, Exit, Option } from 'effect';
2
+ import { createEvent, EventStoreTag, foldRun, isRunTerminal, } from '@swirls/looms/core';
3
+ import { nextRuntimeDeadline, planDeadlineTransitions, planTerminalCancellationTransitions, } from './deadline-transition-planner.js';
4
+ import { DuplicateEffectDispatchError, MaxWakeIterationsError, RuntimeExecutionError, } from './errors.js';
5
+ import { stripSeq, synthesizedThreadFailed, waitSatisfiedEvents } from './helpers.js';
6
+ import { executeLocalEffects } from './local-effect-executor.js';
7
+ import { notifyObserver } from './observer.js';
8
+ import { dispatchWorkerEffect, isWorkerEffect, planWorkerEffects } from './worker-effect-dispatch.js';
9
+ function currentTimeMillis() {
10
+ return Math.floor(performance.timeOrigin + performance.now());
11
+ }
12
+ export function createWakeEngine(options) {
13
+ const { registry, services, definitions, executionContexts, repository, snapshotEvery, maxWakeIterations, } = options;
14
+ const { appendObserved, applyAppend, liveAppends, loadCursor, persistSnapshot } = repository;
15
+ const runCache = {
16
+ delete: repository.deleteCachedCursor,
17
+ set: repository.setCachedCursor,
18
+ };
19
+ const observe = (event) => notifyObserver(options.observer, event);
20
+ return (runId) => Effect.gen(function* () {
21
+ observe({ type: 'run.wake', runId, at: currentTimeMillis() });
22
+ const executionContext = executionContexts.get(runId);
23
+ // Claim synchronously before any yield so concurrent fibers cannot both pass.
24
+ if (executionContext.waking) {
25
+ executionContext.wakeAgain = true;
26
+ const activeCompletion = executionContext.wakeCompletion;
27
+ if (!activeCompletion) {
28
+ return yield* new RuntimeExecutionError(`Run "${runId}" has an active wake without completion state`);
29
+ }
30
+ const completion = yield* Effect.promise(() => activeCompletion);
31
+ if (Exit.isFailure(completion)) {
32
+ return yield* Effect.failCause(completion.cause);
33
+ }
34
+ return completion.value;
35
+ }
36
+ let resolveCompletion;
37
+ // oxlint-disable-next-line effecttsgo/new-promise
38
+ executionContext.wakeCompletion = new Promise((resolve) => {
39
+ resolveCompletion = resolve;
40
+ });
41
+ executionContext.resolveWakeCompletion = resolveCompletion;
42
+ executionContext.waking = true;
43
+ let released = false;
44
+ const releaseWake = () => {
45
+ released = true;
46
+ executionContext.waking = false;
47
+ executionContext.wakeAgain = false;
48
+ executionContext.wakeCompletion = null;
49
+ executionContext.resolveWakeCompletion = null;
50
+ executionContext.liveStore = undefined;
51
+ liveAppends.clear(runId);
52
+ executionContext.liveCount = 0;
53
+ executionContexts.releaseIfIdle(runId);
54
+ };
55
+ // Checking for a joined wake and releasing the claim must happen without yielding, or a wake
56
+ // requested in between would join a completion that never saw its events.
57
+ const releaseUnlessWokenAgain = () => {
58
+ if (executionContext.wakeAgain) {
59
+ executionContext.wakeAgain = false;
60
+ return false;
61
+ }
62
+ releaseWake();
63
+ return true;
64
+ };
65
+ return yield* Effect.gen(function* () {
66
+ const store = yield* EventStoreTag;
67
+ const runInContext = Effect.runPromiseWith(yield* Effect.context());
68
+ executionContext.liveStore = store;
69
+ let finalState;
70
+ do {
71
+ let cursor = yield* loadCursor(store, runId);
72
+ if (isRunTerminal(cursor.state)) {
73
+ const now = yield* Clock.currentTimeMillis;
74
+ const events = planTerminalCancellationTransitions({
75
+ runId,
76
+ state: cursor.state,
77
+ now,
78
+ });
79
+ if (events.length > 0) {
80
+ const appended = yield* appendObserved(store, runId, stripSeq(events));
81
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq(events), appended);
82
+ }
83
+ const nextDeadline = nextRuntimeDeadline(cursor.state, now);
84
+ if (nextDeadline !== undefined) {
85
+ yield* options.scheduler().schedule(runId, nextDeadline);
86
+ }
87
+ else {
88
+ yield* options.scheduler().cancel(runId);
89
+ }
90
+ finalState = cursor.state;
91
+ continue;
92
+ }
93
+ executionContext.liveCount = 0;
94
+ const dispatchedEffectIds = new Set();
95
+ const startedEffectIds = new Set();
96
+ let guard = 0;
97
+ // Deadlines at or before this instant were handled by the last loop iteration.
98
+ let plannedAt = Number.NEGATIVE_INFINITY;
99
+ while (!isRunTerminal(cursor.state)) {
100
+ if (guard >= maxWakeIterations) {
101
+ return yield* new MaxWakeIterationsError(runId, maxWakeIterations);
102
+ }
103
+ guard += 1;
104
+ const state = cursor.state;
105
+ const now = yield* Clock.currentTimeMillis;
106
+ plannedAt = now;
107
+ const deadlinePlan = planDeadlineTransitions({ runId, state, registry, now });
108
+ if (deadlinePlan.events.length > 0) {
109
+ for (const notice of deadlinePlan.timerNotices) {
110
+ observe({
111
+ type: 'timer.late',
112
+ runId,
113
+ at: now,
114
+ timerId: notice.timerId,
115
+ latenessMs: notice.latenessMs,
116
+ });
117
+ }
118
+ for (const notice of deadlinePlan.timeoutNotices) {
119
+ observe({
120
+ type: 'effect.timeout',
121
+ runId,
122
+ at: now,
123
+ effectId: notice.effectId,
124
+ attempt: notice.attempt,
125
+ detail: notice.timeout,
126
+ });
127
+ }
128
+ const append = deadlinePlan.timerNotices.length > 0
129
+ ? yield* appendObserved(store, runId, stripSeq(deadlinePlan.events), {
130
+ expectedTail: cursor.seq,
131
+ }).pipe(Effect.asSome, Effect.catchTag('EventStoreConflictError', () => Effect.succeedNone))
132
+ : Option.some(yield* appendObserved(store, runId, stripSeq(deadlinePlan.events)));
133
+ if (Option.isNone(append)) {
134
+ runCache.delete(runId);
135
+ cursor = yield* loadCursor(store, runId);
136
+ continue;
137
+ }
138
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq(deadlinePlan.events), append.value);
139
+ continue;
140
+ }
141
+ const workerPlans = planWorkerEffects({
142
+ runId,
143
+ state,
144
+ registry,
145
+ worker: options.worker,
146
+ now,
147
+ });
148
+ if (workerPlans.length > 0) {
149
+ for (const workerPlan of workerPlans) {
150
+ if (workerPlan.queuedEvent) {
151
+ const queuedAppend = yield* appendObserved(store, runId, stripSeq([workerPlan.queuedEvent]));
152
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq([workerPlan.queuedEvent]), queuedAppend);
153
+ }
154
+ const dispatchEvent = yield* dispatchWorkerEffect(runId, workerPlan, options.worker);
155
+ const dispatchAppend = yield* appendObserved(store, runId, stripSeq([dispatchEvent]));
156
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq([dispatchEvent]), dispatchAppend);
157
+ }
158
+ continue;
159
+ }
160
+ const cancellationTargets = new Set(state.outstandingEffects.flatMap((item) => item.effect.type === 'runtime.cancel' && 'threadId' in item.effect
161
+ ? [item.effect.threadId]
162
+ : []));
163
+ const outstanding = state.outstandingEffects.filter((item) => {
164
+ const execution = state.effectExecutions[item.effectId];
165
+ return !((cancellationTargets.has(item.threadId) && item.effect.type !== 'runtime.cancel') ||
166
+ isWorkerEffect({ runId, item, execution, registry, worker: options.worker }) ||
167
+ (execution?.status === 'retry_wait' &&
168
+ execution.nextAttemptAt !== null &&
169
+ execution.nextAttemptAt > now));
170
+ });
171
+ if (outstanding.length === 0) {
172
+ break;
173
+ }
174
+ const attempts = new Map(outstanding.map((item) => {
175
+ const execution = state.effectExecutions[item.effectId];
176
+ const attempt = execution?.status === 'running' && startedEffectIds.has(item.effectId)
177
+ ? execution.attempt
178
+ : (execution?.attempt ?? 0) + 1;
179
+ return [item.effectId, attempt];
180
+ }));
181
+ for (const item of outstanding) {
182
+ const dispatchKey = `${item.effectId}:${attempts.get(item.effectId) ?? 1}`;
183
+ if (dispatchedEffectIds.has(dispatchKey)) {
184
+ return yield* new DuplicateEffectDispatchError(runId, item.effectId, item.threadId);
185
+ }
186
+ }
187
+ for (const item of outstanding) {
188
+ dispatchedEffectIds.add(`${item.effectId}:${attempts.get(item.effectId) ?? 1}`);
189
+ }
190
+ const attemptEvents = outstanding.map((item) => createEvent(runId, {
191
+ type: 'runtime.effect.attempt.started',
192
+ payload: {
193
+ effectId: item.effectId,
194
+ attempt: attempts.get(item.effectId) ?? 1,
195
+ },
196
+ threadId: item.threadId,
197
+ origin: { type: 'system' },
198
+ }));
199
+ for (const item of outstanding) {
200
+ observe({
201
+ type: 'effect.attempt',
202
+ runId,
203
+ at: now,
204
+ effectId: item.effectId,
205
+ attempt: attempts.get(item.effectId) ?? 1,
206
+ });
207
+ }
208
+ const attemptAppend = yield* appendObserved(store, runId, stripSeq(attemptEvents));
209
+ for (const item of outstanding) {
210
+ startedEffectIds.add(item.effectId);
211
+ }
212
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq(attemptEvents), attemptAppend);
213
+ const produced = yield* executeLocalEffects({
214
+ registry,
215
+ services,
216
+ definitions,
217
+ executionContext,
218
+ liveAppends,
219
+ runInContext,
220
+ observe,
221
+ }, { runId, outstanding, attempts, now });
222
+ if (produced.length === 0) {
223
+ break;
224
+ }
225
+ yield* liveAppends.drain(runId);
226
+ const beforeFold = cursor.state;
227
+ const intermediateState = foldRun(produced, registry, { runId, initial: beforeFold });
228
+ const synthesized = synthesizedThreadFailed(beforeFold, intermediateState, produced);
229
+ const synthesizedEvents = synthesized.map((input) => createEvent(runId, input));
230
+ const stateAfterSynth = synthesizedEvents.length > 0
231
+ ? foldRun(synthesizedEvents, registry, { runId, initial: intermediateState })
232
+ : intermediateState;
233
+ const allNewEvents = [...produced, ...synthesizedEvents];
234
+ const satisfied = waitSatisfiedEvents(stateAfterSynth, allNewEvents);
235
+ const satisfiedEvents = satisfied.map((input) => createEvent(runId, input));
236
+ const fullBatch = [...allNewEvents, ...satisfiedEvents];
237
+ const appendResult = yield* appendObserved(store, runId, stripSeq(fullBatch));
238
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq(fullBatch), appendResult);
239
+ // Bound replay after a crash inside a long wake.
240
+ if (snapshotEvery > 0 && cursor.durableSinceSnapshot >= snapshotEvery) {
241
+ cursor = yield* persistSnapshot(store, runId, cursor);
242
+ }
243
+ }
244
+ const root = cursor.state.rootThreadId
245
+ ? cursor.state.threads[cursor.state.rootThreadId]
246
+ : undefined;
247
+ if (root &&
248
+ (root.status === 'completed' ||
249
+ root.status === 'failed' ||
250
+ root.status === 'cancelled') &&
251
+ !isRunTerminal(cursor.state)) {
252
+ const completed = root.status === 'cancelled'
253
+ ? createEvent(runId, {
254
+ type: 'runtime.run.cancelled',
255
+ payload: { reason: root.error ?? 'cancelled' },
256
+ threadId: null,
257
+ origin: { type: 'system' },
258
+ })
259
+ : createEvent(runId, {
260
+ type: 'runtime.run.completed',
261
+ payload: {
262
+ output: root.output,
263
+ error: root.error,
264
+ },
265
+ threadId: null,
266
+ origin: { type: 'system' },
267
+ });
268
+ const appendResult = yield* appendObserved(store, runId, stripSeq([completed]));
269
+ cursor = yield* applyAppend(store, runId, cursor, stripSeq([completed]), appendResult);
270
+ }
271
+ // Snapshot on park: the next wake (on any instance) starts from here.
272
+ cursor = yield* persistSnapshot(store, runId, cursor);
273
+ const nextDeadline = nextRuntimeDeadline(cursor.state, plannedAt);
274
+ if (nextDeadline !== undefined) {
275
+ yield* options.scheduler().schedule(runId, nextDeadline);
276
+ }
277
+ else {
278
+ yield* options.scheduler().cancel(runId);
279
+ }
280
+ if (isRunTerminal(cursor.state)) {
281
+ observe({
282
+ type: 'run.terminal',
283
+ runId,
284
+ at: currentTimeMillis(),
285
+ status: cursor.state.status,
286
+ });
287
+ runCache.delete(runId);
288
+ }
289
+ else {
290
+ observe({
291
+ type: 'run.park',
292
+ runId,
293
+ at: currentTimeMillis(),
294
+ status: cursor.state.status,
295
+ });
296
+ runCache.set(runId, cursor);
297
+ }
298
+ finalState = cursor.state;
299
+ } while (!releaseUnlessWokenAgain());
300
+ if (!finalState) {
301
+ return yield* new RuntimeExecutionError(`Wake for run "${runId}" completed without state`);
302
+ }
303
+ return finalState;
304
+ }).pipe(Effect.onExit((exit) => Effect.sync(() => {
305
+ if (!released) {
306
+ releaseWake();
307
+ }
308
+ resolveCompletion(exit);
309
+ })));
310
+ });
311
+ }
@@ -1,8 +1,7 @@
1
1
  import { Effect } from 'effect';
2
2
  export interface WakeScheduler {
3
- schedule(runId: string, at: number): void | Promise<void>;
4
- cancel(runId: string): void | Promise<void>;
3
+ schedule(runId: string, at: number): Effect.Effect<void>;
4
+ cancel(runId: string): Effect.Effect<void>;
5
5
  dispose?(): void;
6
6
  }
7
- export declare function createTimeoutScheduler(wake: (runId: string) => Promise<void> | void): WakeScheduler;
8
- export declare function toEffectVoid(result: void | Promise<void>): Effect.Effect<void>;
7
+ export declare function createTimeoutScheduler(wake: (runId: string) => Effect.Effect<void>): WakeScheduler;
@@ -1,38 +1,32 @@
1
- import { Effect } from 'effect';
1
+ import { Clock, Effect, Fiber } from 'effect';
2
2
  export function createTimeoutScheduler(wake) {
3
- const scheduledTimers = new Map();
3
+ const scheduled = new Map();
4
+ const cancel = (runId) => Effect.gen(function* () {
5
+ const existing = scheduled.get(runId);
6
+ if (existing) {
7
+ scheduled.delete(runId);
8
+ yield* Fiber.interrupt(existing.fiber);
9
+ }
10
+ });
4
11
  return {
5
- schedule(runId, timerAt) {
6
- const existing = scheduledTimers.get(runId);
7
- if (existing) {
8
- clearTimeout(existing);
9
- scheduledTimers.delete(runId);
10
- }
11
- const delay = Math.max(0, timerAt - Date.now() + 5);
12
- const handle = setTimeout(() => {
13
- scheduledTimers.delete(runId);
14
- void wake(runId);
15
- }, delay);
16
- scheduledTimers.set(runId, handle);
17
- },
18
- cancel(runId) {
19
- const existing = scheduledTimers.get(runId);
20
- if (existing) {
21
- clearTimeout(existing);
22
- scheduledTimers.delete(runId);
23
- }
24
- },
12
+ schedule: (runId, timerAt) => Effect.gen(function* () {
13
+ yield* cancel(runId);
14
+ const now = yield* Clock.currentTimeMillis;
15
+ const token = Symbol(runId);
16
+ const release = Effect.sync(() => {
17
+ if (scheduled.get(runId)?.token === token) {
18
+ scheduled.delete(runId);
19
+ }
20
+ });
21
+ const fiber = yield* Effect.sleep(Math.max(0, timerAt - now + 5)).pipe(Effect.andThen(release), Effect.andThen(wake(runId)), Effect.ensuring(release), Effect.forkDetach);
22
+ scheduled.set(runId, { fiber, token });
23
+ }),
24
+ cancel,
25
25
  dispose() {
26
- for (const handle of scheduledTimers.values()) {
27
- clearTimeout(handle);
26
+ for (const { fiber } of scheduled.values()) {
27
+ Effect.runFork(Fiber.interrupt(fiber));
28
28
  }
29
- scheduledTimers.clear();
29
+ scheduled.clear();
30
30
  },
31
31
  };
32
32
  }
33
- export function toEffectVoid(result) {
34
- if (result instanceof Promise) {
35
- return Effect.promise(() => result);
36
- }
37
- return Effect.void;
38
- }
@@ -0,0 +1,16 @@
1
+ import { Effect } from 'effect';
2
+ import { EventStoreTag, type ComposedRegistry, type RunState, type WorkerCallbackInput } from '@swirls/looms/core';
3
+ import type { RunIngress } from './ingress';
4
+ import { type RuntimeObserver } from './observer';
5
+ import type { RuntimeRepository } from './repository';
6
+ import type { LoomsRuntime } from './types';
7
+ export interface WorkerCallbacksOptions {
8
+ readonly registry: ComposedRegistry;
9
+ readonly repository: RuntimeRepository;
10
+ readonly withRunIngress: RunIngress;
11
+ readonly observer?: RuntimeObserver;
12
+ readonly wake: LoomsRuntime['wake'];
13
+ }
14
+ export declare function createWorkerCallbacks(options: WorkerCallbacksOptions): {
15
+ workerCallback: (runId: string, callback: WorkerCallbackInput) => Effect.Effect<RunState, import("./errors").DuplicateEffectDispatchError | Error | import("@swirls/looms/core").EventPayloadTooLargeError | import("@swirls/looms/core").EventStoreConflictError | import("@swirls/looms/core").EventStoreError | import("@swirls/looms/core").EventStoreFencedError | import("@swirls/looms/core").EventStoreTrimmedError | import("@swirls/looms/core").EventStoreTruncationError | import("./errors").MaxWakeIterationsError | import("./ingress").RunOverloadedError | import("./errors").RuntimeExecutionError, EventStoreTag>;
16
+ };
@@ -0,0 +1,167 @@
1
+ import { Clock, Effect, Predicate } from 'effect';
2
+ import { createEvent, EventStoreTag, isRunTerminal, } from '@swirls/looms/core';
3
+ import { materializeEffectOutcome } from './helpers.js';
4
+ import { notifyObserver } from './observer.js';
5
+ import { planAttemptFailure } from './retry-policy.js';
6
+ const WORKER_ORIGIN = { type: 'external', actorId: 'worker' };
7
+ /** The earliest of the start-to-close cap and the next heartbeat deadline. */
8
+ function workerDeadline(retry, startedAt, now) {
9
+ const deadlines = [
10
+ retry?.startToCloseTimeoutMs ? startedAt + retry.startToCloseTimeoutMs : null,
11
+ retry?.heartbeatTimeoutMs ? now + retry.heartbeatTimeoutMs : null,
12
+ ].filter(Predicate.isNotNull);
13
+ return deadlines.length > 0 ? Math.min(...deadlines) : null;
14
+ }
15
+ function workerStartedEvent(runId, item, attempt, retry, now) {
16
+ return createEvent(runId, {
17
+ type: 'runtime.effect.worker.started',
18
+ payload: { effectId: item.effectId, attempt, deadlineAt: workerDeadline(retry, now, now) },
19
+ threadId: item.threadId,
20
+ origin: WORKER_ORIGIN,
21
+ });
22
+ }
23
+ const IGNORED = { events: [], notices: [] };
24
+ /**
25
+ * Turn a worker callback into events against the current durable state. Callbacks for another
26
+ * attempt, a settled attempt, or an effect running in process are ignored.
27
+ */
28
+ function planWorkerCallback(input) {
29
+ const { runId, state, callback, registry, now } = input;
30
+ const execution = state.effectExecutions[callback.effectId];
31
+ const item = state.outstandingEffects.find((candidate) => candidate.effectId === callback.effectId);
32
+ const isCancellationAcknowledgement = callback.kind === 'cancelled' &&
33
+ execution?.attempt === callback.attempt &&
34
+ execution.status === 'cancel_requested';
35
+ if (!execution ||
36
+ execution.attempt !== callback.attempt ||
37
+ (isRunTerminal(state) && !isCancellationAcknowledgement) ||
38
+ (!item && !isCancellationAcknowledgement) ||
39
+ execution.status === 'running' ||
40
+ execution.status === 'retry_wait' ||
41
+ execution.status === 'timed_out' ||
42
+ execution.status === 'ambiguous' ||
43
+ (callback.kind === 'cancelled' && !isCancellationAcknowledgement) ||
44
+ (execution.status === 'cancel_requested' && callback.kind !== 'cancelled')) {
45
+ return Effect.succeed(IGNORED);
46
+ }
47
+ const retry = item ? registry.effects.get(item.effect.type)?.retry : undefined;
48
+ const notice = (type) => ({
49
+ type,
50
+ runId,
51
+ at: now,
52
+ effectId: callback.effectId,
53
+ attempt: callback.attempt,
54
+ });
55
+ switch (callback.kind) {
56
+ case 'started': {
57
+ return Effect.succeed(item && execution.status === 'dispatched'
58
+ ? { events: [workerStartedEvent(runId, item, callback.attempt, retry, now)], notices: [] }
59
+ : IGNORED);
60
+ }
61
+ case 'heartbeat': {
62
+ const implicitStart = execution.status === 'dispatched';
63
+ if (!item ||
64
+ (!implicitStart && execution.status !== 'started' && execution.status !== 'heartbeat')) {
65
+ return Effect.succeed(IGNORED);
66
+ }
67
+ return Effect.succeed({
68
+ events: [
69
+ ...(implicitStart ? [workerStartedEvent(runId, item, callback.attempt, retry, now)] : []),
70
+ createEvent(runId, {
71
+ type: 'runtime.effect.heartbeat',
72
+ payload: {
73
+ effectId: item.effectId,
74
+ attempt: callback.attempt,
75
+ heartbeatAt: now,
76
+ deadlineAt: workerDeadline(retry, execution.startedAt ?? now, now),
77
+ },
78
+ threadId: item.threadId,
79
+ origin: WORKER_ORIGIN,
80
+ }),
81
+ ],
82
+ notices: [notice('effect.heartbeat')],
83
+ });
84
+ }
85
+ case 'complete': {
86
+ if (!item) {
87
+ return Effect.succeed(IGNORED);
88
+ }
89
+ return Effect.forEach(callback.events, (event) => materializeEffectOutcome(registry.catalogs, runId, item, event)).pipe(Effect.map((outcomes) => ({
90
+ events: [
91
+ ...outcomes,
92
+ createEvent(runId, {
93
+ type: 'runtime.effect.completed',
94
+ payload: { effectId: item.effectId, attempt: callback.attempt },
95
+ threadId: item.threadId,
96
+ effectId: item.effectId,
97
+ causationId: item.causingEventId,
98
+ origin: WORKER_ORIGIN,
99
+ }),
100
+ ],
101
+ notices: [],
102
+ })));
103
+ }
104
+ case 'fail': {
105
+ if (!item) {
106
+ return Effect.succeed(IGNORED);
107
+ }
108
+ const failure = planAttemptFailure({
109
+ runId,
110
+ item,
111
+ attempt: callback.attempt,
112
+ error: callback.error,
113
+ now,
114
+ retry,
115
+ origin: WORKER_ORIGIN,
116
+ });
117
+ return Effect.succeed({
118
+ events: [failure.event],
119
+ notices: failure.retrying
120
+ ? [
121
+ {
122
+ type: 'effect.retry',
123
+ runId,
124
+ at: now,
125
+ effectId: item.effectId,
126
+ attempt: callback.attempt,
127
+ detail: callback.error,
128
+ },
129
+ ]
130
+ : [],
131
+ });
132
+ }
133
+ case 'cancelled': {
134
+ return Effect.succeed({
135
+ events: [
136
+ createEvent(runId, {
137
+ type: 'runtime.effect.cancelled',
138
+ payload: { effectId: callback.effectId, attempt: callback.attempt },
139
+ threadId: item?.threadId ?? null,
140
+ effectId: callback.effectId,
141
+ origin: WORKER_ORIGIN,
142
+ }),
143
+ ],
144
+ notices: [notice('effect.cancellation')],
145
+ });
146
+ }
147
+ default: {
148
+ const exhaustiveCheck = callback;
149
+ return exhaustiveCheck;
150
+ }
151
+ }
152
+ }
153
+ export function createWorkerCallbacks(options) {
154
+ const { registry, repository, withRunIngress } = options;
155
+ const applyWorkerCallback = (runId, callback) => withRunIngress(runId, Effect.gen(function* () {
156
+ const store = yield* EventStoreTag;
157
+ const now = yield* Clock.currentTimeMillis;
158
+ const committed = yield* repository.commit(store, runId, (cursor) => planWorkerCallback({ runId, state: cursor.state, callback, registry, now }).pipe(Effect.map((plan) => ({ events: plan.events, value: plan.notices }))));
159
+ if (committed.append) {
160
+ for (const notice of committed.value) {
161
+ notifyObserver(options.observer, notice);
162
+ }
163
+ }
164
+ return committed.cursor.state;
165
+ })).pipe(Effect.andThen(options.wake(runId)));
166
+ return { workerCallback: applyWorkerCallback };
167
+ }
@@ -0,0 +1,36 @@
1
+ import { Effect } from 'effect';
2
+ import { type ComposedRegistry, type EffectExecutionRecord, type EventEnvelope, type OutstandingEffect, type RunState } from '@swirls/looms/core';
3
+ import type { EffectWorker, EffectWorkerTask } from './types';
4
+ export interface WorkerEffectDispatchPlan {
5
+ readonly item: OutstandingEffect;
6
+ readonly attempt: number;
7
+ readonly queuedEvent?: EventEnvelope;
8
+ readonly task: EffectWorkerTask;
9
+ readonly scheduleToStartDeadlineAt: number | null;
10
+ }
11
+ /**
12
+ * Decides where an outstanding effect runs. An attempt already in flight keeps
13
+ * its durable placement. Otherwise an explicit `execution` wins, then a worker
14
+ * claim; effects with a local handler run in process and handler-less effects
15
+ * go to the worker.
16
+ */
17
+ export declare function isWorkerEffect(input: {
18
+ readonly runId: string;
19
+ readonly item: OutstandingEffect;
20
+ readonly execution: EffectExecutionRecord | undefined;
21
+ readonly registry: ComposedRegistry;
22
+ readonly worker?: EffectWorker;
23
+ }): boolean;
24
+ export declare function planWorkerEffects(input: {
25
+ readonly runId: string;
26
+ readonly state: RunState;
27
+ readonly registry: ComposedRegistry;
28
+ readonly worker?: EffectWorker;
29
+ readonly now: number;
30
+ }): ReadonlyArray<WorkerEffectDispatchPlan>;
31
+ /**
32
+ * Hand a planned attempt to the worker. A rejected dispatch may still have reached the worker, so
33
+ * with a schedule-to-start deadline it is recorded as dispatched and recovered by that timeout;
34
+ * without one it is parked as ambiguous for an operator.
35
+ */
36
+ export declare function dispatchWorkerEffect(runId: string, plan: WorkerEffectDispatchPlan, worker: EffectWorker | undefined): Effect.Effect<EventEnvelope>;