lua-cli 3.30.0 โ†’ 3.32.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 (62) hide show
  1. package/dist/api-exports.d.ts +1096 -41
  2. package/dist/api-exports.js +5544 -137
  3. package/dist/api-exports.js.map +1 -1
  4. package/dist/index.js +21255 -8412
  5. package/dist/index.js.map +1 -1
  6. package/dist/voice/test/index.d.ts +4 -4
  7. package/dist/workflow-builder.d.ts +800 -0
  8. package/dist/workflow-builder.js +6273 -0
  9. package/dist/workflow-builder.js.map +1 -0
  10. package/docs/API_INDEX.md +2 -0
  11. package/docs/README.md +27 -9
  12. package/docs/api/Jobs.md +10 -10
  13. package/docs/api/LuaWorkflow.md +89 -0
  14. package/docs/api/Workflows.md +111 -0
  15. package/docs/workflows/approvals.md +41 -0
  16. package/docs/workflows/artefacts-and-datasets.md +19 -0
  17. package/docs/workflows/coding-harness.md +12 -0
  18. package/docs/workflows/compliance-gates.md +16 -0
  19. package/docs/workflows/connections-in-coding-turns.md +10 -0
  20. package/docs/workflows/correlation-keys.md +11 -0
  21. package/docs/workflows/env-overlays.md +12 -0
  22. package/docs/workflows/evidence-bundles.md +11 -0
  23. package/docs/workflows/exports.md +5 -0
  24. package/docs/workflows/external-content-and-toolscope.md +11 -0
  25. package/docs/workflows/git-credentials.md +32 -0
  26. package/docs/workflows/goals.md +46 -0
  27. package/docs/workflows/knowledge-bindings.md +13 -0
  28. package/docs/workflows/limits.md +11 -0
  29. package/docs/workflows/long-steps-and-checkpoints.md +13 -0
  30. package/docs/workflows/migrating-cloud-tasks.md +9 -0
  31. package/docs/workflows/migrating-runs.md +13 -0
  32. package/docs/workflows/output-visibility.md +9 -0
  33. package/docs/workflows/per-item-approvals.md +9 -0
  34. package/docs/workflows/private-network-sources.md +12 -0
  35. package/docs/workflows/recovery.md +32 -0
  36. package/docs/workflows/replay-local.md +35 -0
  37. package/docs/workflows/reply-channels.md +11 -0
  38. package/docs/workflows/retention-and-archival.md +82 -0
  39. package/docs/workflows/roles.md +12 -0
  40. package/docs/workflows/schedules.md +26 -0
  41. package/docs/workflows/script-form.md +50 -0
  42. package/docs/workflows/testing-offline.md +49 -0
  43. package/docs/workflows/workspace-backends.md +11 -0
  44. package/docs/workflows/workspaces-and-long-steps.md +29 -0
  45. package/package.json +8 -3
  46. package/scripts/run-api-extractor.mjs +1 -1
  47. package/template/.gitignore +2 -0
  48. package/template/examples/workflows/CLAUDE.md +27 -0
  49. package/template/examples/workflows/adversarial-verify.workflow.script.js +48 -0
  50. package/template/examples/workflows/github-review.webhook.ts +19 -0
  51. package/template/examples/workflows/linear-ready.trigger.ts +21 -0
  52. package/template/examples/workflows/outreach.ts +55 -0
  53. package/template/examples/workflows/pr-review-round.ts +75 -0
  54. package/template/examples/workflows/provision-tenant.ts +35 -0
  55. package/template/examples/workflows/refund-approval.ts +57 -0
  56. package/template/examples/workflows/research-brief.ts +42 -0
  57. package/template/examples/workflows/reviewed-brief.ts +19 -0
  58. package/template/examples/workflows/support-triage.ts +81 -0
  59. package/template/examples/workflows/ticket-to-pr.ts +137 -0
  60. package/template/examples/workflows/vendor-invoices.ts +83 -0
  61. package/template/lua.skill.yaml +1 -0
  62. package/template/package.json +1 -1
package/docs/API_INDEX.md CHANGED
@@ -11,6 +11,7 @@ Quick reference for all lua-cli APIs and exports.
11
11
  | [LuaTool](./api/LuaTool.md) | Individual tool interface | `import { LuaTool } from 'lua-cli/skill'` |
12
12
  | [LuaWebhook](./api/LuaWebhook.md) | HTTP webhook handler | `import { LuaWebhook } from 'lua-cli'` |
13
13
  | [LuaJob](./api/LuaJob.md) | Scheduled task | `import { LuaJob } from 'lua-cli'` |
14
+ | [LuaWorkflow](./api/LuaWorkflow.md) | Durable multi-step workflow (`createStep`/`createWorkflow`) | `import { createWorkflow } from 'lua-cli'` |
14
15
  | [PreProcessor](./api/PreProcessor.md) | Message preprocessor | `import { PreProcessor } from 'lua-cli'` |
15
16
  | [PostProcessor](./api/PostProcessor.md) | Response postprocessor | `import { PostProcessor } from 'lua-cli'` |
16
17
  | LuaMCPServer | MCP server integration | `import { LuaMCPServer } from 'lua-cli'` |
@@ -20,6 +21,7 @@ Quick reference for all lua-cli APIs and exports.
20
21
  | API | Purpose | Methods |
21
22
  |-----|---------|---------|
22
23
  | [AI](./api/AI.md) | AI text generation | `generate()` |
24
+ | [Workflows](./api/Workflows.md) | Workflow runs | `start()`, `get()`, `list()`, `cancel()`, `resume()`, `signal()` |
23
25
  | [User](./api/User.md) | User data management | `get()`, `getChatHistory()` |
24
26
  | [Products](./api/Products.md) | Product catalog | `get()`, `search()`, `getById()`, `create()`, `update()`, `delete()` |
