ai-workflows 2.1.1 → 2.3.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 (211) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -1
  3. package/README.md +305 -184
  4. package/dist/barrier.d.ts +159 -0
  5. package/dist/barrier.d.ts.map +1 -0
  6. package/dist/barrier.js +377 -0
  7. package/dist/barrier.js.map +1 -0
  8. package/dist/cascade-context.d.ts +149 -0
  9. package/dist/cascade-context.d.ts.map +1 -0
  10. package/dist/cascade-context.js +324 -0
  11. package/dist/cascade-context.js.map +1 -0
  12. package/dist/cascade-executor.d.ts +196 -0
  13. package/dist/cascade-executor.d.ts.map +1 -0
  14. package/dist/cascade-executor.js +384 -0
  15. package/dist/cascade-executor.js.map +1 -0
  16. package/dist/context.d.ts.map +1 -1
  17. package/dist/context.js +27 -8
  18. package/dist/context.js.map +1 -1
  19. package/dist/cron-parser.d.ts +65 -0
  20. package/dist/cron-parser.d.ts.map +1 -0
  21. package/dist/cron-parser.js +294 -0
  22. package/dist/cron-parser.js.map +1 -0
  23. package/dist/cron-scheduler.d.ts +117 -0
  24. package/dist/cron-scheduler.d.ts.map +1 -0
  25. package/dist/cron-scheduler.js +176 -0
  26. package/dist/cron-scheduler.js.map +1 -0
  27. package/dist/database-context.d.ts +184 -0
  28. package/dist/database-context.d.ts.map +1 -0
  29. package/dist/database-context.js +428 -0
  30. package/dist/database-context.js.map +1 -0
  31. package/dist/dependency-graph.d.ts +157 -0
  32. package/dist/dependency-graph.d.ts.map +1 -0
  33. package/dist/dependency-graph.js +382 -0
  34. package/dist/dependency-graph.js.map +1 -0
  35. package/dist/digital-objects-adapter.d.ts +159 -0
  36. package/dist/digital-objects-adapter.d.ts.map +1 -0
  37. package/dist/digital-objects-adapter.js +229 -0
  38. package/dist/digital-objects-adapter.js.map +1 -0
  39. package/dist/durable-execution-cloudflare.d.ts +427 -0
  40. package/dist/durable-execution-cloudflare.d.ts.map +1 -0
  41. package/dist/durable-execution-cloudflare.js +510 -0
  42. package/dist/durable-execution-cloudflare.js.map +1 -0
  43. package/dist/durable-execution.d.ts +482 -0
  44. package/dist/durable-execution.d.ts.map +1 -0
  45. package/dist/durable-execution.js +594 -0
  46. package/dist/durable-execution.js.map +1 -0
  47. package/dist/durable-workflow.d.ts +176 -0
  48. package/dist/durable-workflow.d.ts.map +1 -0
  49. package/dist/durable-workflow.js +552 -0
  50. package/dist/durable-workflow.js.map +1 -0
  51. package/dist/every.d.ts +31 -2
  52. package/dist/every.d.ts.map +1 -1
  53. package/dist/every.js +63 -32
  54. package/dist/every.js.map +1 -1
  55. package/dist/graph/index.d.ts +8 -0
  56. package/dist/graph/index.d.ts.map +1 -0
  57. package/dist/graph/index.js +8 -0
  58. package/dist/graph/index.js.map +1 -0
  59. package/dist/graph/topological-sort.d.ts +121 -0
  60. package/dist/graph/topological-sort.d.ts.map +1 -0
  61. package/dist/graph/topological-sort.js +292 -0
  62. package/dist/graph/topological-sort.js.map +1 -0
  63. package/dist/index.d.ts +10 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +25 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/logger.d.ts +101 -0
  68. package/dist/logger.d.ts.map +1 -0
  69. package/dist/logger.js +115 -0
  70. package/dist/logger.js.map +1 -0
  71. package/dist/on.d.ts +35 -10
  72. package/dist/on.d.ts.map +1 -1
  73. package/dist/on.js +53 -19
  74. package/dist/on.js.map +1 -1
  75. package/dist/runtime.d.ts +169 -0
  76. package/dist/runtime.d.ts.map +1 -0
  77. package/dist/runtime.js +275 -0
  78. package/dist/runtime.js.map +1 -0
  79. package/dist/send.d.ts.map +1 -1
  80. package/dist/send.js +4 -3
  81. package/dist/send.js.map +1 -1
  82. package/dist/telemetry.d.ts +150 -0
  83. package/dist/telemetry.d.ts.map +1 -0
  84. package/dist/telemetry.js +388 -0
  85. package/dist/telemetry.js.map +1 -0
  86. package/dist/timer-registry.d.ts +77 -0
  87. package/dist/timer-registry.d.ts.map +1 -0
  88. package/dist/timer-registry.js +154 -0
  89. package/dist/timer-registry.js.map +1 -0
  90. package/dist/types.d.ts +105 -6
  91. package/dist/types.d.ts.map +1 -1
  92. package/dist/types.js +17 -1
  93. package/dist/types.js.map +1 -1
  94. package/dist/worker/durable-step.d.ts +481 -0
  95. package/dist/worker/durable-step.d.ts.map +1 -0
  96. package/dist/worker/durable-step.js +606 -0
  97. package/dist/worker/durable-step.js.map +1 -0
  98. package/dist/worker/index.d.ts +106 -0
  99. package/dist/worker/index.d.ts.map +1 -0
  100. package/dist/worker/index.js +124 -0
  101. package/dist/worker/index.js.map +1 -0
  102. package/dist/worker/state-adapter.d.ts +230 -0
  103. package/dist/worker/state-adapter.d.ts.map +1 -0
  104. package/dist/worker/state-adapter.js +409 -0
  105. package/dist/worker/state-adapter.js.map +1 -0
  106. package/dist/worker/topological-executor.d.ts +282 -0
  107. package/dist/worker/topological-executor.d.ts.map +1 -0
  108. package/dist/worker/topological-executor.js +396 -0
  109. package/dist/worker/topological-executor.js.map +1 -0
  110. package/dist/worker/workflow-builder.d.ts +286 -0
  111. package/dist/worker/workflow-builder.d.ts.map +1 -0
  112. package/dist/worker/workflow-builder.js +565 -0
  113. package/dist/worker/workflow-builder.js.map +1 -0
  114. package/dist/worker.d.ts +800 -0
  115. package/dist/worker.d.ts.map +1 -0
  116. package/dist/worker.js +2428 -0
  117. package/dist/worker.js.map +1 -0
  118. package/dist/workflow-builder.d.ts +287 -0
  119. package/dist/workflow-builder.d.ts.map +1 -0
  120. package/dist/workflow-builder.js +762 -0
  121. package/dist/workflow-builder.js.map +1 -0
  122. package/dist/workflow.d.ts +14 -30
  123. package/dist/workflow.d.ts.map +1 -1
  124. package/dist/workflow.js +136 -292
  125. package/dist/workflow.js.map +1 -1
  126. package/examples/01-ecommerce-order-pipeline.ts +358 -0
  127. package/examples/02-content-moderation-cascade.ts +454 -0
  128. package/examples/03-scheduled-reporting-dependencies.ts +479 -0
  129. package/examples/04-database-persistence.ts +518 -0
  130. package/examples/README.md +173 -0
  131. package/package.json +21 -4
  132. package/src/__tests__/digital-objects-adapter.test.ts +274 -0
  133. package/src/__tests__/durable-workflow.test.ts +297 -0
  134. package/src/barrier.ts +507 -0
  135. package/src/cascade-context.ts +495 -0
  136. package/src/cascade-executor.ts +588 -0
  137. package/src/context.ts +51 -17
  138. package/src/cron-parser.ts +347 -0
  139. package/src/cron-scheduler.ts +239 -0
  140. package/src/database-context.ts +658 -0
  141. package/src/dependency-graph.ts +518 -0
  142. package/src/digital-objects-adapter.ts +351 -0
  143. package/src/durable-execution-cloudflare.ts +855 -0
  144. package/src/durable-execution.ts +1042 -0
  145. package/src/durable-workflow.ts +717 -0
  146. package/src/every.ts +104 -35
  147. package/src/graph/index.ts +19 -0
  148. package/src/graph/topological-sort.ts +412 -0
  149. package/src/index.ts +147 -0
  150. package/src/logger.ts +148 -0
  151. package/src/on.ts +81 -26
  152. package/src/runtime.ts +436 -0
  153. package/src/send.ts +4 -5
  154. package/src/telemetry.ts +577 -0
  155. package/src/timer-registry.ts +179 -0
  156. package/src/types.ts +146 -10
  157. package/src/worker/durable-step.ts +976 -0
  158. package/src/worker/index.ts +216 -0
  159. package/src/worker/state-adapter.ts +589 -0
  160. package/src/worker/topological-executor.ts +625 -0
  161. package/src/worker/workflow-builder.ts +871 -0
  162. package/src/worker.ts +2906 -0
  163. package/src/workflow-builder.ts +1068 -0
  164. package/src/workflow.ts +199 -355
  165. package/test/barrier-join.test.ts +442 -0
  166. package/test/barrier-unhandled-rejections.test.ts +359 -0
  167. package/test/cascade-context.test.ts +390 -0
  168. package/test/cascade-executor.test.ts +852 -0
  169. package/test/cron-parser.test.ts +314 -0
  170. package/test/cron-scheduler.test.ts +291 -0
  171. package/test/database-context.test.ts +770 -0
  172. package/test/db-provider-adapter.test.ts +862 -0
  173. package/test/dependency-graph.test.ts +512 -0
  174. package/test/durable-execution-cloudflare.test.ts +606 -0
  175. package/test/durable-execution-in-process.test.ts +286 -0
  176. package/test/durable-execution.test.ts +247 -0
  177. package/test/e2e/workflow-scenarios.e2e.test.ts +1039 -0
  178. package/test/graph/topological-sort.test.ts +586 -0
  179. package/test/integration.test.ts +442 -0
  180. package/test/rpc-surface.test.ts +946 -0
  181. package/test/runtime.test.ts +262 -0
  182. package/test/schedule-timer-cleanup.test.ts +353 -0
  183. package/test/send-race-conditions.test.ts +400 -0
  184. package/test/type-safety-every.test.ts +303 -0
  185. package/test/worker/durable-cascade.test.ts +1117 -0
  186. package/test/worker/durable-step.test.ts +723 -0
  187. package/test/worker/topological-executor.test.ts +1240 -0
  188. package/test/worker/workflow-builder.test.ts +1067 -0
  189. package/test/worker.test.ts +608 -0
  190. package/test/workflow-builder.test.ts +1670 -0
  191. package/test/workflow-cron.test.ts +256 -0
  192. package/test/workflow-state-adapter.test.ts +923 -0
  193. package/test/workflow.test.ts +25 -22
  194. package/tsconfig.json +3 -1
  195. package/vitest.config.ts +38 -1
  196. package/vitest.workers.config.ts +44 -0
  197. package/wrangler.jsonc +22 -0
  198. package/.turbo/turbo-test.log +0 -7
  199. package/src/context.js +0 -83
  200. package/src/every.js +0 -267
  201. package/src/index.js +0 -71
  202. package/src/on.js +0 -79
  203. package/src/send.js +0 -111
  204. package/src/types.js +0 -4
  205. package/src/workflow.js +0 -455
  206. package/test/context.test.js +0 -116
  207. package/test/every.test.js +0 -282
  208. package/test/on.test.js +0 -80
  209. package/test/send.test.js +0 -89
  210. package/test/workflow.test.js +0 -224
  211. package/vitest.config.js +0 -7
