lua-cli 3.44.0 → 3.45.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.
- package/dist/api-exports.d.ts +146 -51
- package/dist/api-exports.js +345 -31
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +735 -230
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +18 -18
- package/dist/workflow-builder.d.ts +5 -1
- package/dist/workflow-builder.js +238 -4
- package/dist/workflow-builder.js.map +1 -1
- package/docs/API_REFERENCE.md +1 -1
- package/docs/api/Agents.md +16 -9
- package/docs/api/Baskets.md +11 -1
- package/docs/api/Workflows.md +12 -4
- package/docs/workflows/approvals.md +2 -0
- package/package.json +2 -2
- package/template/package.json +1 -1
- package/template-web-app/web/src/lua/client.ts +12 -7
package/docs/API_REFERENCE.md
CHANGED
|
@@ -717,7 +717,7 @@ if (Lua.request.webhook) {
|
|
|
717
717
|
|
|
718
718
|
**Properties:**
|
|
719
719
|
|
|
720
|
-
- `Lua.request.channel` - Channel type (`'whatsapp'`, `'web'
|
|
720
|
+
- `Lua.request.channel` - Channel type (`'whatsapp'`, `'pop'` for the embeddable chat widget, `'phone'` for voice, `'web'` for the Lua web workspace (not the widget), `'api'`, and others; unrecognised channels arrive as plain strings)
|
|
721
721
|
- `Lua.request.webhook` - Webhook info (if running in webhook)
|
|
722
722
|
- `Lua.request.agentId` - The agent handling this turn
|
|
723
723
|
- `Lua.request.orgId` - The agent's organization, on deployed runtimes that know it (absent offline in `lua test`
|
package/docs/api/Agents.md
CHANGED
|
@@ -40,7 +40,7 @@ Supervision lets one agent observe, preview or release another agent in the same
|
|
|
40
40
|
|
|
41
41
|
| Action | Allows |
|
|
42
42
|
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
-
| `observe` | `logs` (without message bodies), `runs`, `data` (only the collections named on the grant), `versions.list/get/diff`, `persona.versions`, `source`, `env.keys`, `workflows.get`
|
|
43
|
+
| `observe` | `logs` (without message bodies), `runs`, `data` (only the collections named on the grant), `versions.list/get/diff`, `persona.versions`, `source`, `env.keys`, `workflows.get`, `deployed` |
|
|
44
44
|
| `observe-content` | Log message bodies in `logs` |
|
|
45
45
|
| `preview` | `versions.preview`: chat a staged version |
|
|
46
46
|
| `release` | `versions.promote`, `persona.activate`, `versions.delete` (a `superseded` version only), `workflows.create` (an inert, inactive workflow with no human author, so it cannot carry a schedule), `workflows.push` (a new, never-activated version of an existing workflow) and `workflows.deploy`. Promote, activate and deploy always need `expectActive`. Every release op runs only from a live workflow step of the supervisor whose workflow the grant names (`releaseWorkflowIds`). |
|
|
@@ -55,6 +55,10 @@ const errors = await Agents.logs('base_agent-tp', { level: 'error', limit: 20 })
|
|
|
55
55
|
const runs = await Agents.runs('base_agent-tp').list({ status: 'failed' });
|
|
56
56
|
const state = await Agents.data('base_agent-tp').get('pilot_state', 'config'); // collection named on the grant
|
|
57
57
|
const keys = await Agents.env('base_agent-tp').keys(); // names only, never values
|
|
58
|
+
// What it serves right now, even with no promoted agent version (activeAgentVersion: null): live skills and tools,
|
|
59
|
+
// the published persona and processors, each with the version id an agent version snapshot would pin, and the
|
|
60
|
+
// live model (live.model; compare with a version's snapshot.model, since a promote applies it).
|
|
61
|
+
const live = await Agents.deployed('base_agent-tp');
|
|
58
62
|
|
|
59
63
|
const versions = await Agents.versions('base_agent-tp').list();
|
|
60
64
|
const diff = await Agents.versions('base_agent-tp').diff(7, 8);
|
|
@@ -71,12 +75,14 @@ await Agents.workflows('base_agent-tp').push('wf_123', definition); // the body
|
|
|
71
75
|
// A workflow the target does not have yet: create it (inert), push, then deploy with expectActive: null.
|
|
72
76
|
// const { workflowId } = await Agents.workflows('base_agent-tp').create('nightly-digest');
|
|
73
77
|
await Agents.workflows('base_agent-tp').deploy('wf_123', '1.4.0', { expectActive: wf.activeVersionId });
|
|
78
|
+
// Optionally name the source commit the version was built from: deploy(wf, v, { expectActive, commitHash? })
|
|
74
79
|
|
|
75
80
|
// Clean up: only a superseded version can be deleted.
|
|
76
81
|
await Agents.versions('base_agent-tp').delete(5);
|
|
77
82
|
```
|
|
78
83
|
|
|
79
84
|
- A workflow deploy also records a new active agent version. Read `versions.list` again before a later `promote`.
|
|
85
|
+
- `commitHash` on `workflows.deploy` is optional and forwarded unchanged. A call without it behaves as before, and the platform may fill it from the Job workspace.
|
|
80
86
|
|
|
81
87
|
### Platform switches
|
|
82
88
|
|
|
@@ -88,13 +94,14 @@ await Agents.versions('base_agent-tp').delete(5);
|
|
|
88
94
|
|
|
89
95
|
### What a supervisor can and cannot see
|
|
90
96
|
|
|
91
|
-
| Surface
|
|
92
|
-
|
|
|
93
|
-
| Logs
|
|
94
|
-
| Runs
|
|
95
|
-
| Data
|
|
96
|
-
| Env
|
|
97
|
-
|
|
|
97
|
+
| Surface | What is returned |
|
|
98
|
+
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| Logs | Timestamp, level, log source and primitive or tool name. No user ids. The window is at most 7 days and at most 100 rows. The message (truncated to 2,000 characters) is included only with `observe-content`. |
|
|
100
|
+
| Runs | Metadata only: id, workflow, status, reason, trigger and times. No inputs or outputs. |
|
|
101
|
+
| Data | Read-only. Only the collections the org admin named on the grant (`dataCollections`). |
|
|
102
|
+
| Env | Variable names only. |
|
|
103
|
+
| Deployed | Live skills (tool names, bundle hashes, whether source is attached), the published persona (id, version, content hash) and processors, with version ids. No code, tool source or persona text. |
|
|
104
|
+
| Source | The files in the target's source backup, each up to 512 KB. Backup exclusions and credential-like paths are never listed or returned: dotfiles, `.env*`, `*.pem`, `*.key`, `id_rsa*`, `.npmrc`, `credentials*`, `*secret*`. |
|
|
98
105
|
|
|
99
106
|
### Guarantees
|
|
100
107
|
|
|
@@ -109,7 +116,7 @@ await Agents.versions('base_agent-tp').delete(5);
|
|
|
109
116
|
|
|
110
117
|
- **Read members** call the developer API with **your** credential and mirror the projections above: env keys only, and logs without user ids or message bodies.
|
|
111
118
|
- **Refused offline:** `versions.promote`, `persona.activate`, `versions.delete`, `workflows.create`, `workflows.push`, `workflows.deploy`, `versions.preview` and `data`. A local test never changes a live agent, and readable collections live on the grant.
|
|
112
|
-
- **Not available offline:** `Agents.list`, `Agents.get` and `
|
|
119
|
+
- **Not available offline:** `Agents.list`, `Agents.get`, `source.get` and `Agents.deployed` (`supervision_offline_unsupported`).
|
|
113
120
|
|
|
114
121
|
### Errors
|
|
115
122
|
|
package/docs/api/Baskets.md
CHANGED
|
@@ -32,10 +32,13 @@ The Baskets API enables:
|
|
|
32
32
|
enum BasketStatus {
|
|
33
33
|
ACTIVE = 'active',
|
|
34
34
|
CHECKED_OUT = 'checked_out',
|
|
35
|
-
ABANDONED = 'abandoned'
|
|
35
|
+
ABANDONED = 'abandoned',
|
|
36
|
+
EXPIRED = 'expired'
|
|
36
37
|
}
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
`Baskets.get(status)` and `basket.updateStatus(status)` reject any other value (including a different letter case) with HTTP 400 `Invalid basket status '<value>'. Allowed: active, checked_out, abandoned, expired`, before anything is read or persisted. A missing basket is HTTP 404 and a failure inside the commerce service is a generic HTTP 500 (503 when it is unreachable). The SDK surfaces each as a thrown `Error`; there is no `success: false` body to branch on.
|
|
41
|
+
|
|
39
42
|
## BasketInstance Methods
|
|
40
43
|
|
|
41
44
|
Once you have a basket instance, you can call these methods:
|
|
@@ -112,6 +115,13 @@ Updates the basket's status.
|
|
|
112
115
|
|
|
113
116
|
**Returns:** `Promise<Basket>` - Updated basket data
|
|
114
117
|
|
|
118
|
+
**Errors:**
|
|
119
|
+
- An unknown status is rejected with HTTP 400 `Invalid basket status '<value>'. Allowed: active, checked_out, abandoned, expired`; nothing is persisted.
|
|
120
|
+
- A missing basket is HTTP 404.
|
|
121
|
+
- A failure inside the commerce service is a generic HTTP 500 (503 when the commerce service itself is down or unreachable).
|
|
122
|
+
|
|
123
|
+
The SDK surfaces each of these as a thrown `Error`; there is no `success: false` body to branch on.
|
|
124
|
+
|
|
115
125
|
**Example:**
|
|
116
126
|
```typescript
|
|
117
127
|
await basket.updateStatus(BasketStatus.ABANDONED);
|
package/docs/api/Workflows.md
CHANGED
|
@@ -76,7 +76,7 @@ Returns the run summary (status, counts, gate, lineage, `output` when readable).
|
|
|
76
76
|
|
|
77
77
|
### `Workflows.list(opts?)`
|
|
78
78
|
|
|
79
|
-
`opts`: `{ limit?, status?, workflow?, correlationKey?, tags?, sort? }` — `tags` are AND-ed; `sort` is one of `-createdAt | createdAt | -durationMs | durationMs`.
|
|
79
|
+
`opts`: `{ limit?, status?, workflow?, correlationKey?, tags?, sort? }` — `tags` are AND-ed; `sort` is one of `-createdAt | createdAt | -durationMs | durationMs`. `workflow` is a name or an id; an id (UUID, `wf_…`, `wfc_…`) is queried as-is. Under `lua test` a name that matches no workflow definition returns `[]`, and a failed definition lookup throws a typed `WorkflowApiError`; deployed, the bridge matches runs by recorded name or id. A `workflow` filter never widens to every run in either runtime.
|
|
80
80
|
|
|
81
81
|
### `Workflows.cancel(runId, opts?)`
|
|
82
82
|
|
|
@@ -90,6 +90,12 @@ Resumes a step suspended with `ctx.suspend(...)`. The loser of a resume race rec
|
|
|
90
90
|
|
|
91
91
|
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`.
|
|
92
92
|
|
|
93
|
+
### `Workflows.raiseBudget(runId, patch)`
|
|
94
|
+
|
|
95
|
+
Raises the caps of one of the calling agent's runs. `patch` is `{ maxCredits?, maxSteps?, maxJobSeconds?, maxDurationSeconds?, note? }` — at least one cap, increases only, each at or under the org cap. Returns `{ raised: true, budget, runStatus, resumed }`, or the idempotent `{ raised: false, reason: 'already_raised' }` when the same raise was already applied.
|
|
96
|
+
|
|
97
|
+
Only a run parked on its budget gate, or still queued/running, is raisable; any other state (including a terminal run, or a run past its per-run raise limit) throws `BUDGET_NOT_RAISABLE` (409). Only the run's creator/holder or an org admin may raise it (`NOT_RUN_CREATOR`, 403 — so a call from a job or workflow step with no user fails closed). A run that is not the calling agent's throws `RUN_NOT_FOUND` (404). An over-cap value throws `CAP_EXCEEDED` (400, with `cap`, `value`, `ceiling`); a malformed patch throws `VALIDATION_FAILED` (400). Behaviour is the same under `lua test` and deployed.
|
|
98
|
+
|
|
93
99
|
#### Reserved payload keys
|
|
94
100
|
|
|
95
101
|
The run ledger redacts by **key name**, not by value: a payload member whose key is one of
|
|
@@ -100,14 +106,16 @@ is persisted as `[REDACTED]` whatever it holds. A key matches when one of those
|
|
|
100
106
|
|
|
101
107
|
This is by design (a credential must never land in a run), so the placeholder is what `stepResults.<id>.payload`, the run output, `lua workflows status --steps`, exports and the chat run card show — a workflow cannot read a data field with one of these names back. Name data fields otherwise (`ticketRef`, `handle`, `code`, `verifier`). The same rule applies to every step output and suspend payload. A `waitForSignal` whose `schema` declares a property with a reserved name draws the non-blocking `signal-reserved-key` warning at push / compose (the property, the placeholder it would read as, the rename), and `lua workflows signal` warns before sending a payload that carries a reserved key (the signal is still delivered).
|
|
102
108
|
|
|
103
|
-
###
|
|
109
|
+
### Other members
|
|
104
110
|
|
|
105
|
-
`startBatch`, `signalByKey`, `
|
|
111
|
+
`startBatch`, `signalByKey`, `setGoal` and `goals.{list,get,pause,resume,close}` are part of the API surface (the member list is frozen). For `startBatch`: from an agent turn, prefer the `startWorkflowRunBatch` tool (one consent card for the whole batch, D23-r5).
|
|
106
112
|
|
|
107
113
|
## Errors
|
|
108
114
|
|
|
109
115
|
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`).
|
|
110
116
|
|
|
117
|
+
- `WORKFLOW_INACTIVE` (409): the workflow was deactivated (`lua workflows deactivate`), so `Workflows.start` by name or by id starts no new run. Runs already going keep going; activate the workflow to start again.
|
|
118
|
+
|
|
111
119
|
```typescript
|
|
112
120
|
try {
|
|
113
121
|
await Workflows.start('nightly-sync', {});
|
|
@@ -130,7 +138,7 @@ lua test workflow outreach --input @leads.json \
|
|
|
130
138
|
|
|
131
139
|
- `--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`.
|
|
132
140
|
- `--approve <id>[=@payload]` / `--deny <id>[=@reason]` pre-answer approvals; `--signal <name>=<json>` pre-supplies `waitForSignal` payloads in order.
|
|
133
|
-
- `--fixtures <dir>` replays recorded
|
|
141
|
+
- `--fixtures <dir>` replays recorded fixtures (`--record <dir>` writes each live agent reply — requires `--agents live`; tool steps never run offline, so nothing else is recorded); a missing fixture exits **5** — never a silent fake.
|
|
134
142
|
- `--from-run <runId>` seeds every completed step from a real run and restarts at the first non-completed one.
|
|
135
143
|
- `--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`.
|
|
136
144
|
- `--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.
|
|
@@ -46,6 +46,8 @@ Offline, `lua workflows run --approve <id>` completes the node with `decidedBy:
|
|
|
46
46
|
|
|
47
47
|
`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}`.
|
|
48
48
|
|
|
49
|
+
A numeric `timeoutHours` (on `approval` and `waitForSignal`) must be in 1..720: outside that range `lua compile` and `lua push` refuse it as `timeout-hours-out-of-range`. A `waitForSignal` signal name must match `[a-zA-Z0-9_.-]{1,64}` or it is refused as `signal-name-invalid`. The run's own wall (7 days by default) still caps the live deadline, so a window longer than the run wall is clamped at runtime (the `deadline-clamped` warning).
|
|
50
|
+
|
|
49
51
|
## Reminders
|
|
50
52
|
|
|
51
53
|
`remindAfterHours: 24` re-sends the approval push to the approvers who still have to decide, every 24 hours while the approval is pending: at most **3 reminders** per ask (an escalation hop is a new ask, so its count starts again), and never at or after the deadline. The reminder replaces the earlier notification in place (same collapse key) and reads "Reminder: …". It takes a plain number of hours in `1..720` (no binding); anything else is refused as `remind-after-invalid`, and a value at or above the first deadline pushes with the `remind-after-exceeds-timeout` warning, because it can never fire. The first deadline is the first escalation hop's `timeoutHours` when `onTimeout` starts with a hop, otherwise `timeoutHours` (168 hours when you leave it out). Reminders ride the approval **push**, so they reach the approvers an agent's approval routing names (shared-agent approvers); an approval with no routing gets the inbox row and the channel fallback but no push, and so no reminder. They are sent on the platform's sweep cadence, so a reminder can arrive a few minutes after it is due, and quiet hours and do-not-disturb drop it like any other push.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lua-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.45.0",
|
|
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",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"dotenv": "^17.2.3",
|
|
88
88
|
"esbuild": "^0.28.1",
|
|
89
89
|
"inquirer": "^12.9.6",
|
|
90
|
-
"js-yaml": "^4.
|
|
90
|
+
"js-yaml": "^4.3.2",
|
|
91
91
|
"open": "^10.1.0",
|
|
92
92
|
"posthog-node": "^5.26.0",
|
|
93
93
|
"ts-evaluator": "^2.0.0",
|
package/template/package.json
CHANGED
|
@@ -244,18 +244,23 @@ class LuaAppClient {
|
|
|
244
244
|
/**
|
|
245
245
|
* One post per allowed shell origin: the browser delivers it only when the
|
|
246
246
|
* framing window really has that origin, so no other page ever receives it.
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
247
|
+
*
|
|
248
|
+
* A shell on a custom scheme (lua-desktop's `tauri://localhost`) is not a
|
|
249
|
+
* valid postMessage target origin: WebKit throws "Invalid target origin",
|
|
250
|
+
* which aborted the loop and the session ("Could not load" in lua-desktop),
|
|
251
|
+
* and skipping it would leave the desktop shell without `lua:ready`.
|
|
252
|
+
* Such an origin is posted with '*': the messages carry only {type, v}, and
|
|
253
|
+
* the CSP's frame-ancestors already limits who can frame the app. Each post
|
|
254
|
+
* is isolated so one origin can never stop the others.
|
|
251
255
|
*/
|
|
252
256
|
private post(message: { type: string; v: number }): void {
|
|
253
257
|
if (window.parent === window) return;
|
|
254
|
-
|
|
258
|
+
const targets = new Set(this.hostOrigins().map((origin) => (/^https?:\/\//i.test(origin) ? origin : '*')));
|
|
259
|
+
for (const target of targets) {
|
|
255
260
|
try {
|
|
256
|
-
window.parent.postMessage(message,
|
|
261
|
+
window.parent.postMessage(message, target);
|
|
257
262
|
} catch {
|
|
258
|
-
//
|
|
263
|
+
// An origin this browser will not target: skip it, keep the rest.
|
|
259
264
|
}
|
|
260
265
|
}
|
|
261
266
|
}
|