25
27
  | [Baskets](./api/Baskets.md) | Shopping carts | `get()`, `create()`, `getById()`, `addItem()`, `clear()`, `placeOrder()` |
package/docs/README.md CHANGED
@@ -1,10 +1,11 @@
1
- # lua-cli v3.30.0
1
+ # lua-cli v3.32.1
2
2
 
3
- Welcome to the comprehensive API documentation for lua-cli v3.30.0. This guide covers every API, class, and function exported by the package.
3
+ Welcome to the comprehensive API documentation for lua-cli v3.32.1. This guide covers every API, class, and function exported by the package.
4
4
 
5
5
  ## ๐Ÿ“š Documentation Index
6
6
 
7
7
  ### Core Primitives
8
+
8
9
  - [LuaAgent](./api/LuaAgent.md) - Unified agent configuration
9
10
  - [LuaSkill](./api/LuaSkill.md) - Tool collections
10
11
  - [LuaTool](./api/LuaTool.md) - Individual tools
@@ -16,6 +17,7 @@ Welcome to the comprehensive API documentation for lua-cli v3.30.0. This guide c
16
17
  - LuaDevice - Bidirectional device communication (send commands, receive triggers)
17
18
 
18
19
  ### Runtime APIs
20
+
19
21
  - [User API](./api/User.md) - User data management
20
22
  - [Products API](./api/Products.md) - Product catalog (with filter support)
21
23
  - [Data API](./api/Data.md) - Custom data storage with vector search
@@ -29,6 +31,7 @@ Welcome to the comprehensive API documentation for lua-cli v3.30.0. This guide c
29
31
  - Lua Runtime - Channel detection (`import { Lua } from 'lua-cli'`)
30
32
 
31
33
  ### Utilities
34
+
32
35
  - `env()` - Environment variable access (`import { env } from 'lua-cli'`)
33
36
  - `BasketStatus` - Basket state enum
34
37
  - `OrderStatus` - Order state enum
