@swirls/looms 0.0.1

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 (270) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +9 -0
  3. package/dist/actor/cell.d.ts +27 -0
  4. package/dist/actor/cell.js +41 -0
  5. package/dist/actor/host.d.ts +27 -0
  6. package/dist/actor/host.js +82 -0
  7. package/dist/actor/index.d.ts +3 -0
  8. package/dist/actor/index.js +3 -0
  9. package/dist/actor/routing.d.ts +15 -0
  10. package/dist/actor/routing.js +80 -0
  11. package/dist/agent/definitions-store.d.ts +11 -0
  12. package/dist/agent/definitions-store.js +10 -0
  13. package/dist/agent/definitions.d.ts +152 -0
  14. package/dist/agent/definitions.js +74 -0
  15. package/dist/agent/effects.d.ts +425 -0
  16. package/dist/agent/effects.js +318 -0
  17. package/dist/agent/events.d.ts +170 -0
  18. package/dist/agent/events.js +61 -0
  19. package/dist/agent/index.d.ts +13 -0
  20. package/dist/agent/index.js +13 -0
  21. package/dist/agent/llm.d.ts +35 -0
  22. package/dist/agent/llm.js +48 -0
  23. package/dist/agent/module.d.ts +1037 -0
  24. package/dist/agent/module.js +27 -0
  25. package/dist/agent/projections.d.ts +17 -0
  26. package/dist/agent/projections.js +81 -0
  27. package/dist/agent/scope.d.ts +171 -0
  28. package/dist/agent/scope.js +7 -0
  29. package/dist/agent/signals.d.ts +17 -0
  30. package/dist/agent/signals.js +19 -0
  31. package/dist/agent/stop-when.d.ts +12 -0
  32. package/dist/agent/stop-when.js +6 -0
  33. package/dist/agent/threads.d.ts +34 -0
  34. package/dist/agent/threads.js +407 -0
  35. package/dist/agent/tool-schema.d.ts +21 -0
  36. package/dist/agent/tool-schema.js +67 -0
  37. package/dist/agent/types.d.ts +87 -0
  38. package/dist/agent/types.js +32 -0
  39. package/dist/ai-vercel/index.d.ts +2 -0
  40. package/dist/ai-vercel/index.js +2 -0
  41. package/dist/ai-vercel/messages.d.ts +9 -0
  42. package/dist/ai-vercel/messages.js +68 -0
  43. package/dist/ai-vercel/vercel-llm.d.ts +8 -0
  44. package/dist/ai-vercel/vercel-llm.js +98 -0
  45. package/dist/approval/effects.d.ts +133 -0
  46. package/dist/approval/effects.js +13 -0
  47. package/dist/approval/events.d.ts +52 -0
  48. package/dist/approval/events.js +28 -0
  49. package/dist/approval/index.d.ts +6 -0
  50. package/dist/approval/index.js +6 -0
  51. package/dist/approval/module.d.ts +342 -0
  52. package/dist/approval/module.js +10 -0
  53. package/dist/approval/projections.d.ts +42 -0
  54. package/dist/approval/projections.js +57 -0
  55. package/dist/approval/scope.d.ts +45 -0
  56. package/dist/approval/scope.js +7 -0
  57. package/dist/approval/signals.d.ts +19 -0
  58. package/dist/approval/signals.js +34 -0
  59. package/dist/cli/cli.d.ts +2 -0
  60. package/dist/cli/cli.js +6 -0
  61. package/dist/cli/commands.d.ts +13 -0
  62. package/dist/cli/commands.js +133 -0
  63. package/dist/cli/index.d.ts +1 -0
  64. package/dist/cli/index.js +1 -0
  65. package/dist/client/client.d.ts +69 -0
  66. package/dist/client/client.js +179 -0
  67. package/dist/client/index.d.ts +2 -0
  68. package/dist/client/index.js +2 -0
  69. package/dist/client/sse.d.ts +10 -0
  70. package/dist/client/sse.js +103 -0
  71. package/dist/cloudflare/alarm-scheduler.d.ts +9 -0
  72. package/dist/cloudflare/alarm-scheduler.js +13 -0
  73. package/dist/cloudflare/durable-object.d.ts +24 -0
  74. package/dist/cloudflare/durable-object.js +43 -0
  75. package/dist/cloudflare/event-store.d.ts +10 -0
  76. package/dist/cloudflare/event-store.js +18 -0
  77. package/dist/cloudflare/index.d.ts +4 -0
  78. package/dist/cloudflare/index.js +4 -0
  79. package/dist/cloudflare/router.d.ts +12 -0
  80. package/dist/cloudflare/router.js +26 -0
  81. package/dist/core/bun-sqlite.d.ts +13 -0
  82. package/dist/core/bun-sqlite.js +19 -0
  83. package/dist/core/catalog.d.ts +49 -0
  84. package/dist/core/catalog.js +39 -0
  85. package/dist/core/coalesce.d.ts +21 -0
  86. package/dist/core/coalesce.js +178 -0
  87. package/dist/core/compose.d.ts +59 -0
  88. package/dist/core/compose.js +97 -0
  89. package/dist/core/cursor.d.ts +16 -0
  90. package/dist/core/cursor.js +46 -0
  91. package/dist/core/effects.d.ts +110 -0
  92. package/dist/core/effects.js +99 -0
  93. package/dist/core/envelope.d.ts +79 -0
  94. package/dist/core/envelope.js +68 -0
  95. package/dist/core/event-codec.d.ts +8 -0
  96. package/dist/core/event-codec.js +82 -0
  97. package/dist/core/event-index.d.ts +28 -0
  98. package/dist/core/event-index.js +86 -0
  99. package/dist/core/fold.d.ts +19 -0
  100. package/dist/core/fold.js +362 -0
  101. package/dist/core/ids.d.ts +11 -0
  102. package/dist/core/ids.js +47 -0
  103. package/dist/core/index.d.ts +26 -0
  104. package/dist/core/index.js +25 -0
  105. package/dist/core/match.d.ts +8 -0
  106. package/dist/core/match.js +58 -0
  107. package/dist/core/module-scope.d.ts +99 -0
  108. package/dist/core/module-scope.js +72 -0
  109. package/dist/core/module.d.ts +74 -0
  110. package/dist/core/module.js +3 -0
  111. package/dist/core/projection.d.ts +83 -0
  112. package/dist/core/projection.js +241 -0
  113. package/dist/core/protocol.d.ts +99 -0
  114. package/dist/core/protocol.js +101 -0
  115. package/dist/core/replay.d.ts +13 -0
  116. package/dist/core/replay.js +34 -0
  117. package/dist/core/schema.d.ts +35 -0
  118. package/dist/core/schema.js +130 -0
  119. package/dist/core/serialize.d.ts +14 -0
  120. package/dist/core/serialize.js +42 -0
  121. package/dist/core/snapshot-store.d.ts +39 -0
  122. package/dist/core/snapshot-store.js +53 -0
  123. package/dist/core/snapshots.d.ts +26 -0
  124. package/dist/core/snapshots.js +100 -0
  125. package/dist/core/sqlite-store.d.ts +31 -0
  126. package/dist/core/sqlite-store.js +328 -0
  127. package/dist/core/state.d.ts +43 -0
  128. package/dist/core/state.js +31 -0
  129. package/dist/core/store.d.ts +146 -0
  130. package/dist/core/store.js +229 -0
  131. package/dist/core/thread.d.ts +41 -0
  132. package/dist/core/thread.js +13 -0
  133. package/dist/core/types.d.ts +27 -0
  134. package/dist/core/types.js +42 -0
  135. package/dist/core/validate-event.d.ts +19 -0
  136. package/dist/core/validate-event.js +42 -0
  137. package/dist/debugger/components/debugger-split.d.ts +23 -0
  138. package/dist/debugger/components/debugger-split.js +25 -0
  139. package/dist/debugger/components/event-inspector.d.ts +5 -0
  140. package/dist/debugger/components/event-inspector.js +38 -0
  141. package/dist/debugger/components/event-stream.d.ts +13 -0
  142. package/dist/debugger/components/event-stream.js +119 -0
  143. package/dist/debugger/components/json-view.d.ts +5 -0
  144. package/dist/debugger/components/json-view.js +5 -0
  145. package/dist/debugger/components/run-tree.d.ts +14 -0
  146. package/dist/debugger/components/run-tree.js +38 -0
  147. package/dist/debugger/components/status-dot.d.ts +3 -0
  148. package/dist/debugger/components/status-dot.js +5 -0
  149. package/dist/debugger/contracts.d.ts +22 -0
  150. package/dist/debugger/contracts.js +1 -0
  151. package/dist/debugger/events/catalog.d.ts +6 -0
  152. package/dist/debugger/events/catalog.js +27 -0
  153. package/dist/debugger/events/domain-summarize.d.ts +3 -0
  154. package/dist/debugger/events/domain-summarize.js +107 -0
  155. package/dist/debugger/events/family.d.ts +8 -0
  156. package/dist/debugger/events/family.js +44 -0
  157. package/dist/debugger/events/protocol-summarize.d.ts +3 -0
  158. package/dist/debugger/events/protocol-summarize.js +70 -0
  159. package/dist/debugger/index.d.ts +17 -0
  160. package/dist/debugger/index.js +16 -0
  161. package/dist/debugger/lib/cn.d.ts +5 -0
  162. package/dist/debugger/lib/cn.js +17 -0
  163. package/dist/debugger/lib/event-counts.d.ts +5 -0
  164. package/dist/debugger/lib/event-counts.js +23 -0
  165. package/dist/debugger/lib/event-meta.d.ts +8 -0
  166. package/dist/debugger/lib/event-meta.js +20 -0
  167. package/dist/debugger/lib/project-run.d.ts +9 -0
  168. package/dist/debugger/lib/project-run.js +14 -0
  169. package/dist/debugger/lib/status.d.ts +3 -0
  170. package/dist/debugger/lib/status.js +31 -0
  171. package/dist/debugger/lib/visible-events.d.ts +8 -0
  172. package/dist/debugger/lib/visible-events.js +21 -0
  173. package/dist/debugger/styles.css +71 -0
  174. package/dist/debugger/ui/checkbox.d.ts +4 -0
  175. package/dist/debugger/ui/checkbox.js +9 -0
  176. package/dist/debugger/ui/input.d.ts +3 -0
  177. package/dist/debugger/ui/input.js +7 -0
  178. package/dist/debugger/ui/scroll-area.d.ts +9 -0
  179. package/dist/debugger/ui/scroll-area.js +10 -0
  180. package/dist/debugger/ui/tabs.d.ts +7 -0
  181. package/dist/debugger/ui/tabs.js +17 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +2 -0
  184. package/dist/projectors/index-model.d.ts +56 -0
  185. package/dist/projectors/index-model.js +117 -0
  186. package/dist/projectors/index.d.ts +4 -0
  187. package/dist/projectors/index.js +3 -0
  188. package/dist/projectors/memory.d.ts +5 -0
  189. package/dist/projectors/memory.js +77 -0
  190. package/dist/projectors/postgres.d.ts +9 -0
  191. package/dist/projectors/postgres.js +108 -0
  192. package/dist/projectors/projector.d.ts +11 -0
  193. package/dist/projectors/projector.js +1 -0
  194. package/dist/projectors/schema.d.ts +5 -0
  195. package/dist/projectors/schema.js +43 -0
  196. package/dist/projectors/sqlite.d.ts +9 -0
  197. package/dist/projectors/sqlite.js +103 -0
  198. package/dist/projectors/with-projectors.d.ts +17 -0
  199. package/dist/projectors/with-projectors.js +55 -0
  200. package/dist/react/derived.d.ts +19 -0
  201. package/dist/react/derived.js +50 -0
  202. package/dist/react/index.d.ts +6 -0
  203. package/dist/react/index.js +5 -0
  204. package/dist/react/materialize.d.ts +3 -0
  205. package/dist/react/materialize.js +86 -0
  206. package/dist/react/notify.d.ts +18 -0
  207. package/dist/react/notify.js +80 -0
  208. package/dist/react/react.d.ts +61 -0
  209. package/dist/react/react.js +187 -0
  210. package/dist/react/register.d.ts +31 -0
  211. package/dist/react/register.js +1 -0
  212. package/dist/react/sse.d.ts +1 -0
  213. package/dist/react/sse.js +1 -0
  214. package/dist/react/store.d.ts +56 -0
  215. package/dist/react/store.js +340 -0
  216. package/dist/react/tables.d.ts +21 -0
  217. package/dist/react/tables.js +6 -0
  218. package/dist/runtime/dispatch-effect.d.ts +3 -0
  219. package/dist/runtime/dispatch-effect.js +113 -0
  220. package/dist/runtime/events-api.d.ts +4 -0
  221. package/dist/runtime/events-api.js +84 -0
  222. package/dist/runtime/index.d.ts +5 -0
  223. package/dist/runtime/index.js +5 -0
  224. package/dist/runtime/looms.d.ts +74 -0
  225. package/dist/runtime/looms.js +109 -0
  226. package/dist/runtime/runtime-helpers.d.ts +51 -0
  227. package/dist/runtime/runtime-helpers.js +230 -0
  228. package/dist/runtime/runtime.d.ts +91 -0
  229. package/dist/runtime/runtime.js +531 -0
  230. package/dist/runtime/server.d.ts +17 -0
  231. package/dist/runtime/server.js +226 -0
  232. package/dist/runtime/sse.d.ts +15 -0
  233. package/dist/runtime/sse.js +96 -0
  234. package/dist/runtime/wake-scheduler.d.ts +8 -0
  235. package/dist/runtime/wake-scheduler.js +38 -0
  236. package/dist/s2/config.d.ts +27 -0
  237. package/dist/s2/config.js +54 -0
  238. package/dist/s2/index.d.ts +6 -0
  239. package/dist/s2/index.js +6 -0
  240. package/dist/s2/lite.d.ts +26 -0
  241. package/dist/s2/lite.js +150 -0
  242. package/dist/s2/projector.d.ts +8 -0
  243. package/dist/s2/projector.js +28 -0
  244. package/dist/s2/serialize.d.ts +1 -0
  245. package/dist/s2/serialize.js +1 -0
  246. package/dist/s2/snapshot-store.d.ts +17 -0
  247. package/dist/s2/snapshot-store.js +320 -0
  248. package/dist/s2/store.d.ts +35 -0
  249. package/dist/s2/store.js +337 -0
  250. package/dist/testing/index.d.ts +11 -0
  251. package/dist/testing/index.js +63 -0
  252. package/dist/workflow/definitions-store.d.ts +11 -0
  253. package/dist/workflow/definitions-store.js +8 -0
  254. package/dist/workflow/definitions.d.ts +82 -0
  255. package/dist/workflow/definitions.js +32 -0
  256. package/dist/workflow/effects.d.ts +305 -0
  257. package/dist/workflow/effects.js +208 -0
  258. package/dist/workflow/events.d.ts +60 -0
  259. package/dist/workflow/events.js +39 -0
  260. package/dist/workflow/index.d.ts +8 -0
  261. package/dist/workflow/index.js +8 -0
  262. package/dist/workflow/module.d.ts +926 -0
  263. package/dist/workflow/module.js +19 -0
  264. package/dist/workflow/projections.d.ts +30 -0
  265. package/dist/workflow/projections.js +43 -0
  266. package/dist/workflow/scope.d.ts +61 -0
  267. package/dist/workflow/scope.js +7 -0
  268. package/dist/workflow/threads.d.ts +171 -0
  269. package/dist/workflow/threads.js +287 -0
  270. package/package.json +183 -0
