lua-cli 3.32.3 โ 3.32.4
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.
- package/dist/api-exports.d.ts +5 -3
- package/dist/api-exports.js +634 -297
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +1443 -923
- package/dist/index.js.map +1 -1
- package/dist/workflow-builder.d.ts +5 -3
- package/dist/workflow-builder.js +426 -256
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +1 -1
- package/docs/workflows/approvals.md +2 -0
- package/docs/workflows/recovery.md +1 -1
- package/docs/workflows/schedules.md +13 -4
- package/package.json +4 -4
- package/template/examples/workflows/research-brief.ts +29 -16
- package/template/package.json +1 -1
package/docs/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# lua-cli v3.32.
|
|
1
|
+
# lua-cli v3.32.4
|
|
2
2
|
|
|
3
|
-
Welcome to the comprehensive API documentation for lua-cli v3.32.
|
|
3
|
+
Welcome to the comprehensive API documentation for lua-cli v3.32.4. This guide covers every API, class, and function exported by the package.
|
|
4
4
|
|
|
5
5
|
## ๐ Documentation Index
|
|
6
6
|
|
package/docs/api/LuaWorkflow.md
CHANGED
|
@@ -61,7 +61,7 @@ Config: `name`, `description?`, `inputSchema`, `outputSchema?`, `budget?` (`maxC
|
|
|
61
61
|
| `.specialistStep(id, { role, prompt, โฆ })` | Ephemeral role on the owning agent (D25) |
|
|
62
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
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 (
|
|
64
|
+
| `.parallel([...arms], { merge? })` | Concurrent arms; a `[map, step]` pair is a legal arm โ the step runs with the map as its own `input` (the arm node carries `input`, like a `toolStep` with one). Not a `foreach` / loop body: those receive their item / the previous output (`mapping-placement` at `.commit()`) โ map the items before the container instead |
|
|
65
65
|
| `.switch([[predicate, armRef]...], otherwise?)` / `.branch(...)` | Conditional (exclusive / inclusive) |
|
|
66
66
|
| `.foreach(step, { concurrency?, maxItems?, chunk?, rateLimit? })` / `.foreach({ items })` | Fan-out over an upstream array |
|
|
67
67
|
| `.dowhile(ref, predicate, { maxIterations, intervalSeconds? })` / `.dountil(...)` | Loop |
|
|
@@ -50,6 +50,8 @@ Offline, `lua workflows run --approve <id>` completes the node with `decidedBy:
|
|
|
50
50
|
|
|
51
51
|
`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.
|
|
52
52
|
|
|
53
|
+
From the CLI: `lua workflows approval-payload <runId> --approval <wfa_โฆ>` prints the current payload with its `payloadFingerprint`, `editRevision` and the editable paths (a large payload answers the arrays to page โ `--path drafts [--limit n] [--cursor c]` reads one page); then `lua workflows approve <runId> --approval <wfa_โฆ> --edit @edited.json --fingerprint <payloadFingerprint>`. A stale fingerprint is `409 PAYLOAD_MISMATCH` โ refetch and retry.
|
|
54
|
+
|
|
53
55
|
## Where approvals surface
|
|
54
56
|
|
|
55
57
|
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.
|
|
@@ -19,7 +19,7 @@ A parked step waits for exactly one of:
|
|
|
19
19
|
|
|
20
20
|
`onError:'continue'` never parks: the step is `failed` on the ledger, the run goes on, and every successor sees the step's **continued-failure value** `{ __lua_workflow:'continued_failure', failed:true, error:{code,message}, text:'' }` under `stepResults.<id>` (the default input, `${stepResults.<id>.text}` โ `''`, `getStepResult(id)`, a `conditional` on `stepResults.<id>.failed`, and the parallel / foreach join entry). A `retry-step` on such a row while the run is still running re-arms it, but a successor that already consumed the value is not re-run.
|
|
21
21
|
|
|
22
|
-
`fail` (or cancelling the run) applies the step's failure as-is. From the CLI: `lua workflows status <runId>` shows the park, `lua workflows
|
|
22
|
+
`fail` (or cancelling the run) applies the step's failure as-is. From the CLI: `lua workflows status <runId>` shows the park and names the verbs; `lua workflows retry-step <runId> --step <id>` re-runs, `lua workflows resolve-step <runId> --step <id> --outcome skip|complete|fail [--output <json|@file>] [--note โฆ]` decides (R37 โ `complete` needs `--output`, validated against the step's `outputSchema`; a second decision on the same park is a 200 no-op that names who decided). The desktop inbox carries the same three, plus a repair run.
|
|
23
23
|
|
|
24
24
|
## Repair runs
|
|
25
25
|
|
|
@@ -2,25 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
_Source of truth: workflows-spec 07 ยง7.1. This page is the developer summary; the spec is normative._
|
|
4
4
|
|
|
5
|
-
`schedule: { type:'cron', expression: '0 9 * * 1', timezone: 'Europe/London' }` on `createWorkflow` fires runs on the cron grid; `scheduleInput` is the run input. `concurrencyPolicy:'forbid'` skips a fire while a run is in flight (the skip is recorded, never queued). Manage with `lua workflows activate` / `deactivate
|
|
5
|
+
`schedule: { type:'cron', expression: '0 9 * * 1', timezone: 'Europe/London' }` on `createWorkflow` fires runs on the cron grid; `scheduleInput` is the run input. `concurrencyPolicy:'forbid'` skips a fire while a run is in flight (the skip is recorded, never queued). Manage with `lua workflows activate` / `deactivate`, or create and drive a schedule from the CLI without a `schedule:` block โ `lua workflows schedules create|pause|resume|patch|delete` below; a re-enable with `--backfill-now` starts the missed occurrences (deduplicated on `backfill:<workflowId>:<occurrenceIso>`).
|
|
6
6
|
|
|
7
7
|
## Re-enabling a schedule
|
|
8
8
|
|
|
9
|
-
A paused or auto-disabled schedule never replays missed fires by itself; opt in with `schedule.backfillOnEnable: { maxOccurrences }` (or `lua workflows
|
|
9
|
+
A paused or auto-disabled schedule never replays missed fires by itself; opt in with `schedule.backfillOnEnable: { maxOccurrences }` (or `lua workflows schedules resume <jobId> --backfill-now` for one re-enable, `--backfill-on-enable <n>` to persist the opt-in) and the most recent misses start as one batch, deduplicated by the shared `backfill:<workflowId>:<occurrenceIso>` key, capped, and summarised in your inbox.
|
|
10
10
|
|
|
11
11
|
Offline, schedules do not fire (`backfillOnEnable` is not emulated) โ start runs with `lua test workflow` / `lua workflows start`.
|
|
12
12
|
|
|
13
13
|
## From the CLI
|
|
14
14
|
|
|
15
|
-
Schedules are `Job{kind:'workflow'}` rows on the agent; the CLI reads them through `GET /workflows/:agentId/schedules
|
|
15
|
+
Schedules are `Job{kind:'workflow'}` rows on the agent; the CLI reads them through `GET /workflows/:agentId/schedules`, creates one through R27, pauses / resumes it through R56 and removes it through R28.
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
lua workflows schedules list -i outreach # this workflow's schedule Jobs (omit -i for every workflow of the agent)
|
|
19
19
|
lua workflows schedules list --json # { success, data: { items: [{ jobId, workflowId, trigger, paused, nextRunAt, lastFiredAt?, consecutiveFailures, autoDisabled?, goalId? }] } }
|
|
20
|
+
lua workflows schedules create outreach --cadence '0 9 * * 1' --timezone Europe/London --input '{"segment":"trial"}'
|
|
21
|
+
# R27 โ the goals cadence grammar: --cadence (cron | JSON | @file, โค 5) [--timezone] or --every 30m;
|
|
22
|
+
# [--tag] [--notify emailApp|email|app|off] [-v <version>] [--budget-credits <n>] [--backfill-on-enable <n>].
|
|
23
|
+
# Create-or-REPLACE: a second create swaps the workflow's schedule.
|
|
24
|
+
lua workflows schedules pause <jobId> # R56 { paused:true }
|
|
25
|
+
lua workflows schedules resume <jobId> [--backfill-now] [--backfill-on-enable <n|none>] # R56 { paused:false, โฆ } โ the one-shot backfill rides a re-enable only
|
|
26
|
+
lua workflows schedules patch <jobId> --paused true|false [--backfill-on-enable <n|none>] [--backfill-now]
|
|
20
27
|
lua workflows schedules delete <jobId> [--yes] # R28 โ asks first unless --yes
|
|
21
28
|
lua workflows view outreach # the "Schedules:" line + table; --json carries the same rows under `schedules`
|
|
22
29
|
```
|
|
23
30
|
|
|
31
|
+
Every verb prints one `{ success, data | error }` envelope under `--json`; exit codes are the usual ones (`2` usage before any call, `3` an unknown workflow / `SCHEDULE_NOT_FOUND`, `1` a refusal โ `SCHEDULE_CAP`, a `VALIDATION_FAILED` whose issues are printed, `goal_schedule`).
|
|
32
|
+
|
|
24
33
|
Each row shows the trigger (`cron 0 9 * * 1 (Europe/London)` ยท `every 3600s` ยท `once <iso>`), the next fire, the last fire, its status (`active` ยท `paused` ยท `auto-disabled` after the PRO-726 strike limit) and the strike count.
|
|
25
34
|
|
|
26
|
-
**Goal-owned schedules.** A goal's cadence is a schedule Job too; it is tagged with the goal's id (`goalId` in `--json`, the `Goal` column in the table). While the goal is **active or paused** the CLI refuses to delete it โ `schedules delete` on such a row answers `goal_schedule` (exit 1) without calling the API, the same refusal the chat tool gives โ because unscheduling a live goal's job is recorded as a failure of the goal, never as stopping it. Stop the goal instead: `lua workflows goals pause <goalId>` (it can come back) or `lua workflows goals close <goalId>` (final; closing retires the cadence Job with it). The server refuses it too while the goal is live: R28 answers `409 GOAL_SCHEDULE {goalId}` for every caller (SDK, desktop, curl), and the CLI renders that with the same message. Once the goal has **ended** (`done` / `closed`, or its row is gone) a cadence Job that still lingers โ one left behind before LUA-760, or one whose retirement did not land โ is R28's to remove: `schedules delete <jobId>` goes through and retires it (the engine's sweep does the same on its own within one interval). A goal the CLI cannot read refuses (fail closed). See [goals.md](./goals.md).
|
|
35
|
+
**Goal-owned schedules.** A goal's cadence is a schedule Job too; it is tagged with the goal's id (`goalId` in `--json`, the `Goal` column in the table). `schedules pause|resume|patch` on such a row refuses (`goal_schedule`, exit 1, nothing called) whatever the goal's state โ a goal's Job follows its goal (`goals pause` / `goals resume`), never the other way round. While the goal is **active or paused** the CLI refuses to delete it โ `schedules delete` on such a row answers `goal_schedule` (exit 1) without calling the API, the same refusal the chat tool gives โ because unscheduling a live goal's job is recorded as a failure of the goal, never as stopping it. Stop the goal instead: `lua workflows goals pause <goalId>` (it can come back) or `lua workflows goals close <goalId>` (final; closing retires the cadence Job with it). The server refuses it too while the goal is live: R28 answers `409 GOAL_SCHEDULE {goalId}` for every caller (SDK, desktop, curl), and the CLI renders that with the same message. Once the goal has **ended** (`done` / `closed`, or its row is gone) a cadence Job that still lingers โ one left behind before LUA-760, or one whose retirement did not land โ is R28's to remove: `schedules delete <jobId>` goes through and retires it (the engine's sweep does the same on its own within one interval). A goal the CLI cannot read refuses (fail closed). See [goals.md](./goals.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lua-cli",
|
|
3
|
-
"version": "3.32.
|
|
3
|
+
"version": "3.32.4",
|
|
4
4
|
"description": "Build, test, and deploy AI agents with custom tools, webhooks, and scheduled jobs. Features LuaAgent unified configuration, streaming chat, and batch deployment.",
|
|
5
5
|
"readmeFilename": "README.md",
|
|
6
6
|
"main": "dist/api-exports.js",
|
|
@@ -113,11 +113,11 @@
|
|
|
113
113
|
"stripe": "^19.2.0",
|
|
114
114
|
"ts-node": "^10.9.2",
|
|
115
115
|
"tsup": "^8.5.1",
|
|
116
|
-
"@lua/
|
|
116
|
+
"@lua/sandbox-runtime": "0.0.1",
|
|
117
|
+
"@lua/shared-sandbox": "0.0.1",
|
|
117
118
|
"@lua/shared-types": "0.0.1",
|
|
118
119
|
"@lua/workflow-graph": "0.0.1",
|
|
119
|
-
"@lua/
|
|
120
|
-
"@lua/shared-sandbox": "0.0.1"
|
|
120
|
+
"@lua/shared-source-sync": "0.0.1"
|
|
121
121
|
},
|
|
122
122
|
"scripts": {
|
|
123
123
|
"clean": "rm -rf dist temp",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Sequential + parallel research, typed end to end.
|
|
2
|
-
// Verbatim from workflows-spec 03 ยง3.2 (a) (WF-215 / WF-223 โ the spec is normative)
|
|
2
|
+
// Verbatim from workflows-spec 03 ยง3.2 (a) (WF-215 / WF-223 โ the spec is normative), with the workflow EXPORTED
|
|
3
|
+
// like every other example โ a non-exported `const` cannot be registered on a LuaAgent without editing the file.
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { createStep, createWorkflow, stepOf, fromStep, template, gt, lit } from 'lua-cli';
|
|
5
6
|
|
|
@@ -19,24 +20,36 @@ const fetchSources = createStep({
|
|
|
19
20
|
|
|
20
21
|
const angle = z.object({ summary: z.string(), confidence: z.number() });
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
export const researchBrief = createWorkflow({
|
|
23
24
|
name: 'research-brief',
|
|
24
25
|
description: 'Fetch sources, summarise from two angles in parallel, merge โ or fall back when confidence is low.',
|
|
25
26
|
inputSchema: z.object({ topic: z.string() }),
|
|
26
27
|
outputSchema: z.object({ brief: z.string() }),
|
|
27
28
|
budget: { maxCredits: 40 },
|
|
28
29
|
})
|
|
29
|
-
.then(fetchSources)
|
|
30
|
-
.parallel(['techAngle', 'marketAngle'])
|
|
31
|
-
.agentStep('techAngle', {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
outputSchema: angle
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
.then(fetchSources) // entry 1: step
|
|
31
|
+
.parallel(['techAngle', 'marketAngle']) // entry 2: parallel โ 2 arms, both declared BELOW (ยง3.2.0: "declare here, inside me")
|
|
32
|
+
.agentStep('techAngle', {
|
|
33
|
+
agentId: 'analyst',
|
|
34
|
+
prompt: template('Summarise the technical angle of ${initData.topic} using ${stepResults.fetchSources.urls}'),
|
|
35
|
+
outputSchema: angle,
|
|
36
|
+
}) // no top-level entry โ placed by the parallel above
|
|
37
|
+
.agentStep('marketAngle', {
|
|
38
|
+
agentId: 'analyst',
|
|
39
|
+
prompt: template('Summarise the market angle of ${initData.topic} using ${stepResults.fetchSources.urls}'),
|
|
40
|
+
outputSchema: angle,
|
|
41
|
+
}) // same
|
|
42
|
+
.switch(
|
|
43
|
+
// entry 3: conditional{exclusive}
|
|
44
|
+
[[gt(stepOf<typeof angle>('techAngle').path('confidence'), lit(0.6)), 'merge']], // TypedRef<number> vs Literal<number> โ `lit('a')` would not compile
|
|
45
|
+
'lowConfidence'
|
|
46
|
+
)
|
|
47
|
+
.map({ brief: fromStep('techAngle', 'summary'), market: fromStep('marketAngle', 'summary') }, { id: 'merge' }) // placed INSIDE the switch arm (string ref above) โ one entry, not two
|
|
48
|
+
.agentStep('lowConfidence', {
|
|
49
|
+
agentId: 'analyst', // placed as the switch's `otherwise`
|
|
50
|
+
prompt: template(
|
|
51
|
+
'Confidence was low. Write a cautious brief on ${initData.topic} from ${stepResults.techAngle.summary} and ${stepResults.marketAngle.summary}'
|
|
52
|
+
),
|
|
53
|
+
outputSchema: z.object({ brief: z.string() }),
|
|
54
|
+
})
|
|
55
|
+
.commit(); // the conditional is the last entry: the taken arm's output IS the run output
|