@@ -49,7 +52,7 @@ import {
49
52
  LuaJob,
50
53
  PreProcessor,
51
54
  PostProcessor,
52
-
55
+
53
56
  // Runtime APIs
54
57
  User,
55
58
  Products,
@@ -58,7 +61,7 @@ import {
58
61
  Data,
59
62
  Jobs,
60
63
  Webhooks,
61
-
64
+
62
65
  // Instances
63
66
  UserDataInstance,
64
67
  ProductInstance,
@@ -66,19 +69,20 @@ import {
66
69
  OrderInstance,
67
70
  DataEntryInstance,
68
71
  JobInstance,
69
-
72
+
70
73
  // Enums
71
74
  BasketStatus,
72
75
  OrderStatus,
73
-
76
+
74
77
  // Utilities
75
- env
78
+ env,
76
79
  } from 'lua-cli';
77
80
  ```
78
81
 
79
82
  ### Common Patterns
80
83
 
81
84
  #### Create a Tool
85
+
82
86
  ```typescript
83
87
  import { LuaTool } from 'lua-cli/skill';
84
88
  import { z } from 'zod';
@@ -87,7 +91,7 @@ export default class MyTool implements LuaTool {
87
91
  name = 'my_tool';
88
92
  description = 'Tool description';
89
93
  inputSchema = z.object({ param: z.string() });
90
-
94
+
91
95
  async execute(input: z.infer<typeof this.inputSchema>) {
92
96
  return { result: 'success' };
93
97
  }
@@ -95,17 +99,19 @@ export default class MyTool implements LuaTool {
95
99
  ```
96
100
 
97
101
  #### Create an Agent
102
+
98
103
  ```typescript
99
104
  import { LuaAgent, LuaSkill } from 'lua-cli';
100
105
 
101
106
  export const agent = new LuaAgent({
102
107
  name: 'my-agent',
103
108
  persona: 'Agent personality...',
104
- skills: [skill1, skill2]
109
+ skills: [skill1, skill2],
105
110
  });
106
111
  ```
107
112
 
108
113
  #### Use Runtime APIs
114
+
109
115
  ```typescript
110
116
  import { User, Products, Data } from 'lua-cli';
111
117
 
@@ -119,6 +125,7 @@ await Data.create('notes', { text: 'Hello' });
119
125
  ## ๐Ÿ“– By Category
120
126
 
121
127
  ### For Building Agents
128
+
122
129
  - Start with [LuaAgent](./api/LuaAgent.md)
123
130
  - Learn about [LuaSkill](./api/LuaSkill.md)
124
131
  - Create [LuaTool](./api/LuaTool.md)
@@ -126,25 +133,30 @@ await Data.create('notes', { text: 'Hello' });
126
133
  - Schedule [LuaJob](./api/LuaJob.md) tasks
127
134
 
128
135
  ### For Data Management
136
+
129
137
  - [User API](./api/User.md) - User profiles
130
138
  - [Data API](./api/Data.md) - Custom collections
131
139
  - [Products API](./api/Products.md) - Product catalog
132
140
 
133
141
  ### For E-commerce
142
+
134
143
  - [Products API](./api/Products.md) - Product catalog
135
144
  - [Baskets API](./api/Baskets.md) - Shopping carts
136
145
  - [Orders API](./api/Orders.md) - Order fulfillment
137
146
 
138
147
  ### For Automation
148
+
139
149
  - [LuaJob](./api/LuaJob.md) - Scheduled tasks (cron, intervals)
140
150
  - [Jobs API](./api/Jobs.md) - Dynamic job creation from tools
141
151
  - [LuaWebhook](./api/LuaWebhook.md) - HTTP endpoints for external triggers
142
152
 
143
153
  ### For Message Processing
154
+
144
155
  - [PreProcessor](./api/PreProcessor.md) - Filter/modify incoming messages
145
156
  - [PostProcessor](./api/PostProcessor.md) - Format/enhance agent responses
146
157
 
147
158
  ### For Third-Party Integrations
159
+
148
160
  - `lua integrations` command - Connect accounts (Linear, Discord, Google Calendar, etc.)
149
161
  - Auto-creates MCP servers for connected integrations
150
162
 
@@ -153,16 +165,19 @@ await Data.create('notes', { text: 'Hello' });
153
165
  ## ๐ŸŽฏ Learning Path
154
166
 
155
167
  ### Beginner
168
+
156
169
  1. Read [LuaAgent](./api/LuaAgent.md) - Understand agent configuration
157
170
  2. Read [LuaTool](./api/LuaTool.md) - Learn to create tools
158
171
  3. Read [LuaSkill](./api/LuaSkill.md) - Organize tools into skills
159
172
 
160
173
  ### Intermediate
174
+
161
175
  1. [User API](./api/User.md) - Work with user data
162
176
  2. [Products API](./api/Products.md) - Manage products
163
177
  3. [Data API](./api/Data.md) - Use custom storage
164
178
 
165
179
  ### Advanced
180
+
166
181
  1. [LuaJob](./api/LuaJob.md) - Schedule automated tasks
167
182
  2. [Jobs API](./api/Jobs.md) - Create jobs dynamically
168
183
  3. [LuaWebhook](./api/LuaWebhook.md) - Build integrations
@@ -218,6 +233,7 @@ await Data.create('notes', { text: 'Hello' });
218
233
  ## ๐Ÿ“ Documentation Standards
219
234
 
220
235
  Each API doc includes:
236
+
221
237
  - โœ… Overview and purpose
222
238
  - โœ… Type definitions
223
239
  - โœ… Complete method reference
@@ -255,6 +271,7 @@ Each API doc includes:
255
271
  ## Additional Utilities
256
272
 
257
273
  ### Lua Runtime
274
+
258
275
  ```typescript
259
276
  import { Lua } from 'lua-cli';
260
277
 
@@ -264,6 +281,7 @@ if (Lua.request.channel === 'whatsapp') {
264
281
  ```
265
282
 
266
283
  ### env() Utility
284
+
267
285
  ```typescript
268
286
  import { env } from 'lua-cli';
269
287
 
package/docs/api/Jobs.md CHANGED
@@ -53,7 +53,7 @@ const job = await Jobs.create({
53
53
  console.log('Job created:', job.id);
54
54
  ```
55
55
 
56
- ### `Jobs.get(jobId)`
56
+ ### `Jobs.getJob(jobId)`
57
57
 
58
58
  Retrieves a job by ID.
59
59
 
@@ -64,7 +64,7 @@ Retrieves a job by ID.
64
64
 
65
65
  **Example:**
66
66
  ```typescript
67
- const job = await Jobs.get('job_123');
67
+ const job = await Jobs.getJob('job_123');
68
68
  console.log(job.name);
69
69
  console.log(job.schedule);
70
70
  ```
@@ -85,7 +85,7 @@ interface JobConfig {
85
85
  /** When/how often to run */
86
86
  schedule: JobSchedule;
87
87
 
88
- /** Maximum execution time in ms */
88
+ /** Maximum execution time in seconds (default 300, supported range: 1-600) */
89
89
  timeout?: number;
90
90
 
91
91
  /** Retry configuration */
@@ -133,7 +133,7 @@ schedule: {
133
133
  ```typescript
134
134
  schedule: {
135
135
  type: 'interval',
136
- intervalSeconds: number // Seconds between executions
136
+ seconds: number // Seconds between executions
137
137
  }
138
138
  ```
139
139
 
@@ -142,13 +142,13 @@ schedule: {
142
142
  // Run every 5 minutes
143
143
  schedule: {
144
144
  type: 'interval',
145
- intervalSeconds: 300
145
+ seconds: 300
146
146
  }
147
147
 
148
148
  // Run every hour
149
149
  schedule: {
150
150
  type: 'interval',
151
- intervalSeconds: 3600
151
+ seconds: 3600
152
152
  }
153
153
  ```
154
154
 
@@ -157,7 +157,7 @@ schedule: {
157
157
  ```typescript
158
158
  schedule: {
159
159
  type: 'cron',
160
- pattern: string // Cron pattern
160
+ expression: string // Cron expression
161
161
  }
162
162
  ```
163
163
 
@@ -166,19 +166,19 @@ schedule: {
166
166
  // Every day at 9 AM
167
167
  schedule: {
168
168
  type: 'cron',
169
- pattern: '0 9 * * *'
169
+ expression: '0 9 * * *'
170
170
  }
171
171
 
172
172
  // Every Monday at 8 AM
173
173
  schedule: {
174
174
  type: 'cron',
175
- pattern: '0 8 * * 1'
175
+ expression: '0 8 * * 1'
176
176
  }
177
177
 
178
178
  // Every 15 minutes
179
179
  schedule: {
180
180
  type: 'cron',
181
- pattern: '*/15 * * * *'
181
+ expression: '*/15 * * * *'
182
182
  }
183
183
  ```
184
184
 
@@ -0,0 +1,89 @@
1
+ # LuaWorkflow โ€” `createStep` / `createWorkflow`
2
+
3
+ The workflow primitive: a **durable, multi-step program** you author in TypeScript, compile with `lua compile`, and push with `lua push workflow`. The engine executes the compiled graph server-side โ€” steps survive restarts, waits hold no compute, and approvals/signals park the run until a person or a webhook answers.
4
+
5
+ > **Execution model โ€” read this first.** _`execute` re-runs from the top after resume; execution is at-least-once โ€” dedupe on `occurrenceId`._ `occurrenceId` is `${lineageId}:${stepId}` โ€” the same key on retry, resume, `retry-step` and a repair run; an effect that must be unique across independent runs needs your own business key (`refund:${ticketId}`). Wrap external effects in `ctx.once(key, fn)`.
6
+
7
+ ## Import
8
+
9
+ ```typescript
10
+ import {
11
+ createStep,
12
+ createWorkflow,
13
+ defineWorkflow,
14
+ step,
15
+ stepOf,
16
+ eq,
17
+ gt,
18
+ gte,
19
+ lit,
20
+ template,
21
+ fromInit,
22
+ fromStep,
23
+ fromKnowledge,
24
+ rows,
25
+ env,
26
+ } from 'lua-cli';
27
+ ```
28
+
29
+ ## `createStep(config)`
30
+
31
+ A typed code step. `inputSchema` / `outputSchema` / `resumeSchema` are zod schemas; the compiler serializes them to JSON Schema.
32
+
33
+ | Field | Meaning |
34
+ | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35
+ | `id` | Unique step id (referenced by `then`, containers, `fromStep`) |
36
+ | `inputSchema` / `outputSchema` | zod; outputs are validated after every attempt |
37
+ | `timeoutSeconds` | Per-attempt wall (worker tier โ‰ค 600; `tier:'job'` up to 86 400) |
38
+ | `retry` | `{ maxAttempts, backoffSeconds?, backoff?: 'fixed'\|'exponential', maxBackoffSeconds? }` โ€” waits `backoffSeconds ยท 2^(attempt-1)` capped at `maxBackoffSeconds` |
39
+ | `sideEffects` | `'external'` โ‡’ never auto-retried on a platform-fault reclaim โ€” the step **parks** instead (see [When a step parks](../workflows/recovery.md)) |
40
+ | `onError` | What the **final** failure does to the run: `'fail'` (default โ€” the run unwinds `failed`) ยท `'continue'` (the run goes on; the step's result is the **continued-failure value** `{ __lua_workflow:'continued_failure', failed:true, error:{code,message}, text:'' }` โ€” `${stepResults.<id>.text}` renders `''`, `getStepResult(id)` returns it, a `conditional` can branch on `stepResults.<id>.failed`) ยท `'park'` (the step parks on an exception gate for `retry-step` / `resolve-step` โ€” see [When a step parks](../workflows/recovery.md)) |
41
+ | `requiredConnections` | Connection ids that must mount before `execute` runs |
42
+ | `tier` / `workspace` / `jobResources` / `jobTools` | Job-tier fields โ€” see [Workspaces and long steps](../workflows/workspaces-and-long-steps.md) |
43
+ | `execute(ctx)` | `ctx`: `inputData`, `resumeData`, `getInitData()`, `getStepResult(id)`, `state`, `suspend()`, `bail()`, `bailRun()`, `once()`, `log()`, `env`, `artefacts`, `occurrenceId`, `workspace?` |
44
+
45
+ `ctx.suspend(payload)` parks the step for `Workflows.resume(runId, stepId, resumeData)`; on resume **`execute` re-runs from the top** with `ctx.resumeData` set.
46
+
47
+ ## `createWorkflow(config)โ€ฆcommit()`
48
+
49
+ `createWorkflow(cfg)` opens a fluent builder; `.commit()` freezes it into the compiled graph. `defineWorkflow(cfg, (wf) => โ€ฆ)` is sugar for the same thing.
50
+
51
+ Config: `name`, `description?`, `inputSchema`, `outputSchema?`, `budget?` (`maxCredits`, `maxDurationSeconds`, `maxJobSeconds?`), `schedule?` (`{ type:'cron', expression, timezone }`), `scheduleInput?`, `concurrencyPolicy?` (`'allow'|'forbid'`), `workspace?`, `outputVisibility?` (see [Output visibility](../workflows/output-visibility.md)), `env.template()` overlays (see [Env overlays](../workflows/env-overlays.md)).
52
+
53
+ ### Builder verbs and the placement rule
54
+
55
+ **The call site places.** Every builder call โ€” `then`, `agentStep`, `specialistStep`, `toolStep`, `map`, `approval`, `waitForSignal`, `sleep`, `sleepUntil`, `foreach`, `parallel`, `branch`, `switch`, `dowhile`, `dountil`, `workflow` โ€” appends **exactly one** entry to the chain where it is called. Containers take `StepRef`s: an inline step object, or a **string** naming an `agentStep`/`specialistStep`/`toolStep` declared elsewhere in the same chain โ€” a string ref inside a container means "declare here, inside me" and does not append a second top-level entry. An id declared but never placed is `WORKFLOW_UNPLACED_STEP` (warning locally, error at push); referenced but never declared is `unknown-step-ref` at `.commit()`.
56
+
57
+ | Verb | Entry |
58
+ | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
59
+ | `.then(step, input?)` | Sequential step |
60
+ | `.agentStep(id, { agentId, prompt, outputSchema?, toolScope?, tier?, โ€ฆ })` | Agent turn (`prompt` is a `template(...)`) |
61
+ | `.specialistStep(id, { role, prompt, โ€ฆ })` | Ephemeral role on the owning agent (D25) |
62
+ | `.toolStep(id, { toolId, input? })` | One tool call โ€” `toolId` must name a `LuaTool` the compiler can see as a tool primitive (declared in its own module and exported, or registered on the agent); a tool object defined inline in the workflow file is not detected and fails `WORKFLOW_TOOL_REF_UNRESOLVED` at compile |
63
+ | `.map(descriptors, { id })` | Data reshaping โ€” `id` is **required** once the workflow has โ‰ฅ 2 maps (`map-id-required`) |
64
+ | `.parallel([...arms], { merge? })` | Concurrent arms; a `[map, step]` pair is a legal arm (lowered to an implicit subrun) |
65
+ | `.switch([[predicate, armRef]...], otherwise?)` / `.branch(...)` | Conditional (exclusive / inclusive) |
66
+ | `.foreach(step, { concurrency?, maxItems?, chunk?, rateLimit? })` / `.foreach({ items })` | Fan-out over an upstream array |
67
+ | `.dowhile(ref, predicate, { maxIterations, intervalSeconds? })` / `.dountil(...)` | Loop |
68
+ | `.sleep(ms)` / `.sleepUntil(template)` | Engine-side waits โ€” `sleepUntil` lowers to an `<id>_at` mapping + `sleepUntil{dateFrom}` (D6-r1) |
69
+ | `.approval(id, { title, approver, timeoutHours, onTimeout, editable?, editablePaths?, itemsPath?, โ€ฆ })` | Human gate โ€” see [Approvals](../workflows/approvals.md) |
70
+ | `.waitForSignal(id, { signal, schema?, timeoutHours, acceptedSources? })` | External event via `Workflows.signal` โ€” completes with `{ payload, source: { kind, id, principalId? }, signalId, receivedAt }` |
71
+ | `.workflow(id, ref, input?, { workspace? })` | Child run the parent waits for |
72
+
73
+ ### Typed predicates
74
+
75
+ `step(x).path('confidence')` / `stepOf<typeof schema>('id').path('confidence')` yield a `TypedRef<T>`; `eq`, `gt`, `gte`, `inSet` type-check against `lit(...)` literals โ€” `gt(number, lit('a'))` does not compile.
76
+
77
+ ### Bindings
78
+
79
+ `fromInit(path)` ยท `fromStep(id, path?)` ยท `template('${initData.x} ${stepResults.id.y}')` ยท `fromKnowledge({ source, query, maxChars })` ยท `rows(id, path, { offset, limit })` (paged dataset rows) ยท `env(key)` (run-time secret) vs `env.template(key)` (push-time overlay).
80
+
81
+ ## Testing and pushing
82
+
83
+ - Offline: `lua test workflow <name>` โ€” the local reference driver ([Testing locally](./Workflows.md#testing-locally), full flags in [Testing offline](../workflows/testing-offline.md)).
84
+ - `lua push workflow` compiles, validates (placement, schema flow, caps preflight) and creates a sandbox version; `lua workflows deploy` / `activate` manage the lifecycle.
85
+
86
+ ## Related
87
+
88
+ - [Workflows](./Workflows.md) โ€” the runtime API (`Workflows.start/get/list/cancel/resume/signal`)
89
+ - [When a step parks](../workflows/recovery.md) ยท [Approvals](../workflows/approvals.md) ยท [Script form](../workflows/script-form.md)
@@ -0,0 +1,111 @@
1
+ # Workflows API
2
+
3
+ The Workflows API lets your tools, jobs, webhooks, triggers and other workflows start, inspect and steer **workflow runs** โ€” durable, multi-step programs you author with `createWorkflow(...)` and push with `lua push workflow`.
4
+
5
+ ## Import
6
+
7
+ ```typescript
8
+ import { Workflows } from 'lua-cli';
9
+ ```
10
+
11
+ Inside a sandbox (tools, jobs, webhooks, workflow code steps) `Workflows` is also available as a global.
12
+
13
+ ## Overview
14
+
15
+ - `start` **never awaits execution** โ€” it returns `{ runId, status }` immediately. `waitSeconds` (โ‰ค 55) asks the server to hold the response for early terminal state; it changes the response, never the execution model.
16
+ - Runs are idempotent on `idempotencyKey` โ€” re-sending the same key returns the existing run instead of a duplicate.
17
+ - A workflow with `concurrencyPolicy: 'forbid'` refuses a second start while one is in flight with a thrown `WorkflowApiError { code: 'RUNS_IN_FLIGHT', blockingRunId }` โ€” never a 429, never a gate.
18
+ - `status: 'gated'` means the run is parked (quota, billing or consent) and holds no slot. Consent gates are cleared from the desktop or with `lua workflows approve`.
19
+ - `nameOrId` resolves the workflow **name on the calling agent first**, then a workflow id.
20
+ - `get` / `list` honour output visibility: a restricted run comes back with `restricted: true` and no `output` โ€” never an error.
21
+ - From a workflow code step, `Workflows.start` creates a **detached** run (no parent link). Use the `.workflow(...)` builder node when the parent should wait.
22
+
23
+ ## Methods
24
+
25
+ ### `Workflows.start(nameOrId, input?, opts?)`
26
+
27
+ Starts a run.
28
+
29
+ **Parameters:**
30
+
31
+ - `nameOrId` (string) โ€” workflow name (agent-local) or id
32
+ - `input` (unknown) โ€” validated against the workflow's `inputSchema`
33
+ - `opts` (object, optional)
34
+ - `idempotencyKey` (string) โ€” dedup key (โ‰ค 128 chars)
35
+ - `budget` (`{ maxCredits?, maxSteps?, maxDurationSeconds? }`)
36
+ - `waitSeconds` (number โ‰ค 55) โ€” server long-poll
37
+ - `initialState` (object โ‰ค 64 KB) โ€” seeds `ctx.state`
38
+ - `correlationKey` (string), `tags` (string[] โ‰ค 10)
39
+ - `replyTo` (`{ channel, threadId }`) โ€” customer-channel continuation
40
+ - `workflowVersionId` (string) โ€” pin a version (default: the active one)
41
+
42
+ **Returns:** `Promise<{ runId, status: 'queued' | 'gated' | 'running' | 'completed' | 'failed', output? }>`
43
+
44
+ ```typescript
45
+ const { runId, status } = await Workflows.start(
46
+ 'outreach',
47
+ { leads },
48
+ { idempotencyKey: `outreach:${batchId}`, tags: ['crm'] }
49
+ );
50
+ ```
51
+
52
+ ### `Workflows.get(runId)`
53
+
54
+ Returns the run summary (status, counts, gate, lineage, `output` when readable).
55
+
56
+ ### `Workflows.list(opts?)`
57
+
58
+ `opts`: `{ limit?, status?, workflow?, correlationKey?, tags?, sort? }` โ€” `tags` are AND-ed; `sort` is one of `-createdAt | createdAt | -durationMs | durationMs`.
59
+
60
+ ### `Workflows.cancel(runId, opts?)`
61
+
62
+ `opts.mode` is `'request'` (default, two-stage) or `'force'` (after `forceAvailableAt`). Returns `{ status, nextAction: 'cancel_again' | 'force' | 'none', forceAvailableAt? }`. A running code step is killed within seconds; if the runner cannot be reached the step ends at its next boundary (โ‰ค 10 min) โ€” cancellation is never lost either way.
63
+
64
+ ### `Workflows.resume(runId, stepId, resumeData)`
65
+
66
+ Resumes a step suspended with `ctx.suspend(...)`. The loser of a resume race receives `{ resumed: false, reason: 'already_resumed', recorded }` โ€” never an error. Approvals resolve through `lua workflows approve` (human-only); signals through `Workflows.signal`.
67
+
68
+ ### `Workflows.signal(runId, name, payload?, opts?)`
69
+
70
+ Delivers a named signal to a run waiting on `waitForSignal(name)`. `opts.dedupeKey` makes a re-send a no-op (`{ accepted: true, duplicate: true }`). Returns `{ accepted, reason?: 'source_not_accepted' | 'duplicate' | 'parked' }`. The waiting step completes with `{ payload, source: { kind, id, principalId? }, signalId, receivedAt }` โ€” read the payload as `stepResults.<id>.payload`.
71
+
72
+ ### Reserved members
73
+
74
+ `startBatch`, `signalByKey`, `raiseBudget`, `setGoal` and `goals.{list,get,pause,resume,close}` are part of the API surface (the member list is frozen) and throw `WorkflowApiError { code: 'WORKFLOWS_API_UNAVAILABLE' }` until their server routes ship. For `startBatch`: from an agent turn, prefer the `startWorkflowRunBatch` tool (one consent card for the whole batch, D23-r5).
75
+
76
+ ## Errors
77
+
78
+ All failures throw `WorkflowApiError` with `code` (the server discriminator โ€” `RUNS_IN_FLIGHT`, `SUSPENDED_CAP`, `WORKFLOW_NOT_FOUND`, `CONTROL_UNAVAILABLE`, โ€ฆ), `statusCode`, and any extra fields the server returned (for example `blockingRunId`).
79
+
80
+ ```typescript
81
+ try {
82
+ await Workflows.start('nightly-sync', {});
83
+ } catch (e) {
84
+ if (e.code === 'RUNS_IN_FLIGHT') return `Already running: ${e.blockingRunId}`;
85
+ throw e;
86
+ }
87
+ ```
88
+
89
+ ## Testing locally
90
+
91
+ `lua test workflow <name>` runs a compiled workflow **offline** through the local reference driver โ€” no engine, no cluster:
92
+
93
+ ```bash
94
+ lua test workflow outreach --input @leads.json \
95
+ --step-output draftEmail=@fixtures/draft.json \
96
+ --approve reviewDrafts=@edited.json \
97
+ --ledger-out out.json
98
+ ```
99
+
100
+ - `--step-output <id>=@file|<json>` completes a step without running it (validated against its `outputSchema`) โ€” this is how a predicate over an agent output gets both truth values under the default `--agents fake`.
101
+ - `--approve <id>[=@payload]` / `--deny <id>[=@reason]` pre-answer approvals; `--signal <name>=<json>` pre-supplies `waitForSignal` payloads in order.
102
+ - `--fixtures <dir>` replays recorded agent/tool outputs (`--record <dir>` writes them); a missing fixture exits **5** โ€” never a silent fake.
103
+ - `--from-run <runId>` seeds every completed step from a real run and restarts at the first non-completed one.
104
+ - `--ledger-out <file>` writes the in-memory ledger; `lua workflows replay <runId> --local` re-evaluates a real run's predicates and mappings against your compiled artifact and reports `LEDGER_DIVERGENCE`.
105
+ - `--park <id>` simulates a platform-fault park of a `sideEffects:'external'` step (retry / skip / complete / fail, same verbs as production); `--fast-retries` collapses retry backoff to 0; `--real-time` actually waits instead of fast-forwarding the virtual clock; `--artefacts-dir <dir>` backs `ctx.artefacts.*` on disk.
106
+
107
+ ## Related
108
+
109
+ - `lua workflows` โ€” list, start, runs, status, watch, attach, cancel, resume, approve, signal, replay, logs, delete โ€” `attach` re-attaches to a running run's event stream (the same stream your chat card re-attaches to)
110
+ - `lua workflows goals <list|get|create|pause|resume|close>` and `lua workflows schedules <list|delete>` โ€” the operator surface for goals (R57โ€“R62) and schedule Jobs; `lua workflows view --json` carries `schedules` (goal-owned rows tagged `goalId`) and `goals`. See [workflows/goals.md](../workflows/goals.md) and [workflows/schedules.md](../workflows/schedules.md).
111
+ - [LuaWorkflow](./LuaWorkflow.md) โ€” the builder primitive
@@ -0,0 +1,41 @@
1
+ # Approvals
2
+
3
+ _Source of truth: workflows-spec 06 ยง6.4. This page is the developer summary; the spec is normative._
4
+
5
+ `.approval(id, cfg)` parks the run until a person decides. The node completes with the decision as data โ€” branchable, never an exception. A human decision completes it with
6
+
7
+ ```typescript
8
+ {
9
+ approved: boolean;
10
+ note?: string; // the approver's note, when one was left
11
+ editedPayload?: unknown; // only when the approver edited (see below)
12
+ editRevision: number; // 0 when the payload was never edited
13
+ decidedBy: { id?: string; kind: string }; // who decided
14
+ evidence?: string[]; // decision artefact ids frozen with the decision
15
+ }
16
+ ```
17
+
18
+ A timeout that ends the chain in `'deny'` completes it with the sweep's shape instead โ€” `{ approved: false, timedOut: true, escalations: <hops> }` (no `decidedBy`, `editRevision` or `evidence`); `'continue'` is treated as `'deny'` for approvals; `'cancel-run'` cancels the run. The original payload is not echoed back โ€” read it from the step's own input (`stepResults`), not from the output.
19
+
20
+ ## Approver specs
21
+
22
+ `approver:` takes `'creator' | 'org-admins' | { users: [...] } | { role: '<org-role>' } | { group: '<group>' }`. Role/group specs are org data and template-portable; user lists are not. Approvers are re-resolved **at suspend and at decision** โ€” a role change mid-wait applies.
23
+
24
+ - **Maker-checker**: `excludeInitiator: true` โ€” whoever started the run can never approve it. On a customer-channel run (`principalKind:'customer'`) an approval resolving to `'creator'` fails `approver-is-run-principal`.
25
+ - **Four-eyes**: `fourEyes: { edit: <spec>, approve: <spec> }` โ€” whoever edits the payload cannot be the one who approves it.
26
+
27
+ ## Deadlines and escalation chains
28
+
29
+ `timeoutHours` runs on **business time** when `businessHours: { tz, calendar }` is set. `onTimeout` is a chain: each `{ escalateTo, timeoutHours }` hop re-prompts the next audience; the terminal `'deny'` (or `'cancel-run'`/`'continue'`) completes the node โ€” e.g. `{approved:false, timedOut:true, escalations:2}`.
30
+
31
+ ## Edited payloads
32
+
33
+ `editable: true` + `editablePaths: ['amount', 'drafts[*].body']` (the `a.b[0].c` / `[*]` grammar) lets the approver patch the payload. The sequence is **fetch โ†’ patch โ†’ approve-with-fingerprint**: the approve call echoes the fingerprint of the revision the approver saw, so a concurrent edit forces a re-read. Out-of-grammar paths are refused; `editedPayloadSchema` validates the result.
34
+
35
+ ## Where approvals surface
36
+
37
+ The desktop inbox and run detail carry the full card (per-item rows, edits, escalation state). **Text channels (WhatsApp/SMS/email) approve or deny the whole batch only** โ€” no per-item decisions, no edits.
38
+
39
+ Per-item approvals (`itemsPath` / `itemApprover` / `itemTimeout`) fan one node out to one decision per item โ€” see [Per-item approvals](./per-item-approvals.md).
40
+
41
+ Offline: `--approve <id>[=@payload]` / `--deny <id>[=@reason]` pre-answer the prompt; an edited payload is checked with the same `matchesEditablePath` + `editedPayloadSchema` the server uses.
@@ -0,0 +1,19 @@
1
+ # Artefacts and datasets
2
+
3
+ _Source of truth: workflows-spec (P1-8; Cluster K B40). This page is the developer summary; the spec is normative._
4
+
5
+ `ctx.artefacts.put(name, data, { contentType?, kind?, datasetSchema?, title?, source? })` stores a file on the run (โ‰ค 50 per step) and returns `{ artefactId }`; `get(id)` / `list()` read back. Steps pass **references**, not bytes.
6
+
7
+ ## Datasets โ€” `{__datasetRef}`
8
+
9
+ An array-typed step output over **8 MB** becomes a `{__datasetRef}` (NDJSON on the CDN) instead of `OUTPUT_TOO_LARGE`. Bind pages with `rows(stepId, path, { offset, limit })` โ€” the bare ref renders as the ref object. `--input` values may carry `{"__artefactRef": "<id>"}` to hand a run an existing artefact.
10
+
11
+ ## Streaming and datasets
12
+
13
+ `get(id).stream({ range })` reads large artefacts without buffering; `get(id).rows({ offset, limit })` pages NDJSON/parquet datasets (parquet row groups pass through untranscoded). Ceilings: 256 MB inline, 4 GiB streamed.
14
+
15
+ ## Titles and sources
16
+
17
+ `title` and `source` metadata surface in the desktop artefact pane and in exports; `lua workflows artefact <runId> <artefactId>` fetches one from the CLI (presigned).
18
+
19
+ Offline: `--artefacts-dir <dir>` backs `ctx.artefacts.*` on disk (`<dir>/<artefactId>` + `<artefactId>.meta.json`, ids `local-<n>`); without it every call fails loudly (`ARTEFACTS_NOT_AVAILABLE_OFFLINE`) โ€” never a silent fake. Dataset-ref paging is engine-side.
@@ -0,0 +1,12 @@
1
+ # Coding harness โ€” `claude-code` vs `generic`
2
+
3
+ _Source of truth: workflows-spec 05 ยง5.17.6 (D27). This page is the developer summary; the spec is normative._
4
+
5
+ `harness:'claude-code'` runs Claude Code against Anthropic-class models; `harness:'generic'` runs Lua's own agent loop with the **same nine workspace tools** against any provider your org is allowed โ€” the tool ids, the budget, the secret scan and the turn limit are identical.
6
+
7
+ - Absent `harness`, the platform derives one from the step's model; the local driver prints the derived value so you see what the server will stamp.
8
+ - `LUA_WF_JOB_PROVIDERS` gates providers per org; an out-of-list model is `job-tier-provider-unsupported`.
9
+ - **Org BYOK keys go through the sidecar proxy โ€” your key never enters the agent container.**
10
+ - Billing rows carry `model` + `harness` (seat multiplier per provider).
11
+
12
+ Offline: `--agents live` with `harness:'generic'` runs the generic provider in-process against **your own** key from the environment (`OPENAI_API_KEY` / `GOOGLE_API_KEY` / `ANTHROPIC_API_KEY`) โ€” billing and governance are server-side only.
@@ -0,0 +1,16 @@
1
+ # Compliance gates โ€” EXT-* features shipped dark
2
+
3
+ _Source of truth: workflows-spec ยง15 gates. This page is the developer summary; the spec is normative._
4
+
5
+ Several enterprise surfaces are **specified and shipped dark; each flips on its own gate** โ€” the code is in the release, inert until the platform opens the gate for your org:
6
+
7
+ | Gate | Surface | Dark behaviour |
8
+ |---|---|---|
9
+ | `EXT-REGION` | Region-pinned Job workspaces / data residency | `region-unavailable` at push |
10
+ | `EXT-KMS` | BYOK key-alias on workspaces + artefacts (B38) | alias refused at push |
11
+ | `EXT-AUDIT-API` | SIEM pull / `lua workflows audit-search` (B39, R72) | the verb answers **501** |
12
+ | `EXT-EFS` | `workspace.backend: 'efs' \| 's3'` | `workspace-backend-unavailable` (refused, not downgraded) |
13
+ | `EXT-GOVERNANCE` | Signed external governance review (`routeReview`) | policy verdicts stay platform-internal |
14
+ | `EXT-APPROVALS` / `EXT-SCIM` | External approval systems / SCIM approver sync | approver specs resolve org-locally |
15
+
16
+ Asking for a gate is an org-admin โ†’ platform request; nothing in your workflow file changes when it opens.
@@ -0,0 +1,10 @@
1
+ # Connections in coding turns
2
+
3
+ _Source of truth: workflows-spec 05 ยง5.17.6 / 11 ยง11.5. This page is the developer summary; the spec is normative._
4
+
5
+ `toolScope.connectionIds` on a Job-tier agent step mounts MCP connections into the coding turn **through a local proxy**: the model calls `tools/list` / `tools/call`; the proxy holds the token โ€” the model never sees it.
6
+
7
+ - `requiredConnections` (and `workspace.credentialsRef`) accept a declared `connections[].key` instead of an id โ€” declare a key; it resolves against the owner agent's connections on any agent at run time (see [Git credentials](./git-credentials.md)).
8
+ - A tool that needs approval is **refused inside the turn** (`mcp_call_denied`) โ€” hand it to a worker-tier step where the approval card can park the run.
9
+ - Calls are capped per segment; the cap and usage appear in the step detail.
10
+ - Offline: connections are **not mounted** (no proxy, no token mint) โ€” `--mcp <connectionId>=@fixture.json` stubs `tools/list` / `tools/call` from a `{ tools:[โ€ฆ], calls:{ [tool]: result } }` fixture; an unstubbed call returns the production `mcp_call_denied{reason:'not_in_scope'}` shape.
@@ -0,0 +1,11 @@
1
+ # Correlation keys
2
+
3
+ _Source of truth: workflows-spec (P1-3 / P1-14). This page is the developer summary; the spec is normative._
4
+
5
+ `correlationKey` stamps a run with YOUR business identity (`ticket-4711`, `order-โ€ฆ`): pass it at `Workflows.start(..., { correlationKey })`, from a trigger's `correlationKeyTemplate`, or `lua workflows start --correlation-key`.
6
+
7
+ - `Workflows.signalByKey(nameOrId, correlationKey, name, payload?)` delivers a signal **by business key** โ€” no runId bookkeeping in your webhook; `allowMultiple` fans out when several runs share the key.
8
+ - `Workflows.list({ correlationKey })` / `lua workflows runs --correlation-key` find the run(s).
9
+ - `tags` are free-form labels (AND-ed in list filters); `replyTo: { channel, threadId }` routes the terminal reply back to the originating conversation.
10
+
11
+ Offline: `--correlation-key`, `--tag`, `--reply-to <channel>:<threadId>` and `--on-behalf-of` stamp `ctx.runtime.*`; the would-be reply prints on the terminal instead of sending.
@@ -0,0 +1,12 @@
1
+ # Env overlays โ€” `env.template()` vs `env()`
2
+
3
+ _Source of truth: workflows-spec (Cluster K B33, WF-543). This page is the developer summary; the spec is normative._
4
+
5
+ **`env.template('KEY')` = a graph literal resolved at push**, visible in `lua workflows env-overlay <name>` (a presence table โ€” values are never printed). **`env('KEY')` = a run-time secret read inside `execute`.** One file, per-env values: staging and prod resolve different overlays over the **same `graphHash`** (the hash covers placeholders only; `envOverlayHash` sits outside the consent fingerprint).
6
+
7
+ - A missing key at push is ERROR `env-template-missing` (exit 1; the version is never sent).
8
+ - Keys matching `/(SECRET|TOKEN|KEY|PASSWORD)$/` are refused at compile (`env-template-secret-key`) โ€” secrets belong in `env()`.
9
+ - Caps: โ‰ค 64 keys, values โ‰ค 4 KB (`invalid-envelope`).
10
+ - Installing a template re-resolves the overlay against the installing org's values.
11
+
12
+ Offline: `lua test workflow --env KEY=value` (repeatable; then the project's `.env`) โ€” a missing key is exit 2, and values never reach `--ledger-out`.
@@ -0,0 +1,11 @@
1
+ # Evidence bundles and run exports
2
+
3
+ _Source of truth: workflows-spec (Cluster E R53โ€“R56 / D24). This page is the developer summary; the spec is normative._
4
+
5
+ `lua workflows export-run <runId> --out <dir>` writes one run's evidence bundle: the run doc, per-step rows (inputs/outputs/attempts), events, approvals with decision fingerprints, and artefact manifests. `export-runs --full --since โ€ฆ --until โ€ฆ` bulk-exports a window โ€” the DSAR and audit path (`archive-runs` adds zip-per-run + an `archive-index.ndjson`).
6
+
7
+ - Output visibility applies: restricted payloads export as `restricted: true` unless the caller's roles pass (owner bypass is audited).
8
+ - Very large originals are referenced, not inlined (a 413 `PAYLOAD_PAGE_REQUIRED` answer means: page the artefact endpoint).
9
+ - `--no-inputs` / `--no-artefacts` trim bundles for privacy-sensitive hand-offs.
10
+
11
+ See also [Exports](./exports.md) for policy-driven terminal exports.