@@ -1,4 +1,4 @@
1
1
 
2
- > ai-workflows@2.0.3 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-workflows
2
+ > ai-workflows@2.3.0 build /Users/nathanclevenger/projects/primitives.org.ai/packages/ai-workflows
3
3
  > tsc
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # ai-workflows
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @org.ai/types@2.3.0
8
+
9
+ ## 2.1.3
10
+
11
+ ### Patch Changes
12
+
13
+ - Documentation and testing improvements
14
+ - Add deterministic AI testing suite with self-validating patterns
15
+ - Apply StoryBrand narrative to all package READMEs
16
+ - Update TESTING.md with four principles of deterministic AI testing
17
+ - Fix duplicate examples package name conflict
18
+
3
19
  ## 2.1.1
4
20
 
5
21
  ### Patch Changes
@@ -9,7 +25,7 @@
9
25
  - ai-functions: Add tests for AIFunction<Output, Input> generic order flip
10
26
  - ai-workflows: Add tests for EventHandler<TOutput, TInput> order and OnProxy/EveryProxy autocomplete
11
27
  - ai-database: Existing package - no changes in this release
12
- - @primitives/types: New shared types package with failing tests for RED phase
28
+ - @org.ai/types: New shared types package with failing tests for RED phase
13
29
 
14
30
  These tests document the expected behavior for the GREEN phase implementation where generic type parameters will be reordered to put Output first (matching Promise<T> convention).
