lua-cli 3.29.1 โ†’ 3.31.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 (61) hide show
  1. package/dist/api-exports.d.ts +1185 -77
  2. package/dist/api-exports.js +5032 -137
  3. package/dist/api-exports.js.map +1 -1
  4. package/dist/index.js +25610 -14399
  5. package/dist/index.js.map +1 -1
  6. package/dist/voice/test/index.d.ts +40 -40
  7. package/dist/workflow-builder.d.ts +766 -0
  8. package/dist/workflow-builder.js +5732 -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 +73 -0
  14. package/docs/api/Workflows.md +110 -0
  15. package/docs/workflows/approvals.md +28 -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 +9 -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 +11 -0
  26. package/docs/workflows/knowledge-bindings.md +13 -0
  27. package/docs/workflows/limits.md +11 -0
  28. package/docs/workflows/long-steps-and-checkpoints.md +13 -0
  29. package/docs/workflows/migrating-cloud-tasks.md +9 -0
  30. package/docs/workflows/migrating-runs.md +13 -0
  31. package/docs/workflows/output-visibility.md +9 -0
  32. package/docs/workflows/per-item-approvals.md +9 -0
  33. package/docs/workflows/private-network-sources.md +12 -0
  34. package/docs/workflows/recovery.md +28 -0
  35. package/docs/workflows/replay-local.md +35 -0
  36. package/docs/workflows/reply-channels.md +11 -0
  37. package/docs/workflows/retention-and-archival.md +82 -0
  38. package/docs/workflows/roles.md +12 -0
  39. package/docs/workflows/schedules.md +11 -0
  40. package/docs/workflows/script-form.md +50 -0
  41. package/docs/workflows/testing-offline.md +46 -0
  42. package/docs/workflows/workspace-backends.md +11 -0
  43. package/docs/workflows/workspaces-and-long-steps.md +27 -0
  44. package/package.json +7 -2
  45. package/scripts/run-api-extractor.mjs +1 -1
  46. package/template/.gitignore +2 -0
  47. package/template/examples/workflows/CLAUDE.md +24 -0
  48. package/template/examples/workflows/adversarial-verify.workflow.script.js +48 -0
  49. package/template/examples/workflows/github-review.webhook.ts +19 -0
  50. package/template/examples/workflows/linear-ready.trigger.ts +21 -0
  51. package/template/examples/workflows/outreach.ts +55 -0
  52. package/template/examples/workflows/pr-review-round.ts +38 -0
  53. package/template/examples/workflows/provision-tenant.ts +18 -0
  54. package/template/examples/workflows/refund-approval.ts +44 -0
  55. package/template/examples/workflows/research-brief.ts +42 -0
  56. package/template/examples/workflows/reviewed-brief.ts +19 -0
  57. package/template/examples/workflows/support-triage.ts +44 -0
  58. package/template/examples/workflows/ticket-to-pr.ts +65 -0
  59. package/template/examples/workflows/vendor-invoices.ts +30 -0
  60. package/template/lua.skill.yaml +1 -0
  61. 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.29.1
1
+ # lua-cli v3.31.0
2
2
 
