lua-cli 3.39.4 → 3.40.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 +236 -23
- package/dist/api-exports.js +1562 -526
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +1410 -142
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +16 -5
- package/dist/workflow-builder.d.ts +52 -1
- package/dist/workflow-builder.js +1001 -267
- package/dist/workflow-builder.js.map +1 -1
- package/docs/API_REFERENCE.md +115 -45
- package/docs/api/Agents.md +111 -0
- package/docs/workflows/schedule-health.md +77 -0
- package/docs/workflows/schedules.md +6 -2
- package/package.json +3 -3
- package/template/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Agents
|
|
2
|
+
|
|
3
|
+
`Agents` lets your code work with other agents in your organization. It has two parts:
|
|
4
|
+
|
|
5
|
+
- `Agents.invoke` runs a turn on another agent.
|
|
6
|
+
- The supervision members read, and with permission release, another agent's versions, logs, runs and configuration.
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { Agents } from 'lua-cli';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Agents.invoke
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
const text = await Agents.invoke('base_agent-other', 'summarize the last order');
|
|
16
|
+
const result = await Agents.invoke('base_agent-other', { prompt: 'hi', threadId: 'order-1' });
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- **Same organization only.** The target must be in your agent's organization, or be a platform-shared agent. A cross-org call throws `AgentInvocationError` with code `cross_org_invoke_denied`.
|
|
20
|
+
- **`input.userId` rules.** The user must belong to your organization. Otherwise it throws `delegated_user_not_in_org`.
|
|
21
|
+
- **Only a few fields reach another agent.** A turn you create on a different agent keeps only `prompt`/`messages`, `threadId`, `runtimeContext`, `webhookPayload` and the reasoning `effort`.
|
|
22
|
+
- Everything else is dropped: `systemPrompt`, `model`, tool scoping, version preview and the rest.
|
|
23
|
+
- The target always runs with its own persona and model.
|
|
24
|
+
- **The target knows who called.** The target's tools see `Lua.request.invokedBy = { agentId, supervisor, scopes, delegatedUserId? }`.
|
|
25
|
+
- `supervisor` is true only when an org admin granted the caller `supervision:act` on the target.
|
|
26
|
+
- A well-behaved target refuses writes unless `supervisor` is true.
|
|
27
|
+
|
|
28
|
+
## Supervision
|
|
29
|
+
|
|
30
|
+
Supervision lets one agent observe, preview or release another agent in the same organization. Examples are an engineering-manager agent supervising a ticket agent.
|
|
31
|
+
|
|
32
|
+
**Nothing in code can grant it.** An org admin creates a grant for each supervisor and target pair, with one or more actions:
|
|
33
|
+
|
|
34
|
+
| Action | Allows |
|
|
35
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
36
|
+
| `observe` | `logs` (without message bodies), `runs`, `data` (only the collections named on the grant), `versions.list/get/diff`, `persona.versions`, `source`, `env.keys` |
|
|
37
|
+
| `observe-content` | Log message bodies in `logs` |
|
|
38
|
+
| `preview` | `versions.preview`: chat a staged version |
|
|
39
|
+
| `release` | `versions.promote` and `persona.activate`. These always need `expectActive`, and run only from a live workflow step of the supervisor whose workflow the grant names (`releaseWorkflowIds`). |
|
|
40
|
+
| `act` | The target sees `invokedBy.supervisor === true` when the supervisor invokes it |
|
|
41
|
+
|
|
42
|
+
**Discovery.** `Agents.list()` and `Agents.get(id)` return your own organization's agents, and only to an agent that holds at least one supervision grant there. Otherwise `list()` returns `[]`.
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
const agents = await Agents.list(); // [{ agentId, name, actions: ['observe', …] }]
|
|
46
|
+
|
|
47
|
+
const errors = await Agents.logs('base_agent-tp', { level: 'error', limit: 20 });
|
|
48
|
+
const runs = await Agents.runs('base_agent-tp').list({ status: 'failed' });
|
|
49
|
+
const state = await Agents.data('base_agent-tp').get('pilot_state', 'config'); // collection named on the grant
|
|
50
|
+
const keys = await Agents.env('base_agent-tp').keys(); // names only, never values
|
|
51
|
+
|
|
52
|
+
const versions = await Agents.versions('base_agent-tp').list();
|
|
53
|
+
const diff = await Agents.versions('base_agent-tp').diff(7, 8);
|
|
54
|
+
const preview = await Agents.versions('base_agent-tp').preview(8, 'What can you do?'); // no tools unless allowlisted
|
|
55
|
+
|
|
56
|
+
// Release (from a named workflow step): compare-and-swap, refused (409 PROMOTE_CONFLICT) unless 7 is live.
|
|
57
|
+
await Agents.versions('base_agent-tp').promote(8, { expectActive: 7 });
|
|
58
|
+
await Agents.persona('base_agent-tp').activate(3, { expectActive: 2 });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Platform switches
|
|
62
|
+
|
|
63
|
+
- **`preview` and `release` are off until enabled per environment.** Set `LUA_SUPERVISION_PREVIEW_ENABLED` and `LUA_SUPERVISION_RELEASE_ENABLED` on lua-core.
|
|
64
|
+
- While they are off, the call fails with `supervision_preview_disabled` / `supervision_release_disabled`.
|
|
65
|
+
- A version preview is accepted only when the supervision gateway started it.
|
|
66
|
+
- **Preview never carries supervisor rights.** A preview turn has `invokedBy.supervisor === false` and `scopes: []`, so the target's write gates refuse it.
|
|
67
|
+
- Its skills and connection tools are limited to `toolScope.skillIds`, or to none.
|
|
68
|
+
|
|
69
|
+
### What a supervisor can and cannot see
|
|
70
|
+
|
|
71
|
+
| Surface | What is returned |
|
|
72
|
+
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| 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`. |
|
|
74
|
+
| Runs | Metadata only: id, workflow, status, reason, trigger and times. No inputs or outputs. |
|
|
75
|
+
| Data | Read-only. Only the collections the org admin named on the grant (`dataCollections`). |
|
|
76
|
+
| Env | Variable names only. |
|
|
77
|
+
| 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*`. |
|
|
78
|
+
|
|
79
|
+
### Guarantees
|
|
80
|
+
|
|
81
|
+
- **Grants are admin-only.** An org admin (`members:assign-role`) creates them with `POST /admin/orgs/:orgId/supervision-grants`, including `dataCollections` and `releaseWorkflowIds`. They revoke them with `DELETE /admin/orgs/:orgId/supervision-grants/:grantId`, which also works when the target is archived.
|
|
82
|
+
- Every target is an explicit agent id. `*` is refused, and so is an agent supervising itself.
|
|
83
|
+
- The write actions (`release`, `act`) are non-transitive: a supervised agent cannot hold one, an agent that holds one cannot be supervised, and two agents cannot supervise each other.
|
|
84
|
+
- These checks and the write are atomic for each org.
|
|
85
|
+
- **Every call is audited on the target.** It writes a `supervisor_access` row to the target's logs (`lua logs --type supervision`). If the audit row cannot be written, the call fails.
|
|
86
|
+
- **Every call is rate-limited.** The defaults are 60 calls a minute per supervisor and 3 releases a day per supervisor and target.
|
|
87
|
+
- **Promotes record the actor.** A supervisor's promote records `promotedBy: service:agent:<supervisorId>` on the version.
|
|
88
|
+
|
|
89
|
+
### Offline (`lua test`, `lua dev`)
|
|
90
|
+
|
|
91
|
+
- **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.
|
|
92
|
+
- **Refused offline:** `versions.promote`, `persona.activate`, `versions.preview` and `data`. A local test never changes a live agent, and readable collections live on the grant.
|
|
93
|
+
- **Not available offline:** `Agents.list`, `Agents.get` and `source.get`.
|
|
94
|
+
|
|
95
|
+
### Errors
|
|
96
|
+
|
|
97
|
+
Every error is a `SupervisionError` with a `code`:
|
|
98
|
+
|
|
99
|
+
- `supervision_not_granted`
|
|
100
|
+
- `supervision_not_found`, for another org or an unknown agent
|
|
101
|
+
- `supervision_self_refused`
|
|
102
|
+
- `supervision_release_binding_required` and `supervision_release_binding_refused`
|
|
103
|
+
- `supervision_expect_active_required`
|
|
104
|
+
- `supervision_rate_limited`
|
|
105
|
+
- `supervision_release_limited`
|
|
106
|
+
- `supervision_data_not_granted`
|
|
107
|
+
- `supervision_audit_unavailable`
|
|
108
|
+
- `supervision_room_refused`
|
|
109
|
+
- `supervision_unavailable`
|
|
110
|
+
- `supervision_release_disabled` and `supervision_preview_disabled`, while the platform switch is off
|
|
111
|
+
- `PROMOTE_CONFLICT` and `PERSONA_ACTIVATE_CONFLICT`, for a stale `expectActive`
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Schedule health
|
|
2
|
+
|
|
3
|
+
_EM-WS10. Types, validator and rule: `@lua/shared-types` `schedule-health.types.ts`; the sweep: lua-core sweep #35 `schedule-health`._
|
|
4
|
+
|
|
5
|
+
Any scheduled workflow or job can say how it is supposed to behave, and the platform tells the agent's owner when it stops behaving that way. You do not need a heartbeat service, a cron monitor or an agent-specific alert.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
export const heartbeat = createWorkflow({
|
|
9
|
+
name: 'heartbeat',
|
|
10
|
+
schedule: { type: 'cron', expression: '*/10 * * * *', timezone: 'UTC' },
|
|
11
|
+
scheduleInput: {},
|
|
12
|
+
health: { maxSilenceMinutes: 30, alertAfterConsecutiveFailures: 3, notify: 'emailApp' },
|
|
13
|
+
})
|
|
14
|
+
.then(beat)
|
|
15
|
+
.commit();
|
|
16
|
+
|
|
17
|
+
export const nightlySync = new LuaJob({
|
|
18
|
+
name: 'nightly-sync',
|
|
19
|
+
description: 'Sync the catalogue',
|
|
20
|
+
schedule: { type: 'cron', expression: '0 2 * * *' },
|
|
21
|
+
health: { maxSilenceMinutes: 26 * 60, alertAfterConsecutiveFailures: 2 },
|
|
22
|
+
execute: async () => sync(),
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## What `health` declares
|
|
27
|
+
|
|
28
|
+
| Field | Range | Opens an incident when, while the schedule is active, |
|
|
29
|
+
| ------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
30
|
+
| `maxSilenceMinutes` | 5 to 43200 (30 days) | no scheduled run has **completed successfully** for this many minutes |
|
|
31
|
+
| `alertAfterConsecutiveFailures` | workflows 1 to 3; jobs 1 to 50 | this many scheduled runs in a row have failed |
|
|
32
|
+
| `notify` | `app`, `email` or `emailApp` (default) | not a condition: it routes the notice (push, email, or both) beyond the inbox card |
|
|
33
|
+
|
|
34
|
+
At least one of the two thresholds is required. A schedule the platform **turns off** after repeated failures (three consecutive failed runs of a workflow schedule or an agent task) is always an incident while it stays off. That is also why a workflow's failure threshold stops at 3: a fourth failure never happens.
|
|
35
|
+
|
|
36
|
+
Validation runs at compile (`lua compile`) and again at push. Declarations are refused, with the code shown, when:
|
|
37
|
+
|
|
38
|
+
- `health-empty`: neither threshold is given;
|
|
39
|
+
- `health-requires-schedule`: there is no `schedule`;
|
|
40
|
+
- `health-silence-on-once`: silence is declared on a one-time schedule;
|
|
41
|
+
- `health-silence-shorter-than-interval`: the window is not longer than an `interval` schedule's period;
|
|
42
|
+
- `health-failures-unreachable`: a workflow failure threshold is above 3;
|
|
43
|
+
- `health-silence-invalid`, `health-failures-invalid`, `health-notify-invalid` or `health-unknown-field`: a value is out of range or not a health field.
|
|
44
|
+
|
|
45
|
+
A cron cadence is not parsed. Leave headroom above the cron period plus the run time: `*/10` with a 5-minute run wants 30, not 15.
|
|
46
|
+
|
|
47
|
+
## What happens
|
|
48
|
+
|
|
49
|
+
The workflow engine's sweep runs every 5 minutes on the worker. It checks each schedule that declares `health` at most once a minute:
|
|
50
|
+
|
|
51
|
+
1. **An incident opens.** The owner receives one notice: the schedule's creator, or the org admins for a schedule nobody owns (a template schedule that runs as the system, for example). The notice is an inbox card titled with the schedule's name, plus push and/or email per `notify`. The card belongs to the schedule's health (`schedule-health:<agentId>:<jobId>`), separate from the schedule's own run and auto-disable cards.
|
|
52
|
+
2. **Later sweeps stay quiet.** Nobody is pinged again while the same incident is open. If the platform later turns the schedule off, the open incident is updated in place. If the notice could not be delivered, the next sweep sends it again, and it still counts as the same notice.
|
|
53
|
+
3. **The incident resolves** when the schedule is healthy again: a scheduled run completes inside the window and the failure streak is broken. The same card is closed ("Schedule healthy again") and a recovery notice goes out on the same channels. Pausing or turning off the schedule also closes it, with the copy "Schedule paused — health alert closed". Resuming, including after the platform turned the schedule off, gives it a full window before it can be called silent again.
|
|
54
|
+
|
|
55
|
+
Every open, change and resolve is also an **agent log line**: `lua logs --type workflow`, or `--type job` for a job. A `warn` marks an incident, an `info` marks its resolution. The structured fields are `health_event`, `health_condition`, `job_id`, `consecutive_failures`, `last_succeeded_at`, `silent_for_minutes`, `max_silence_minutes` and `alert_after_consecutive_failures`. Your org's [log drains](../../../../docs/log-drains/sources.md) route those lines like any other, so Better Stack, Datadog or a SIEM can alert on `health_event:opened` with no per-agent setup.
|
|
56
|
+
|
|
57
|
+
### What counts
|
|
58
|
+
|
|
59
|
+
- **Workflows.** Only runs fired by the schedule count. A manual `lua workflows start` is neither a success nor a failure for health. Failed, timed-out and abandoned runs are failures, and so are fires that could not start. A cancelled run counts as neither.
|
|
60
|
+
- **Jobs.** Any execution of the job counts, including `lua jobs trigger -i <job>`. The failure statuses are `failed`, `timeout`, `killed`, `abandoned` and `reaped`. Cancelled and in-flight executions are skipped.
|
|
61
|
+
- **When a declaration takes effect.** A workflow's declaration applies when its version is published (`lua push` plus activate). A job's applies when its version is published or promoted. Publishing a version without `health` removes the watch. If an incident is open at that point, its card is closed on the next sweep.
|
|
62
|
+
|
|
63
|
+
## Seeing it
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
lua workflows schedules list # Health column: — | ok | unwatched | ALERT <condition> since <time>
|
|
67
|
+
lua workflows schedules list --json # items[].health = { config, state, condition?, since?, lastSucceededAt?, checkedAt? }, items[].lastSucceededAt
|
|
68
|
+
lua jobs view -i <job> # a `Health:` line (ok | unwatched | ALERT <condition> since <time>)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`state` is `ok` when the schedule is watched and healthy. It is `alerting` while an incident is open. It is `unwatched` while the schedule is paused, or before the first check (up to about 5 minutes after publish).
|
|
72
|
+
|
|
73
|
+
## Limits
|
|
74
|
+
|
|
75
|
+
- Marketplace templates do not yet carry `health` (a template install's schedules are unwatched), and neither does the script form of workflows.
|
|
76
|
+
- The sweep is part of the workflow engine. With the engine's kill switch off (`LUA_WORKFLOWS_ENABLED`), nothing is checked.
|
|
77
|
+
- Code jobs have no failure counter of their own. The streak is read from the job's most recent executions (the threshold plus 10).
|
|
@@ -4,6 +4,10 @@ _Source of truth: workflows-spec 07 §7.1. This page is the developer summary; t
|
|
|
4
4
|
|
|
5
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
|
+
## Schedule health
|
|
8
|
+
|
|
9
|
+
Declare `health: { maxSilenceMinutes, alertAfterConsecutiveFailures, notify }` beside `schedule` and the platform tells the owner when the schedule goes silent, keeps failing or is turned off, and again when it recovers — one notice per incident, plus an agent log line your log drains route. See [schedule-health.md](./schedule-health.md).
|
|
10
|
+
|
|
7
11
|
## Re-enabling a schedule
|
|
8
12
|
|
|
9
13
|
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.
|
|
@@ -16,7 +20,7 @@ Schedules are `Job{kind:'workflow'}` rows on the agent; the CLI reads them throu
|
|
|
16
20
|
|
|
17
21
|
```bash
|
|
18
22
|
lua workflows schedules list -i outreach # this workflow's schedule Jobs (omit -i for every workflow of the agent)
|
|
19
|
-
lua workflows schedules list --json # { success, data: { items: [{ jobId, workflowId, trigger, paused, nextRunAt, lastFiredAt?, consecutiveFailures, autoDisabled?, goalId? }] } }
|
|
23
|
+
lua workflows schedules list --json # { success, data: { items: [{ jobId, workflowId, trigger, paused, nextRunAt, lastFiredAt?, lastSucceededAt?, consecutiveFailures, autoDisabled?, health?, goalId? }] } }
|
|
20
24
|
lua workflows schedules create outreach --cadence '0 9 * * 1' --timezone Europe/London --input '{"segment":"trial"}'
|
|
21
25
|
# R27 — the goals cadence grammar: --cadence (cron | JSON | @file, ≤ 5) [--timezone] or --every 30m;
|
|
22
26
|
# [--tag] [--notify emailApp|email|app|off] [-v <version>] [--budget-credits <n>] [--backfill-on-enable <n>].
|
|
@@ -30,6 +34,6 @@ lua workflows view outreach # the "Schedules:" line + tabl
|
|
|
30
34
|
|
|
31
35
|
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
36
|
|
|
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)
|
|
37
|
+
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), the strike count and its health (`—` when none is declared).
|
|
34
38
|
|
|
35
39
|
**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.
|
|
3
|
+
"version": "3.40.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",
|
|
@@ -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/shared-sandbox": "0.0.1",
|
|
117
116
|
"@lua/sandbox-runtime": "0.0.1",
|
|
118
117
|
"@lua/shared-source-sync": "0.0.1",
|
|
118
|
+
"@lua/shared-types": "0.0.1",
|
|
119
119
|
"@lua/workflow-graph": "0.0.1",
|
|
120
|
-
"@lua/shared-
|
|
120
|
+
"@lua/shared-sandbox": "0.0.1"
|
|
121
121
|
},
|
|
122
122
|
"scripts": {
|
|
123
123
|
"clean": "rm -rf dist temp",
|