@@ -0,0 +1,19 @@
1
+ import { defineModule } from '@swirls/looms/core';
2
+ import { WorkflowDefinitionsLive } from './definitions-store.js';
3
+ import { runNodeEffect, scheduleEffect } from './effects.js';
4
+ import { nodes } from './projections.js';
5
+ import { workflowModule } from './scope.js';
6
+ import { workflowThread } from './threads.js';
7
+ export function workflow(options = {}) {
8
+ const definitions = options.definitions ?? [];
9
+ return defineModule(workflowModule, () => ({
10
+ definitions,
11
+ threads: { workflow: workflowThread },
12
+ effects: {
13
+ schedule: scheduleEffect,
14
+ runNode: runNodeEffect,
15
+ },
16
+ projections: { nodes },
17
+ services: () => WorkflowDefinitionsLive(definitions),
18
+ }));
19
+ }
@@ -0,0 +1,30 @@
1
+ import { Schema } from 'effect';
2
+ export declare const NodesProjectionSchema: Schema.Struct<{
3
+ readonly nodes: Schema.$Record<Schema.String, Schema.Struct<{
4
+ readonly status: Schema.Union<readonly [Schema.Literal<"pending">, Schema.Literal<"running">, Schema.Literal<"completed">, Schema.Literal<"failed">, Schema.Literal<"skipped">]>;
5
+ readonly result: Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
6
+ readonly error: Schema.NullOr<Schema.String>;
7
+ }>>;
8
+ }>;
9
+ export type NodesProjectionState = Schema.Schema.Type<typeof NodesProjectionSchema>;
10
+ export declare const nodes: import("@swirls/looms/core").ProjectionDefinition<{
11
+ readonly nodes: {
12
+ readonly [x: string]: {
13
+ readonly status: "completed" | "failed" | "pending" | "running" | "skipped";
14
+ readonly result: Schema.Json;
15
+ readonly error: string | null;
16
+ };
17
+ };
18
+ } | {
19
+ nodes: {
20
+ [x: string]: {
21
+ readonly status: "completed" | "failed" | "pending" | "running" | "skipped";
22
+ readonly result: Schema.Json;
23
+ readonly error: string | null;
24
+ } | {
25
+ status: string;
26
+ result: string | number | boolean | Schema.JsonArray | Schema.JsonObject | null;
27
+ error: string | null;
28
+ };
29
+ };
30
+ }>;
@@ -0,0 +1,43 @@
1
+ import { Predicate, Schema } from 'effect';
2
+ import { NodeStateSchema } from './definitions.js';
3
+ import { workflowModule } from './scope.js';
4
+ export const NodesProjectionSchema = Schema.Struct({
5
+ nodes: Schema.Record(Schema.String, NodeStateSchema),
6
+ });
7
+ export const nodes = workflowModule.projection({
8
+ name: 'nodes',
9
+ shape: NodesProjectionSchema,
10
+ initialState: { nodes: {} },
11
+ reduce(state, event) {
12
+ switch (event.type) {
13
+ case 'workflow.node.started': {
14
+ const { nodeId } = event.payload;
15
+ return {
16
+ nodes: { ...state.nodes, [nodeId]: { status: 'running', result: null, error: null } },
17
+ };
18
+ }
19
+ case 'workflow.node.finished': {
20
+ const { nodeId, result, error } = event.payload;
21
+ const failed = Predicate.isString(error) && error.length > 0;
22
+ return {
23
+ nodes: {
24
+ ...state.nodes,
25
+ [nodeId]: {
26
+ status: failed ? 'failed' : 'completed',
27
+ result: result ?? null,
28
+ error: failed ? error : null,
29
+ },
30
+ },
31
+ };
32
+ }
33
+ case 'workflow.node.skipped': {
34
+ const { nodeId } = event.payload;
35
+ return {
36
+ nodes: { ...state.nodes, [nodeId]: { status: 'skipped', result: null, error: null } },
37
+ };
38
+ }
39
+ default:
40
+ return state;
41
+ }
42
+ },
43
+ });
@@ -0,0 +1,61 @@
1
+ import { type EventOf } from '@swirls/looms/core';
2
+ export declare const workflowModule: import("@swirls/looms/core").ModuleScope<"workflow", import("@swirls/looms/core").ScopeUniverse<import("@swirls/looms/core").EventCatalog<"workflow", {
3
+ 'node.started': import("effect/Schema").Struct<{
4
+ readonly nodeId: import("effect/Schema").String;
5
+ }>;
6
+ 'node.finished': import("effect/Schema").Struct<{
7
+ readonly nodeId: import("effect/Schema").String;
8
+ readonly result: import("effect/Schema").NullOr<import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>>;
9
+ readonly error: import("effect/Schema").NullOr<import("effect/Schema").String>;
10
+ }>;
11
+ 'node.skipped': import("effect/Schema").Struct<{
12
+ readonly nodeId: import("effect/Schema").String;
13
+ readonly reason: import("effect/Schema").String;
14
+ }>;
15
+ 'spawn.requested': import("effect/Schema").Struct<{
16
+ readonly nodeId: import("effect/Schema").String;
17
+ readonly childThreadId: import("effect/Schema").String;
18
+ readonly kind: import("effect/Schema").String;
19
+ readonly definitionName: import("effect/Schema").String;
20
+ readonly input: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
21
+ }>;
22
+ 'sleep.requested': import("effect/Schema").Struct<{
23
+ readonly nodeId: import("effect/Schema").String;
24
+ readonly waitId: import("effect/Schema").String;
25
+ readonly wakeAt: import("effect/Schema").Number;
26
+ }>;
27
+ 'effects.requested': import("effect/Schema").Struct<{
28
+ readonly nodeId: import("effect/Schema").String;
29
+ readonly effects: import("effect/Schema").$Array<import("effect/Schema").Schema<import("@swirls/looms/core").RuntimeEffect>>;
30
+ }>;
31
+ }>, undefined>, import("@swirls/looms/core").EventCatalog<"workflow", {
32
+ 'node.started': import("effect/Schema").Struct<{
33
+ readonly nodeId: import("effect/Schema").String;
34
+ }>;
35
+ 'node.finished': import("effect/Schema").Struct<{
36
+ readonly nodeId: import("effect/Schema").String;
37
+ readonly result: import("effect/Schema").NullOr<import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>>;
38
+ readonly error: import("effect/Schema").NullOr<import("effect/Schema").String>;
39
+ }>;
40
+ 'node.skipped': import("effect/Schema").Struct<{
41
+ readonly nodeId: import("effect/Schema").String;
42
+ readonly reason: import("effect/Schema").String;
43
+ }>;
44
+ 'spawn.requested': import("effect/Schema").Struct<{
45
+ readonly nodeId: import("effect/Schema").String;
46
+ readonly childThreadId: import("effect/Schema").String;
47
+ readonly kind: import("effect/Schema").String;
48
+ readonly definitionName: import("effect/Schema").String;
49
+ readonly input: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
50
+ }>;
51
+ 'sleep.requested': import("effect/Schema").Struct<{
52
+ readonly nodeId: import("effect/Schema").String;
53
+ readonly waitId: import("effect/Schema").String;
54
+ readonly wakeAt: import("effect/Schema").Number;
55
+ }>;
56
+ 'effects.requested': import("effect/Schema").Struct<{
57
+ readonly nodeId: import("effect/Schema").String;
58
+ readonly effects: import("effect/Schema").$Array<import("effect/Schema").Schema<import("@swirls/looms/core").RuntimeEffect>>;
59
+ }>;
60
+ }>, undefined>;
61
+ export type WorkflowEvent = EventOf<typeof workflowModule>;
@@ -0,0 +1,7 @@
1
+ import { createModuleScope } from '@swirls/looms/core';
2
+ import { workflowCatalog } from './events.js';
3
+ export const workflowModule = createModuleScope({
4
+ namespace: 'workflow',
5
+ protocolVersion: '1.0.0',
6
+ events: workflowCatalog,
7
+ });
@@ -0,0 +1,171 @@
1
+ import { Schema } from 'effect';
2
+ import { type EventInput, type JsonValue, type RuntimeEffect } from '@swirls/looms/core';
3
+ import { type NodeState } from './definitions';
4
+ export { NodeStateSchema, NodeStatusSchema, type NodeState, type NodeStatus } from './definitions';
5
+ export interface WorkflowPendingSpawn {
6
+ readonly childThreadId: string;
7
+ readonly kind: string;
8
+ readonly definitionName: string;
9
+ readonly nodeId: string;
10
+ readonly input: JsonValue;
11
+ }
12
+ export interface WorkflowPendingSleep {
13
+ readonly waitId: string;
14
+ readonly wakeAt: number;
15
+ readonly nodeId: string;
16
+ }
17
+ export interface WorkflowPendingEffects {
18
+ readonly nodeId: string;
19
+ readonly effects: RuntimeEffect[];
20
+ }
21
+ export interface WorkflowPendingEmit {
22
+ readonly id: string;
23
+ readonly event: EventInput;
24
+ }
25
+ export interface WorkflowState {
26
+ definitionName?: string;
27
+ nodes: {
28
+ [nodeId: string]: NodeState;
29
+ };
30
+ concurrency: number;
31
+ input: JsonValue;
32
+ nodeIds: string[];
33
+ needsSchedule: boolean;
34
+ runningNodes: string[];
35
+ pendingSpawns: WorkflowPendingSpawn[];
36
+ pendingSleeps: WorkflowPendingSleep[];
37
+ pendingEffects: WorkflowPendingEffects[];
38
+ pendingEmits: WorkflowPendingEmit[];
39
+ status: 'running' | 'completed' | 'failed';
40
+ error: string | null;
41
+ }
42
+ export declare const WorkflowStateSchema: Schema.Schema<WorkflowState>;
43
+ export declare const workflowThread: import("@swirls/looms/core").ThreadDefinition<{
44
+ definitionName?: string;
45
+ concurrency: number;
46
+ input: JsonValue;
47
+ nodeIds: string[];
48
+ nodes: {
49
+ [x: string]: {
50
+ readonly status: "completed" | "failed" | "pending" | "running" | "skipped";
51
+ readonly result: Schema.Json;
52
+ readonly error: string | null;
53
+ } | {
54
+ status: string;
55
+ result: string | number | boolean | Schema.JsonArray | Schema.JsonObject | null;
56
+ error: string | null;
57
+ };
58
+ };
59
+ runningNodes: string[];
60
+ pendingSpawns: WorkflowPendingSpawn[];
61
+ pendingSleeps: WorkflowPendingSleep[];
62
+ pendingEffects: WorkflowPendingEffects[];
63
+ pendingEmits: WorkflowPendingEmit[];
64
+ status: "completed" | "failed" | "running";
65
+ error: string | null;
66
+ needsSchedule: boolean;
67
+ } | {
68
+ definitionName?: string;
69
+ nodes: {
70
+ [nodeId: string]: NodeState;
71
+ };
72
+ concurrency: number;
73
+ input: JsonValue;
74
+ nodeIds: string[];
75
+ needsSchedule: boolean;
76
+ runningNodes: string[];
77
+ pendingSpawns: WorkflowPendingSpawn[];
78
+ pendingSleeps: WorkflowPendingSleep[];
79
+ pendingEmits: WorkflowPendingEmit[];
80
+ status: 'running' | 'completed' | 'failed';
81
+ error: string | null;
82
+ pendingEffects: (WorkflowPendingEffects | {
83
+ nodeId: string;
84
+ effects: (import("@swirls/looms/core").InvokeEffect | import("@swirls/looms/core").WaitEffect | {
85
+ type: 'runtime.wait';
86
+ waitId: string;
87
+ on: import("@swirls/looms/core").WaitCondition;
88
+ tag: {
89
+ nodeId: string;
90
+ };
91
+ } | {
92
+ type: 'runtime.cancel';
93
+ threadId: string;
94
+ tag: string;
95
+ } | {
96
+ type: 'runtime.complete';
97
+ output: JsonValue;
98
+ tag: string;
99
+ } | {
100
+ type: 'runtime.emit';
101
+ event: EventInput;
102
+ tag: string;
103
+ } | {
104
+ type: 'runtime.fail';
105
+ error: string;
106
+ tag: string;
107
+ })[];
108
+ })[];
109
+ } | {
110
+ definitionName?: string;
111
+ concurrency: number;
112
+ input: JsonValue;
113
+ nodeIds: string[];
114
+ needsSchedule: boolean;
115
+ status: 'running' | 'completed' | 'failed';
116
+ error: string | null;
117
+ nodes: {
118
+ [x: string]: {
119
+ readonly status: "completed" | "failed" | "pending" | "running" | "skipped";
120
+ readonly result: Schema.Json;
121
+ readonly error: string | null;
122
+ } | {
123
+ status: string;
124
+ result: null;
125
+ error: string;
126
+ };
127
+ };
128
+ runningNodes: string[];
129
+ pendingSpawns: WorkflowPendingSpawn[];
130
+ pendingSleeps: WorkflowPendingSleep[];
131
+ pendingEffects: WorkflowPendingEffects[];
132
+ pendingEmits: (WorkflowPendingEmit | {
133
+ id: string;
134
+ event: {
135
+ type: string;
136
+ payload: {
137
+ nodeId: string;
138
+ result: null;
139
+ error: string;
140
+ };
141
+ threadId: string;
142
+ };
143
+ })[];
144
+ } | {
145
+ definitionName?: string;
146
+ nodes: {
147
+ [nodeId: string]: NodeState;
148
+ };
149
+ concurrency: number;
150
+ input: JsonValue;
151
+ nodeIds: string[];
152
+ needsSchedule: boolean;
153
+ runningNodes: string[];
154
+ status: 'running' | 'completed' | 'failed';
155
+ error: string | null;
156
+ pendingSpawns: WorkflowPendingSpawn[];
157
+ pendingSleeps: WorkflowPendingSleep[];
158
+ pendingEffects: WorkflowPendingEffects[];
159
+ pendingEmits: (WorkflowPendingEmit | {
160
+ id: string;
161
+ event: {
162
+ type: string;
163
+ payload: {
164
+ nodeId: string;
165
+ result: string | number | boolean | import("@swirls/looms/core").JsonObject | readonly JsonValue[] | null;
166
+ error: string | null;
167
+ };
168
+ threadId: string;
169
+ };
170
+ })[];
171
+ }>;
@@ -0,0 +1,287 @@
1
+ import { Predicate, Schema } from 'effect';
2
+ import { asJson, createWaitId, emit, fail, invoke, isWithdrawnError, spawn, wait, } from '@swirls/looms/core';
3
+ import {} from './definitions.js';
4
+ import { runNodeEffect, scheduleEffect } from './effects.js';
5
+ import { workflowModule } from './scope.js';
6
+ export { NodeStateSchema, NodeStatusSchema } from './definitions.js';
7
+ // SAFETY: WorkflowState is folded by reducers; Schema.Unknown is a typed placeholder, not a decoder.
8
+ export const WorkflowStateSchema = Schema.Unknown;
9
+ function scheduleInvocation(state) {
10
+ return invoke(scheduleEffect, {
11
+ definitionName: state.definitionName,
12
+ nodes: state.nodes,
13
+ input: state.input,
14
+ });
15
+ }
16
+ function runNodeInvocation(state, nodeId) {
17
+ const results = {};
18
+ for (const [id, node] of Object.entries(state.nodes)) {
19
+ results[id] = node.result;
20
+ }
21
+ return invoke(runNodeEffect, {
22
+ definitionName: state.definitionName,
23
+ nodeId,
24
+ input: state.input,
25
+ results,
26
+ }, `runNode_${nodeId}`);
27
+ }
28
+ export const workflowThread = workflowModule.thread({
29
+ kind: 'workflow',
30
+ shape: WorkflowStateSchema,
31
+ initialState: (ctx) => ({
32
+ definitionName: ctx.definitionName,
33
+ nodes: {},
34
+ concurrency: 8,
35
+ input: ctx.input,
36
+ nodeIds: [],
37
+ needsSchedule: false,
38
+ runningNodes: [],
39
+ pendingSpawns: [],
40
+ pendingSleeps: [],
41
+ pendingEffects: [],
42
+ pendingEmits: [],
43
+ status: 'running',
44
+ error: null,
45
+ }),
46
+ step(state, event, ctx) {
47
+ switch (event.type) {
48
+ case 'runtime.thread.started': {
49
+ const payload = event.payload;
50
+ const defName = payload.definitionName || state.definitionName;
51
+ const inputObj = Predicate.isObject(payload.input) ? payload.input : {};
52
+ const nodeIds = Array.isArray(inputObj.nodeIds)
53
+ ? inputObj.nodeIds.filter(Predicate.isString)
54
+ : state.nodeIds;
55
+ const nodes = {};
56
+ for (const nodeId of nodeIds) {
57
+ nodes[nodeId] = { status: 'pending', result: null, error: null };
58
+ }
59
+ return {
60
+ ...state,
61
+ definitionName: defName,
62
+ nodes,
63
+ nodeIds,
64
+ input: payload.input ?? state.input,
65
+ needsSchedule: true,
66
+ };
67
+ }
68
+ case 'workflow.node.started': {
69
+ const nodeId = event.payload.nodeId;
70
+ if (!nodeId) {
71
+ return state;
72
+ }
73
+ return {
74
+ ...state,
75
+ nodes: {
76
+ ...state.nodes,
77
+ [nodeId]: { status: 'running', result: null, error: null },
78
+ },
79
+ runningNodes: state.runningNodes.includes(nodeId)
80
+ ? state.runningNodes
81
+ : [...state.runningNodes, nodeId],
82
+ needsSchedule: false,
83
+ };
84
+ }
85
+ case 'workflow.node.finished': {
86
+ const { nodeId, result, error } = event.payload;
87
+ if (!nodeId) {
88
+ return state;
89
+ }
90
+ const failed = Predicate.isString(error) && error.length > 0;
91
+ return {
92
+ ...state,
93
+ nodes: {
94
+ ...state.nodes,
95
+ [nodeId]: {
96
+ status: failed ? 'failed' : 'completed',
97
+ result: result ?? null,
98
+ error: failed ? error : null,
99
+ },
100
+ },
101
+ runningNodes: state.runningNodes.filter((id) => id !== nodeId),
102
+ pendingSpawns: state.pendingSpawns.filter((item) => item.nodeId !== nodeId),
103
+ pendingSleeps: state.pendingSleeps.filter((item) => item.nodeId !== nodeId),
104
+ pendingEffects: state.pendingEffects.filter((item) => item.nodeId !== nodeId),
105
+ pendingEmits: state.pendingEmits.filter((item) => item.id !== `finished_${nodeId}`),
106
+ status: failed ? 'failed' : state.status,
107
+ error: failed ? error : state.error,
108
+ needsSchedule: !failed,
109
+ };
110
+ }
111
+ case 'workflow.spawn.requested': {
112
+ const { childThreadId, kind, definitionName, nodeId, input } = event.payload;
113
+ if (!childThreadId || !kind || !definitionName || !nodeId) {
114
+ return state;
115
+ }
116
+ return {
117
+ ...state,
118
+ pendingSpawns: [
119
+ ...state.pendingSpawns.filter((item) => item.nodeId !== nodeId),
120
+ {
121
+ childThreadId,
122
+ kind,
123
+ definitionName,
124
+ nodeId,
125
+ input: input ?? null,
126
+ },
127
+ ],
128
+ };
129
+ }
130
+ case 'workflow.sleep.requested': {
131
+ const { waitId, wakeAt, nodeId } = event.payload;
132
+ if (!waitId || !Predicate.isNumber(wakeAt) || !nodeId) {
133
+ return state;
134
+ }
135
+ return {
136
+ ...state,
137
+ pendingSleeps: [
138
+ ...state.pendingSleeps.filter((item) => item.nodeId !== nodeId),
139
+ { waitId, wakeAt, nodeId },
140
+ ],
141
+ };
142
+ }
143
+ case 'workflow.effects.requested': {
144
+ const { nodeId, effects: rawEffects } = event.payload;
145
+ const effects = Array.isArray(rawEffects) ? rawEffects : [];
146
+ if (!nodeId) {
147
+ return state;
148
+ }
149
+ const taggedEffects = effects.map((effect, idx) => {
150
+ if (effect.type === 'runtime.wait' && 'on' in effect) {
151
+ const tag = Predicate.isObject(effect.tag) ? { ...effect.tag, nodeId } : { nodeId };
152
+ return { ...effect, tag };
153
+ }
154
+ if (!('tag' in effect && Predicate.isString(effect.tag))) {
155
+ return { ...effect, tag: `${nodeId}_${effect.type}_${idx}` };
156
+ }
157
+ return effect;
158
+ });
159
+ return {
160
+ ...state,
161
+ pendingEffects: [
162
+ ...state.pendingEffects.filter((item) => item.nodeId !== nodeId),
163
+ { nodeId, effects: taggedEffects },
164
+ ],
165
+ };
166
+ }
167
+ case 'runtime.effect.failed': {
168
+ const error = event.payload.error || 'effect failed';
169
+ if (isWithdrawnError(error)) {
170
+ return state;
171
+ }
172
+ const running = Object.entries(state.nodes).find(([, node]) => node.status === 'running');
173
+ if (!running) {
174
+ return state;
175
+ }
176
+ const nodeId = running[0];
177
+ const message = `Node ${nodeId} failed: ${error}`;
178
+ return {
179
+ ...state,
180
+ nodes: {
181
+ ...state.nodes,
182
+ [nodeId]: { status: 'failed', result: null, error: message },
183
+ },
184
+ runningNodes: state.runningNodes.filter((id) => id !== nodeId),
185
+ pendingSpawns: state.pendingSpawns.filter((item) => item.nodeId !== nodeId),
186
+ pendingSleeps: state.pendingSleeps.filter((item) => item.nodeId !== nodeId),
187
+ pendingEffects: state.pendingEffects.filter((item) => item.nodeId !== nodeId),
188
+ pendingEmits: [
189
+ ...state.pendingEmits.filter((item) => item.id !== `finished_${nodeId}`),
190
+ {
191
+ id: `finished_${nodeId}`,
192
+ event: {
193
+ type: 'workflow.node.finished',
194
+ payload: { nodeId, result: null, error: message },
195
+ threadId: ctx.threadId,
196
+ },
197
+ },
198
+ ],
199
+ };
200
+ }
201
+ case 'runtime.wait.satisfied': {
202
+ const tag = event.payload.tag;
203
+ if (!Predicate.isObject(tag)) {
204
+ return state;
205
+ }
206
+ const nodeId = Predicate.isString(tag.nodeId) ? tag.nodeId : undefined;
207
+ if (!nodeId) {
208
+ return state;
209
+ }
210
+ const embedded = event.payload.event;
211
+ const embeddedObj = Predicate.isObject(embedded) && Predicate.isObject(embedded.payload)
212
+ ? embedded.payload
213
+ : {};
214
+ const error = Predicate.isString(embeddedObj.error) ? embeddedObj.error : null;
215
+ const result = embeddedObj.output ?? (Predicate.isObject(embedded) ? embedded.payload : { waited: true });
216
+ return {
217
+ ...state,
218
+ pendingSpawns: state.pendingSpawns.filter((item) => item.nodeId !== nodeId),
219
+ pendingSleeps: state.pendingSleeps.filter((item) => item.nodeId !== nodeId),
220
+ pendingEffects: state.pendingEffects.filter((item) => item.nodeId !== nodeId),
221
+ pendingEmits: [
222
+ ...state.pendingEmits.filter((item) => item.id !== `finished_${nodeId}`),
223
+ {
224
+ id: `finished_${nodeId}`,
225
+ event: {
226
+ type: 'workflow.node.finished',
227
+ payload: {
228
+ nodeId,
229
+ // SAFETY: result is serialized to a JSON-compatible node execution result.
230
+ result: asJson(result) ?? null,
231
+ error,
232
+ },
233
+ threadId: ctx.threadId,
234
+ },
235
+ },
236
+ ],
237
+ };
238
+ }
239
+ default:
240
+ return state;
241
+ }
242
+ },
243
+ effects(state, ctx) {
244
+ const effects = [];
245
+ if (state.needsSchedule) {
246
+ effects.push(scheduleInvocation(state));
247
+ }
248
+ for (const nodeId of state.runningNodes) {
249
+ effects.push(runNodeInvocation(state, nodeId));
250
+ }
251
+ for (const spawnReq of state.pendingSpawns) {
252
+ const waitId = createWaitId(ctx.threadId, `spawn_${spawnReq.childThreadId}`);
253
+ effects.push(spawn({
254
+ childThreadId: spawnReq.childThreadId,
255
+ kind: spawnReq.kind,
256
+ definitionName: spawnReq.definitionName,
257
+ input: spawnReq.input,
258
+ }), wait({
259
+ waitId,
260
+ on: {
261
+ type: ['runtime.thread.completed', 'runtime.thread.failed'],
262
+ match: { threadId: spawnReq.childThreadId },
263
+ },
264
+ tag: { nodeId: spawnReq.nodeId },
265
+ }));
266
+ }
267
+ for (const sleepReq of state.pendingSleeps) {
268
+ effects.push(wait({
269
+ waitId: sleepReq.waitId,
270
+ on: { timerAt: sleepReq.wakeAt },
271
+ tag: { nodeId: sleepReq.nodeId },
272
+ }));
273
+ }
274
+ for (const effectReq of state.pendingEffects) {
275
+ for (const eff of effectReq.effects) {
276
+ effects.push(eff);
277
+ }
278
+ }
279
+ for (const emitReq of state.pendingEmits) {
280
+ effects.push(emit(emitReq.event));
281
+ }
282
+ if (state.status === 'failed') {
283
+ effects.push(fail(state.error ?? 'Workflow execution failed'));
284
+ }
285
+ return effects;
286
+ },
287
+ });