3
- Welcome to the comprehensive API documentation for lua-cli v3.29.1. This guide covers every API, class, and function exported by the package.
3
+ Welcome to the comprehensive API documentation for lua-cli v3.31.0. 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.29.1. 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.29.1. 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,73 @@
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 { createStep, createWorkflow, defineWorkflow, step, stepOf, eq, gt, gte, lit, template, fromInit, fromStep, fromKnowledge, rows, env } from 'lua-cli';
11
+ ```
12
+
13
+ ## `createStep(config)`
14
+
15
+ A typed code step. `inputSchema` / `outputSchema` / `resumeSchema` are zod schemas; the compiler serializes them to JSON Schema.
16
+
17
+ | Field | Meaning |
18
+ |---|---|
19
+ | `id` | Unique step id (referenced by `then`, containers, `fromStep`) |
20
+ | `inputSchema` / `outputSchema` | zod; outputs are validated after every attempt |
21
+ | `timeoutSeconds` | Per-attempt wall (worker tier โ‰ค 600; `tier:'job'` up to 86 400) |
22
+ | `retry` | `{ maxAttempts, backoffSeconds?, backoff?: 'fixed'\|'exponential', maxBackoffSeconds? }` โ€” waits `backoffSeconds ยท 2^(attempt-1)` capped at `maxBackoffSeconds` |
23
+ | `sideEffects` | `'external'` โ‡’ never auto-retried on a platform-fault reclaim โ€” the step **parks** instead (see [When a step parks](../workflows/recovery.md)) |
24
+ | `onError` | `'fail'` (default) ยท `'continue'` ยท `'park'` |
25
+ | `requiredConnections` | Connection ids that must mount before `execute` runs |
26
+ | `tier` / `workspace` / `jobResources` / `jobTools` | Job-tier fields โ€” see [Workspaces and long steps](../workflows/workspaces-and-long-steps.md) |
27
+ | `execute(ctx)` | `ctx`: `inputData`, `resumeData`, `getInitData()`, `getStepResult(id)`, `state`, `suspend()`, `bail()`, `bailRun()`, `once()`, `log()`, `env`, `artefacts`, `occurrenceId`, `workspace?` |
28
+
29
+ `ctx.suspend(payload)` parks the step for `Workflows.resume(runId, stepId, resumeData)`; on resume **`execute` re-runs from the top** with `ctx.resumeData` set.
30
+
31
+ ## `createWorkflow(config)โ€ฆcommit()`
32
+
33
+ `createWorkflow(cfg)` opens a fluent builder; `.commit()` freezes it into the compiled graph. `defineWorkflow(cfg, (wf) => โ€ฆ)` is sugar for the same thing.
34
+
35
+ 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)).
36
+
37
+ ### Builder verbs and the placement rule
38
+
39
+ **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()`.
40
+
41
+ | Verb | Entry |
42
+ |---|---|
43
+ | `.then(step, input?)` | Sequential step |
44
+ | `.agentStep(id, { agentId, prompt, outputSchema?, toolScope?, tier?, โ€ฆ })` | Agent turn (`prompt` is a `template(...)`) |
45
+ | `.specialistStep(id, { role, prompt, โ€ฆ })` | Ephemeral role on the owning agent (D25) |
46
+ | `.toolStep(id, { toolId, input? })` | One tool call |
47
+ | `.map(descriptors, { id })` | Data reshaping โ€” `id` is **required** once the workflow has โ‰ฅ 2 maps (`map-id-required`) |
48
+ | `.parallel([...arms], { merge? })` | Concurrent arms; a `[map, step]` pair is a legal arm (lowered to an implicit subrun) |
49
+ | `.switch([[predicate, armRef]...], otherwise?)` / `.branch(...)` | Conditional (exclusive / inclusive) |
50
+ | `.foreach(step, { concurrency?, maxItems?, chunk?, rateLimit? })` / `.foreach({ items })` | Fan-out over an upstream array |
51
+ | `.dowhile(ref, predicate, { maxIterations, intervalSeconds? })` / `.dountil(...)` | Loop |
52
+ | `.sleep(ms)` / `.sleepUntil(template)` | Engine-side waits โ€” `sleepUntil` lowers to an `<id>_at` mapping + `sleepUntil{dateFrom}` (D6-r1) |
53
+ | `.approval(id, { title, approver, timeoutHours, onTimeout, editable?, editablePaths?, itemsPath?, โ€ฆ })` | Human gate โ€” see [Approvals](../workflows/approvals.md) |
54
+ | `.waitForSignal(id, { signal, schema?, timeoutHours, acceptedSources? })` | External event via `Workflows.signal` |
55
+ | `.workflow(id, ref, input?, { workspace? })` | Child run the parent waits for |
56
+
57
+ ### Typed predicates
58
+
59
+ `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.
60
+
61
+ ### Bindings
62
+
63
+ `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).
64
+
65
+ ## Testing and pushing
66
+
67
+ - Offline: `lua test workflow <name>` โ€” the local reference driver ([Testing locally](./Workflows.md#testing-locally), full flags in [Testing offline](../workflows/testing-offline.md)).
68
+ - `lua push workflow` compiles, validates (placement, schema flow, caps preflight) and creates a sandbox version; `lua workflows deploy` / `activate` manage the lifecycle.
69
+
70
+ ## Related
71
+
72
+ - [Workflows](./Workflows.md) โ€” the runtime API (`Workflows.start/get/list/cancel/resume/signal`)
73
+ - [When a step parks](../workflows/recovery.md) ยท [Approvals](../workflows/approvals.md) ยท [Script form](../workflows/script-form.md)
@@ -0,0 +1,110 @@
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' }`.
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
+ - [LuaWorkflow](./LuaWorkflow.md) โ€” the builder primitive
@@ -0,0 +1,28 @@
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 `{ approved, payload?, edited?, timedOut?, escalations? }` โ€” branchable data, never an exception.
6
+
7
+ ## Approver specs
8
+
9
+ `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.
10
+
11
+ - **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`.
12
+ - **Four-eyes**: `fourEyes: { edit: <spec>, approve: <spec> }` โ€” whoever edits the payload cannot be the one who approves it.
13
+
14
+ ## Deadlines and escalation chains
15
+
16
+ `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}`.
17
+
18
+ ## Edited payloads
19
+
20
+ `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.
21
+
22
+ ## Where approvals surface
23
+
24
+ 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.
25
+
26
+ Per-item approvals (`itemsPath` / `itemApprover` / `itemTimeout`) fan one node out to one decision per item โ€” see [Per-item approvals](./per-item-approvals.md).
27
+
28
+ 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,9 @@
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
+ - 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.
8
+ - Calls are capped per segment; the cap and usage appear in the step detail.
9
+ - 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.
@@ -0,0 +1,5 @@
1
+ # Exports on terminal state
2
+
3
+ _Source of truth: workflows-spec (Cluster K B45). This page is the developer summary; the spec is normative._
4
+
5
+ `workflowPolicy.export.onTerminal` (org policy) writes an evidence bundle for every terminal run to the configured destination โ€” the always-on sibling of the manual [evidence bundles](./evidence-bundles.md). `lua workflows exports` lists what was written (destination, run, status); failures surface in the org inbox, and the run itself is never blocked by an export failure.
@@ -0,0 +1,11 @@
1
+ # External content and `toolScope` โ€” the taint rule
2
+
3
+ _Source of truth: workflows-spec (Cluster E, P1-16). This page is the developer summary; the spec is normative._
4
+
5
+ A step is **external-content tainted** when anything untrusted can reach its prompt: customer-channel input (`${initData.message}` on a customer-started run), `{knowledge}` bindings, or output flowing from a tool of the external kinds (`web-search`, `web-fetch`, `email-read`, `http`, `scrape`). Taint **propagates along bindings** โ€” a step consuming a tainted step's output is tainted too (`computeExternalContentTaint` walks the plan).
6
+
7
+ **A tainted `agentStep` must declare `toolScope`** โ€” `{}` means "no tools" (and `defaultToolScopeMode:'deny'` implies it when absent). The point: prompt-injected content must not be able to steer an agent that holds tools.
8
+
9
+ Walk-through โ€” `support-triage` (ยง3.2 (g)): `classify` renders the customer message AND a knowledge binding โ‡’ tainted โ‡’ `toolScope: {}`; the downstream `refundGate` approval routes to `org-admins` (never `'creator'` โ€” on a customer-channel run that is `approver-is-run-principal`).
10
+
11
+ Push refuses a tainted step without `toolScope` (`external-content-needs-toolscope`); the desktop run detail marks tainted steps with the provenance chain (`via`).
@@ -0,0 +1,11 @@
1
+ # Git credentials in workflows
2
+
3
+ _Source of truth: workflows-spec 11 ยง11.5.6 / 05 ยง5.17.3. This page is the developer summary; the spec is normative._
4
+
5
+ `workspace.credentialsRef` names a **git-host connection** (e.g. your org's GitHub App install). Per Job, the platform mints a **short-lived token held by a sidecar** โ€” git operations in the step go through the credential proxy; the token never enters the agent container and never appears in `env`.
6
+
7
+ > **Never put a PAT in `env`.** Rotation or revocation of the underlying connection makes the next mint fail: the step fails `credentials_revoked{git_token_mint_failed}` and โ€” with `onError:'park'` โ€” enters the park/`retry-step` loop, so a re-grant resumes the run without losing work.
8
+
9
+ - `jobTools:['gh']` is what mints `GH_TOKEN` for a code step (`pull_requests:write` + `contents:write` + `issues:read`, repo-scoped). Merge is a contents write โ€” human-only-merge orgs keep `contents:write` off the App.
10
+ - Templates never hardcode a connection id: they use `connections[].key`, resolved at install.
11
+ - Offline, `credentialsRef` is printed and **not** resolved (no token mint) โ€” `--credentials-ref-ok` suppresses the notice; the local driver uses your own git credentials.
@@ -0,0 +1,13 @@
1
+ # Knowledge bindings
2
+
3
+ _Source of truth: workflows-spec (P1-11; Cluster K B41). This page is the developer summary; the spec is normative._
4
+
5
+ `fromKnowledge({ source, query, maxChars?, topK? })` as a map descriptor retrieves at render time (S2): each chunk arrives under a **provenance header** โ€” `[knowledge source=<source> doc=<doc> chunk=<n> score=<s>]` โ€” so the model can cite and the audit trail can trace. `maxChars` caps the rendered block; a retrieval timeout renders the EMPTY block plus a `knowledge: empty` warning (the run continues).
6
+
7
+ ## Connection sources (B41)
8
+
9
+ `source:'connection'` with `connectionId` retrieves from Drive / Notion / Confluence through the org's connection. Requirements: the step must carry `toolScope` (connection knowledge is external content โ€” see [External content](./external-content-and-toolscope.md)); the compiler's overlay pass adds the knowledge connection to the step's mounted set.
10
+
11
+ Any knowledge binding taints the step (P1-16).
12
+
13
+ Offline: knowledge retrieval is not emulated โ€” a knowledge binding fails closed (`binding_unresolved`) unless you `--step-output` the consuming step. (`--knowledge <key>=@file` substitution is specified in 03 ยง3.9 step 7 and not yet wired in this build.)