15
31
 
package/README.md CHANGED
@@ -1,13 +1,16 @@
1
1
  # ai-workflows
2
2
 
3
- Event-driven workflows with the `$` context. Handle events, schedule tasks, and orchestrate processes.
3
+ ![Stability: Beta](https://img.shields.io/badge/stability-beta-yellow)
4
+
5
+ **Event-driven AI workflows shouldn't require a PhD in distributed systems.**
6
+
7
+ You have business logic that needs to react to events, run on schedules, and coordinate parallel tasks. Traditional workflow engines make you wade through XML configs, learn proprietary DSLs, and debug mysterious state machines. You just want to write `$.on.Order.placed(handler)` and have it work.
4
8
 
5
9
  ```typescript
6
10
  import { Workflow } from 'ai-workflows'
7
11
 
8
12
  const workflow = Workflow($ => {
9
13
  $.on.Customer.created(async (customer, $) => {
10
- $.log('New customer:', customer.name)
11
14
  await $.send('Email.welcome', { to: customer.email })
12
15
  })
13
16
 
@@ -17,214 +20,351 @@ const workflow = Workflow($ => {
17
20
  })
18
21
 
19
22
  await workflow.start()
20
- await workflow.send('Customer.created', { name: 'John', email: 'john@example.com' })
21
23
  ```
22
24
 
25
+ That's it. No YAML. No state machine diagrams. Just JavaScript.
26
+
23
27
  ## Installation
24
28
 
25
29
  ```bash
26
- pnpm add ai-workflows
30
+ npm install ai-workflows
27
31
  ```
28
32
 
29
- ## The `$` Context
33
+ ## Quick Start
30
34
 
31
- Everything flows through `$`. It's your workflow's connection to events, schedules, state, and the outside world.
35
+ ### Event Handlers
36
+
37
+ React to events with the `$.on` pattern. Events follow `Noun.verb` naming:
32
38
 
33
39
  ```typescript
34
40
  Workflow($ => {
35
- // Event handlers
36
- $.on.Order.completed(async (order, $) => {
37
- await $.send('Invoice.generate', { orderId: order.id })
38
- $.log('Order completed', order)
41
+ $.on.Order.placed(async (order, $) => {
42
+ $.log('Processing order', order.id)
43
+ await $.send('Inventory.reserve', { items: order.items })
44
+ await $.send('Payment.charge', { amount: order.total })
39
45
  })
40
46
 
41
- // Scheduled tasks
42
- $.every.hour(async ($) => {
43
- $.log('Hourly health check')
47
+ $.on.Payment.completed(async (payment, $) => {
48
+ await $.send('Order.fulfill', { orderId: payment.orderId })
49
+ })
50
+
51
+ $.on.Payment.failed(async (payment, $) => {
52
+ await $.send('Customer.notify', {
53
+ message: 'Payment failed',
54
+ orderId: payment.orderId
55
+ })
44
56
  })
45
57
  })
46
58
  ```
47
59
 
48
- ## Event Handling
60
+ ### Scheduled Tasks
49
61
 
50
- Events follow the `Noun.verb` pattern:
62
+ Natural scheduling with `$.every`:
51
63
 
52
64
  ```typescript
53
- $.on.Customer.created(handler) // Customer created
54
- $.on.Order.shipped(handler) // Order shipped
55
- $.on.Payment.failed(handler) // Payment failed
56
- $.on.Ticket.resolved(handler) // Ticket resolved
65
+ Workflow($ => {
66
+ // Simple intervals
67
+ $.every.hour(async ($) => {
68
+ $.log('Hourly health check')
69
+ })
70
+
71
+ // Day + time combinations
72
+ $.every.Monday.at9am(async ($) => {
73
+ const report = await $.do('Analytics.weeklyReport', {})
74
+ await $.send('Slack.post', { channel: '#metrics', report })
75
+ })
76
+
77
+ $.every.weekday.at8am(async ($) => {
78
+ $.log('Good morning! Time to standup.')
79
+ })
80
+
81
+ // Precise intervals
82
+ $.every.minutes(30)(async ($) => {
83
+ await $.send('Cache.refresh', {})
84
+ })
85
+ })
57
86
  ```
58
87
 
59
- ### Sending Events
88
+ **Available schedules:**
89
+
90
+ | Intervals | Days | Times |
91
+ |-----------|------|-------|
92
+ | `$.every.second` | `$.every.Monday` | `.at6am` `.at7am` `.at8am` |
93
+ | `$.every.minute` | `$.every.Tuesday` | `.at9am` `.at10am` `.at11am` |
94
+ | `$.every.hour` | `$.every.Wednesday` | `.at12pm` `.atnoon` |
95
+ | `$.every.day` | `$.every.Thursday` | `.at1pm` `.at2pm` `.at3pm` |
96
+ | `$.every.week` | `$.every.Friday` | `.at4pm` `.at5pm` `.at6pm` |
97
+ | `$.every.month` | `$.every.Saturday` | `.at7pm` `.at8pm` `.at9pm` |
98
+ | `$.every.minutes(n)` | `$.every.Sunday` | `.atmidnight` |
99
+ | `$.every.hours(n)` | `$.every.weekday` | |
100
+ | | `$.every.weekend` | |
101
+
102
+ ## The Cascade Pattern
103
+
104
+ Not every problem can be solved with code. Some need AI. Some need human judgment. The cascade executor tries each tier in sequence, escalating only when needed:
105
+
106
+ ```
107
+ Code -> Generative AI -> Agentic AI -> Human
108
+ ```
60
109
 
61
110
  ```typescript
62
- // Fire and forget
63
- await $.send('Email.welcome', { to: 'user@example.com' })
111
+ import { CascadeExecutor } from 'ai-workflows'
112
+
113
+ const processRefund = new CascadeExecutor({
114
+ cascadeName: 'refund-processor',
115
+
116
+ tiers: {
117
+ // Tier 1: Deterministic rules (fastest, cheapest)
118
+ code: {
119
+ name: 'rule-based-refund',
120
+ execute: async (request) => {
121
+ if (request.amount < 50 && request.reason === 'defective') {
122
+ return { approved: true, method: 'original-payment' }
123
+ }
124
+ throw new Error('Rules inconclusive')
125
+ }
126
+ },
127
+
128
+ // Tier 2: AI analysis for complex cases
129
+ generative: {
130
+ name: 'ai-refund-analysis',
131
+ execute: async (request, ctx) => {
132
+ const analysis = await analyzeRefundRequest(request)
133
+ if (analysis.confidence > 0.9) {
134
+ return { approved: analysis.shouldApprove, reason: analysis.explanation }
135
+ }
136
+ throw new Error('Confidence too low')
137
+ }
138
+ },
139
+
140
+ // Tier 3: Agent with tool access
141
+ agentic: {
142
+ name: 'refund-agent',
143
+ execute: async (request, ctx) => {
144
+ return await refundAgent.process(request)
145
+ }
146
+ },
147
+
148
+ // Tier 4: Human review for edge cases
149
+ human: {
150
+ name: 'human-review',
151
+ execute: async (request) => {
152
+ return await createHumanTask({
153
+ type: 'refund-review',
154
+ data: request,
155
+ assignTo: 'support-team'
156
+ })
157
+ }
158
+ }
159
+ },
64
160
 
65
- // Execute and wait for result (durable)
66
- const result = await $.do('Order.process', orderData)
161
+ // Default timeouts per tier
162
+ useDefaultTimeouts: true, // code: 5s, generative: 30s, agentic: 5m, human: 24h
163
+ })
67
164
 
68
- // Execute without durability
69
- const result = await $.try('Order.validate', orderData)
165
+ const result = await processRefund.execute(refundRequest)
166
+ console.log(`Resolved by ${result.tier} tier in ${result.metrics.totalDuration}ms`)
70
167
  ```
71
168
 
72
- ## Scheduling
169
+ ### Cascade Features
73
170
 
74
- Natural language scheduling with `$.every`:
171
+ - **Automatic escalation** - Failed tiers escalate to the next level
172
+ - **Tier timeouts** - Each tier has configurable time limits
173
+ - **Retry support** - Configure retries with exponential backoff per tier
174
+ - **Skip conditions** - Skip tiers based on input characteristics
175
+ - **5W+H audit trail** - Full event log: who, what, when, where, why, how
176
+
177
+ ## Dependency Graphs
178
+
179
+ For complex workflows with interdependent steps, use the dependency graph to ensure correct execution order:
75
180
 
76
181
  ```typescript
77
- // Simple intervals
78
- $.every.second(handler)
79
- $.every.minute(handler)
80
- $.every.hour(handler)
81
- $.every.day(handler)
82
- $.every.week(handler)
83
-
84
- // Days of the week
85
- $.every.Monday(handler)
86
- $.every.Friday(handler)
87
- $.every.weekday(handler)
88
- $.every.weekend(handler)
89
-
90
- // Day + time combinations
91
- $.every.Monday.at9am(handler)
92
- $.every.Friday.at5pm(handler)
93
- $.every.weekday.at8am(handler)
94
-
95
- // Intervals with values
96
- $.every.minutes(30)(handler)
97
- $.every.hours(4)(handler)
98
-
99
- // Natural language (requires AI converter)
100
- $.every('first Monday of the month at 9am', handler)
101
- $.every('every 15 minutes during business hours', handler)
182
+ import { DependencyGraph, getExecutionLevels } from 'ai-workflows'
183
+
184
+ const graph = new DependencyGraph()
185
+
186
+ // Steps with no dependencies run first (level 0)
187
+ graph.addNode('fetch-user')
188
+ graph.addNode('fetch-products')
189
+
190
+ // Dependent steps run after their dependencies complete
191
+ graph.addNode('validate-cart', { dependsOn: ['fetch-user', 'fetch-products'] })
192
+ graph.addNode('calculate-shipping', { dependsOn: 'fetch-products' })
193
+ graph.addNode('apply-discounts', { dependsOn: 'validate-cart' })
194
+ graph.addNode('process-payment', { dependsOn: ['apply-discounts', 'calculate-shipping'] })
195
+
196
+ // Automatic cycle detection
197
+ try {
198
+ graph.addNode('bad-step', { dependsOn: 'process-payment' })
199
+ graph.addEdge('bad-step', 'fetch-user') // Would create a cycle!
200
+ } catch (e) {
201
+ console.log('Caught circular dependency:', e.cyclePath)
202
+ }
102
203
  ```
103
204
 
104
- ### Available Times
205
+ ### Topological Sort
206
+
207
+ Execute steps in dependency order:
105
208
 
106
209
  ```typescript
107
- .at6am .at7am .at8am .at9am .at10am .at11am
108
- .at12pm .atnoon .at1pm .at2pm .at3pm .at4pm
109
- .at5pm .at6pm .at7pm .at8pm .at9pm .atmidnight
210
+ import { topologicalSort, getExecutionLevels } from 'ai-workflows'
211
+
212
+ const steps = [
213
+ { id: 'A', dependencies: [] },
214
+ { id: 'B', dependencies: ['A'] },
215
+ { id: 'C', dependencies: ['A'] },
216
+ { id: 'D', dependencies: ['B', 'C'] },
217
+ ]
218
+
219
+ // Linear execution order
220
+ const { order } = topologicalSort(steps)
221
+ // => ['A', 'B', 'C', 'D']
222
+
223
+ // Parallel execution groups
224
+ const levels = getExecutionLevels(steps)
225
+ // => [
226
+ // { level: 0, nodes: ['A'] }, // Run first
227
+ // { level: 1, nodes: ['B', 'C'] }, // Run in parallel
228
+ // { level: 2, nodes: ['D'] } // Run after B and C complete
229
+ // ]
110
230
  ```
111
231
 
112
- ## Standalone API
232
+ ## Barriers and Joins
113
233
 
114
- Use `on`, `every`, and `send` for global registration:
234
+ Coordinate parallel operations with barrier semantics:
115
235
 
116
236
  ```typescript
117
- import { on, every, send } from 'ai-workflows'
118
-
119
- on.Customer.created(async (customer, $) => {
120
- await $.send('Email.welcome', { to: customer.email })
237
+ import { waitForAll, waitForAny, Barrier, withConcurrencyLimit } from 'ai-workflows'
238
+
239
+ // Wait for all parallel tasks
240
+ const results = await waitForAll([
241
+ fetchUserData(userId),
242
+ fetchOrderHistory(userId),
243
+ fetchRecommendations(userId),
244
+ ], { timeout: 5000 })
245
+
246
+ // Wait for N of M (e.g., 2 of 3 replicas)
247
+ const { completed, pending } = await waitForAny(2, [
248
+ writeToReplica1(data),
249
+ writeToReplica2(data),
250
+ writeToReplica3(data),
251
+ ])
252
+
253
+ // Manual barrier for complex coordination
254
+ const barrier = new Barrier(3, {
255
+ timeout: 10000,
256
+ onProgress: ({ arrived, expected, percentage }) => {
257
+ console.log(`${arrived}/${expected} (${percentage}%)`)
258
+ }
121
259
  })
122
260
 
123
- every.hour(async ($) => {
124
- $.log('Hourly task')
125
- })
261
+ // In parallel handlers...
262
+ barrier.arrive(resultFromWorker1)
263
+ barrier.arrive(resultFromWorker2)
264
+ barrier.arrive(resultFromWorker3)
126
265
 
127
- await send('Customer.created', { name: 'John' })
266
+ // Wait for all to arrive
267
+ const allResults = await barrier.wait()
128
268
  ```
129
269
 
130
- ## Real-World Examples
270
+ ### Concurrency Control
131
271
 
132
- ### Order Processing
272
+ Limit parallel executions to prevent overwhelming downstream services:
133
273
 
134
274
  ```typescript
135
- const workflow = Workflow($ => {
136
- $.on.Order.placed(async (order, $) => {
137
- $.log('Processing order', order.id)
275
+ const urls = [/* 100 URLs */]
276
+
277
+ // Process 5 at a time
278
+ const results = await withConcurrencyLimit(
279
+ urls.map(url => () => fetch(url)),
280
+ 5, // max concurrent
281
+ { collectErrors: true } // don't fail fast
282
+ )
283
+ ```
138
284
 
139
- // Validate inventory
140
- const valid = await $.do('Inventory.check', order.items)
141
- if (!valid) {
142
- await $.send('Order.cancelled', { orderId: order.id, reason: 'Out of stock' })
143
- return
144
- }
285
+ ## Standalone API
145
286
 
146
- // Process payment
147
- const payment = await $.do('Payment.charge', {
148
- amount: order.total,
149
- customer: order.customerId,
150
- })
287
+ Use `on`, `every`, and `send` for global registration outside of a workflow:
151
288
 
152
- // Fulfill order
153
- await $.send('Fulfillment.ship', { orderId: order.id })
154
- })
289
+ ```typescript
290
+ import { on, every, send } from 'ai-workflows'
155
291
 
156
- $.on.Order.shipped(async (data, $) => {
157
- await $.send('Email.tracking', { orderId: data.orderId })
158
- })
292
+ // Register handlers
293
+ on.Customer.created(async (customer, $) => {
294
+ await $.send('Email.welcome', { to: customer.email })
159
295
  })
160
- ```
161
296
 
162
- ### Customer Lifecycle
297
+ every.hour(async ($) => {
298
+ $.log('Background task running')
299
+ })
163
300
 
164
- ```typescript
165
- Workflow($ => {
166
- $.on.Customer.signedUp(async (customer, $) => {
167
- await $.send('Email.welcome', { to: customer.email })
168
- await $.send('Slack.notify', { message: `New signup: ${customer.name}` })
169
- })
301
+ // Emit events from anywhere
302
+ await send('Customer.created', { name: 'Alice', email: 'alice@example.com' })
303
+ ```
170
304
 
171
- $.on.Customer.upgraded(async (customer, $) => {
172
- await $.send('Email.upgradeConfirmation', { to: customer.email })
173
- await $.send('Analytics.track', { event: 'upgrade', plan: customer.plan })
174
- })
305
+ ## Configuration
175
306
 
176
- // Check for inactive users daily
177
- $.every.day.at9am(async ($) => {
178
- const inactive = await $.do('Customer.findInactive', { days: 30 })
179
- for (const customer of inactive) {
180
- await $.send('Email.reengagement', { to: customer.email })
181
- }
182
- })
183
- })
184
- ```
307
+ ### Custom Cron Converter
185
308
 
186
- ### Scheduled Reports
309
+ Enable natural language scheduling with an AI-powered cron converter:
187
310
 
188
311
  ```typescript
189
- Workflow($ => {
190
- $.every.Monday.at9am(async ($) => {
191
- const report = await $.do('Analytics.weeklyReport', {})
192
- await $.send('Email.report', {
193
- to: 'team@company.com',
194
- report,
195
- })
196
- })
312
+ import { setCronConverter } from 'ai-workflows'
197
313
 
198
- $.every.month(async ($) => {
199
- const metrics = await $.do('Metrics.monthly', {})
200
- await $.send('Dashboard.update', { metrics })
201
- })
314
+ setCronConverter(async (description) => {
315
+ // Use your AI service to convert natural language to cron
316
+ const response = await ai.complete(`Convert to cron: "${description}"`)
317
+ return response.cron
202
318
  })
319
+
320
+ // Now you can use natural language
321
+ $.every('first Monday of the month at 9am', handler)
322
+ $.every('every 15 minutes during business hours', handler)
203
323
  ```
204
324
 
205
- ## Workflow Instance
325
+ ### Cascade Timeouts
206
326
 
207
- The `Workflow()` function returns an instance with:
327
+ Configure per-tier and total timeouts:
208
328
 
209
329
  ```typescript
210
- const workflow = Workflow($ => { /* ... */ })
330
+ const executor = new CascadeExecutor({
331
+ tiers: { /* ... */ },
332
+
333
+ // Custom timeouts per tier (milliseconds)
334
+ timeouts: {
335
+ code: 2000, // 2 seconds
336
+ generative: 15000, // 15 seconds
337
+ agentic: 60000, // 1 minute
338
+ human: 3600000, // 1 hour
339
+ },
340
+
341
+ // Or use defaults
342
+ useDefaultTimeouts: true, // code: 5s, generative: 30s, agentic: 5m, human: 24h
343
+
344
+ // Total cascade timeout
345
+ totalTimeout: 300000, // 5 minutes max for entire cascade
346
+ })
347
+ ```
211
348
 
212
- // Access the workflow
213
- workflow.definition // Event and schedule registrations
214
- workflow.state // Current state and history
215
- workflow.$ // The $ context
349
+ ### Retry Configuration
216
350
 
217
- // Control the workflow
218
- await workflow.start() // Begin processing schedules
219
- await workflow.stop() // Stop all schedules
351
+ Add retries with exponential backoff:
220
352
 
221
- // Send events
222
- await workflow.send('Customer.created', { name: 'John' })
353
+ ```typescript
354
+ const executor = new CascadeExecutor({
355
+ tiers: { /* ... */ },
356
+
357
+ retryConfig: {
358
+ code: { maxRetries: 2, baseDelay: 100 },
359
+ generative: { maxRetries: 3, baseDelay: 1000, multiplier: 2 },
360
+ agentic: { maxRetries: 1, baseDelay: 5000 },
361
+ }
362
+ })
223
363
  ```
224
364
 
225
365
  ## Testing
226
366
 
227
- Create isolated test contexts:
367
+ Create isolated contexts for testing:
228
368
 
229
369
  ```typescript
230
370
  import { createTestContext } from 'ai-workflows'
@@ -232,75 +372,56 @@ import { createTestContext } from 'ai-workflows'
232
372
  const $ = createTestContext()
233
373
 
234
374
  // Call your handler
235
- await yourHandler({ name: 'test' }, $)
375
+ await orderHandler({ id: '123', total: 99.99 }, $)
236
376
 
237
- // Check what was emitted
377
+ // Assert on emitted events
238
378
  expect($.emittedEvents).toContainEqual({
239
- event: 'Email.welcome',
240
- data: { to: 'test@example.com' },
379
+ event: 'Payment.charge',
380
+ data: { amount: 99.99 },
241
381
  })
242
382
  ```
243
383
 
244
384
  ## API Reference
245
385
 
246
- ### Workflow Functions
386
+ ### Core Workflow
247
387
 
248
388
  | Export | Description |
249
389
  |--------|-------------|
250
390
  | `Workflow($)` | Create a workflow with $ context |
251
- | `on` | Standalone event registration |
252
- | `every` | Standalone schedule registration |
253
- | `send` | Emit events globally |
391
+ | `on` | Standalone event registration proxy |
392
+ | `every` | Standalone schedule registration proxy |
393
+ | `send(event, data)` | Emit events globally |
254
394
  | `createTestContext()` | Create isolated $ for testing |
255
395
 
256
- ### Context Methods
396
+ ### Cascade Executor
257
397
 
258
- | Method | Description |
398
+ | Export | Description |
259
399
  |--------|-------------|
260
- | `$.on.Noun.verb(handler)` | Register event handler |
261
- | `$.every.*` | Register scheduled handler |
262
- | `$.send(event, data)` | Emit event (fire and forget) |
263
- | `$.do(event, data)` | Execute handler (durable) |
264
- | `$.try(event, data)` | Execute handler (non-durable) |
265
- | `$.log(message, data?)` | Log with history |
266
- | `$.state` | Access workflow state |
400
+ | `CascadeExecutor` | Tiered execution: code -> AI -> agent -> human |
401
+ | `TIER_ORDER` | `['code', 'generative', 'agentic', 'human']` |
402
+ | `DEFAULT_TIER_TIMEOUTS` | Default timeout per tier |
267
403
 
268
- ### Schedule Helpers
404
+ ### Dependency Graph
269
405
 
270
406
  | Export | Description |
271
407
  |--------|-------------|
272
- | `toCron(description)` | Convert to cron expression |
273
- | `setCronConverter(fn)` | Set AI cron converter |
274
- | `intervalToMs(interval)` | Get interval in milliseconds |
275
- | `formatInterval(interval)` | Format for display |
408
+ | `DependencyGraph` | DAG for workflow step dependencies |
409
+ | `topologicalSort(nodes)` | Sort nodes in dependency order |
410
+ | `getExecutionLevels(nodes)` | Group nodes for parallel execution |
411
+ | `CircularDependencyError` | Thrown when cycle detected |
276
412
 
277
- ## Types
413
+ ### Barriers
278
414
 
279
- ```typescript
280
- interface WorkflowContext {
281
- on: OnProxy
282
- every: EveryProxy
283
- send<T>(event: string, data: T): Promise<void>
284
- do<T, R>(event: string, data: T): Promise<R>
285
- try<T, R>(event: string, data: T): Promise<R>
286
- log(message: string, data?: unknown): void
287
- state: Record<string, unknown>
288
- db?: DatabaseContext
289
- }
290
-
291
- interface WorkflowInstance {
292
- definition: WorkflowDefinition
293
- state: WorkflowState
294
- $: WorkflowContext
295
- send<T>(event: string, data: T): Promise<void>
296
- start(): Promise<void>
297
- stop(): Promise<void>
298
- }
299
- ```
415
+ | Export | Description |
416
+ |--------|-------------|
417
+ | `Barrier` | Manual synchronization point |
418
+ | `waitForAll(promises)` | Wait for all with timeout support |
419
+ | `waitForAny(n, promises)` | Wait for N of M to complete |
420
+ | `withConcurrencyLimit(tasks, n)` | Limit parallel executions |
300
421
 
301
422
  ## Related Packages
302
423
 
303
- - [`ai-functions`](../ai-functions) AI-powered functions
304
- - [`ai-database`](../ai-database) Durable event storage
305
- - [`human-in-the-loop`](../human-in-the-loop) Human workflow steps
306
- - [`digital-tasks`](../digital-tasks) Task management
424
+ - [`ai-functions`](../ai-functions) - AI-powered functions with type safety
425
+ - [`ai-database`](../ai-database) - Durable event storage
426
+ - [`human-in-the-loop`](../human-in-the-loop) - Human workflow steps
427
+ - [`digital-workers`](../digital-workers) - Autonomous AI agents