lua-cli 3.42.0-beta.1 → 3.42.0-beta.2

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.
@@ -396,6 +396,8 @@ Custom data storage with vector search.
396
396
  - `Data.getEntry(collection, id)` - Get single entry
397
397
  - `Data.search(collection, query)` - Vector search
398
398
  - `Data.create(collection, data)` - Create entry
399
+ - `Data.patch(collection, id, { set?, unset? }, { ifVersion? })` - Atomic field set/unset; `ifVersion` = compare-and-swap (409 `data_version_conflict` on a stale version)
400
+ - `Data.increment(collection, id, field, by?, { upsert? })` - Atomic numeric increment, resolves the new value
399
401
  - `entry.update(data)` - Update entry
400
402
  - `entry.delete()` - Delete entry
401
403
 
@@ -717,11 +719,15 @@ if (Lua.request.webhook) {
717
719
 
718
720
  - `Lua.request.channel` - Channel type (`'whatsapp'`, `'web'`, `'api'`)
719
721
  - `Lua.request.webhook` - Webhook info (if running in webhook)
722
+ - `Lua.request.agentId` - The agent handling this turn
723
+ - `Lua.request.orgId` - The agent's organization, on deployed runtimes that know it (absent offline in `lua test`
724
+ and on older runtimes; read it defensively)
720
725
  - `Lua.request.invokedBy` - Present only when **another agent** created this turn through `Agents.invoke`:
721
726
  `{ agentId, supervisor, scopes, delegatedUserId? }`. Verified by the platform, never caller-supplied.
722
727
  - `supervisor` is true only when an org admin granted the caller `supervision:act` on this agent. Observe, preview and release grants do not set it.
723
728
  - `scopes` lists the caller's supervision scopes on this agent. It is `[]` on version-preview turns.
724
- - `delegatedUserId` is set when the caller passed `input.userId`.
729
+ - `delegatedUserId` is set when the caller passed `input.userId`. It is also set when the caller's own turn
730
+ had such a user, or ran in a Job or workflow run: in those cases the platform did not pick the user.
725
731
  - The field is absent on user, channel, job, webhook and self-invoked turns, and offline in `lua test`.
726
732
 
727
733
  **Gating writes on agent invocation.** Treat a turn with `invokedBy` as agent-initiated. A mutating tool should
package/docs/api/AI.md CHANGED
@@ -98,6 +98,8 @@ The full-options response mirrors AI SDK `GenerateTextResult` (serializable fiel
98
98
  | `toolCalls?` | `AiGenerateToolCall[]` | Tool calls made during generation |
99
99
  | `toolResults?` | `AiGenerateToolResult[]` | Tool results from generation |
100
100
  | `warnings?` | `CallWarning[]` | Provider warnings (e.g. unsupported settings) |
101
+ | `model?` | `string` | The model that actually produced `text` (`provider/model`) |
102
+ | `modelFallback?` | `{ requestedModel, servedModel, reason }` | Present only when `model` differs from the model the call resolved to. `reason`: `'not-approved'` (the model is not in the catalogue, so the platform default ran; it takes precedence when both apply) or `'provider-unavailable'` (a fallback model served during a provider outage) |
101
103
 
102
104
  ## Supported Providers
103
105
 
@@ -108,7 +110,7 @@ The full-options response mirrors AI SDK `GenerateTextResult` (serializable fiel
108
110
  | OpenAI | `openai/` | `openai/gpt-5.4` |
109
111
  | Anthropic | `anthropic/` | `anthropic/claude-sonnet-5` |
110
112
 
111
- Any other code from `lua models list` works too. If the requested model is not available, the request falls back to the platform default (`alibaba/qwen3.8-flash`).
113
+ Any other code from `lua models list` works too. A model that is not available (unknown, misspelled or retired) is never swapped silently: the platform default (`alibaba/qwen3.8-flash`) answers and the response says so in `modelFallback`. A platform switch can turn a model you name into a refusal instead: the call then throws a 400 with `code: 'MODEL_NOT_AVAILABLE'`, a message naming the model, and `availableModels`. Check `model` / `modelFallback`, or catch that code.
112
114
 
113
115
  Google models automatically get Google Search grounding — search result URLs appear in the `sources` field of the full-options response.
114
116
 
@@ -31,13 +31,13 @@ Supervision lets one agent observe, preview or release another agent in the same
31
31
 
32
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
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 |
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`, `workflows.get` |
37
+ | `observe-content` | Log message bodies in `logs` |
38
+ | `preview` | `versions.preview`: chat a staged version |
39
+ | `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`). |
40
+ | `act` | The target sees `invokedBy.supervisor === true` when the supervisor invokes it |
41
41
 
42
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
43
 
@@ -56,8 +56,21 @@ const preview = await Agents.versions('base_agent-tp').preview(8, 'What can you
56
56
  // Release (from a named workflow step): compare-and-swap, refused (409 PROMOTE_CONFLICT) unless 7 is live.
57
57
  await Agents.versions('base_agent-tp').promote(8, { expectActive: 7 });
58
58
  await Agents.persona('base_agent-tp').activate(3, { expectActive: 2 });
59
+
60
+ // Workflows: push an inactive version, then deploy it. The deploy is compare-and-swap on the workflow's
61
+ // live version ID (409 WORKFLOW_DEPLOY_CONFLICT on a mismatch; the message names the live one).
62
+ const wf = await Agents.workflows('base_agent-tp').get('wf_123'); // { activeVersionId, versions: [...] }
63
+ await Agents.workflows('base_agent-tp').push('wf_123', definition); // the body `lua push workflow` sends
64
+ // A workflow the target does not have yet: create it (inert), push, then deploy with expectActive: null.
65
+ // const { workflowId } = await Agents.workflows('base_agent-tp').create('nightly-digest');
66
+ await Agents.workflows('base_agent-tp').deploy('wf_123', '1.4.0', { expectActive: wf.activeVersionId });
67
+
68
+ // Clean up: only a superseded version can be deleted.
69
+ await Agents.versions('base_agent-tp').delete(5);
59
70
  ```
60
71
 
72
+ - A workflow deploy also records a new active agent version. Read `versions.list` again before a later `promote`.
73
+
61
74
  ### Platform switches
62
75
 
63
76
  - **`preview` and `release` are off until enabled per environment.** Set `LUA_SUPERVISION_PREVIEW_ENABLED` and `LUA_SUPERVISION_RELEASE_ENABLED` on lua-core.
@@ -83,13 +96,12 @@ await Agents.persona('base_agent-tp').activate(3, { expectActive: 2 });
83
96
  - 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
97
  - These checks and the write are atomic for each org.
85
98
  - **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.
99
+ - **Releases record the actor.** A supervisor's promote records `promotedBy: service:agent:<supervisorId>` on the version, and a workflow deploy records it as the creator of the agent version it mints. A version delete is logged with the actor.
88
100
 
89
101
  ### Offline (`lua test`, `lua dev`)
90
102
 
91
103
  - **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.
104
+ - **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.
93
105
  - **Not available offline:** `Agents.list`, `Agents.get` and `source.get`.
94
106
 
95
107
  ### Errors
@@ -101,11 +113,184 @@ Every error is a `SupervisionError` with a `code`:
101
113
  - `supervision_self_refused`
102
114
  - `supervision_release_binding_required` and `supervision_release_binding_refused`
103
115
  - `supervision_expect_active_required`
104
- - `supervision_rate_limited`
105
- - `supervision_release_limited`
116
+ - `supervision_version_not_superseded`, when `versions.delete` targets a version that is not superseded
117
+ - `WORKFLOW_DEPLOY_CONFLICT` and `PROMOTE_CONFLICT`, when `expectActive` is not the live version
106
118
  - `supervision_data_not_granted`
107
119
  - `supervision_audit_unavailable`
108
120
  - `supervision_room_refused`
109
121
  - `supervision_unavailable`
110
122
  - `supervision_release_disabled` and `supervision_preview_disabled`, while the platform switch is off
111
123
  - `PROMOTE_CONFLICT` and `PERSONA_ACTIVATE_CONFLICT`, for a stale `expectActive`
124
+
125
+ ## Agent directory and access requests
126
+
127
+ These need no grant. Each platform switch is off until it is enabled per environment (lua-core and lua-api):
128
+ `LUA_AGENT_DIRECTORY_ENABLED` and `LUA_SUPERVISION_ACCESS_REQUESTS_ENABLED`. While a switch is off, the call fails
129
+ with `agent_directory_disabled` / `supervision_access_requests_disabled`.
130
+
131
+ **`Agents.list()` versus `Agents.directory()`.** Once your agent holds any supervision grant in the org, `list()`
132
+ returns **every** live org agent's id and name, private ones included. `directory()` shows only what the
133
+ **user of the current turn** may see: a private agent only to its owner or an agent-grant holder.
134
+
135
+ ```typescript
136
+ // Agents the turn's user can see: owner, privacy, build kind, last activity, 30-day volume, your actions.
137
+ const entries = await Agents.directory();
138
+ // [{ agentId, name, ownerUserId, isPrivate, buildKind: 'repo' | 'backup-source' | 'dashboard',
139
+ // lastActivityAt, volume30d, callerActions: ['observe'] }]
140
+
141
+ // Ask for access. The owner of a private agent (or the org admins / agent builders of a shared one)
142
+ // gets an inbox card; approving creates the grant, expiring at `expiresAt` when given.
143
+ const req = await Agents.requestAccess('base_agent-tp', {
144
+ actions: ['observe', 'preview'],
145
+ reason: 'Triage the failing nightly runs',
146
+ expiresAt: '2026-12-31T00:00:00Z',
147
+ });
148
+ const mine = await Agents.accessRequests(); // newest first: pending | approved | denied | cancelled | expired | revoked
149
+ await Agents.cancelAccessRequest(req.requestId); // only your own pending request
150
+ ```
151
+
152
+ - `directory()` needs a turn the platform ran for a member of your org. These fail with
153
+ `agent_directory_requires_user`:
154
+ - a turn with no such user, for example a channel turn;
155
+ - a turn whose user was chosen by the agent that invoked it (`Agents.invoke(…, { userId })`);
156
+ - code in a Job or a workflow run (step, script or job), because the run's user comes from the turn that
157
+ started it and the platform does not keep where that user came from;
158
+ - any turn invoked onward from one of these. `volume30d` counts chat exchanges (the nightly rollup, up to yesterday) and
159
+ workflow runs; job and trigger executions are not counted.
160
+ - One pending request per agent (`supervision_access_request_pending`). A pending request lapses after 7 days.
161
+ - When nobody can approve (the owner of a private agent has left the org, or a shared agent has no org admin or
162
+ agent builder), the request is refused with `supervision_access_no_approver` and nothing is filed.
163
+ At most 3 new requests per agent and 20 in all per 24 hours, cancelled ones included
164
+ (`supervision_access_request_rate_limited`).
165
+ - An approval never overwrites a grant you already hold (`supervision_access_grant_exists`), and the
166
+ non-transitive rules above still apply. The grant records the approver as its creator. Without the two switches
167
+ below it carries no `dataCollections`, `releaseWorkflowIds` or `unattendedWorkflowIds` (an org admin can add them).
168
+ - An expired grant is suspended within about a minute of its `expiresAt`, even while the access-request switch
169
+ is off. A request whose `expiresAt` has passed
170
+ before anyone approves it cannot be approved; it expires instead.
171
+ - Owners see who supervises their agent with `GET /developer/agents/:agentId/supervisors`, and the owner or an
172
+ org admin revokes one with `DELETE /developer/agents/:agentId/supervisors/:grantId`. Both routes first need
173
+ `agents:read` on the agent: on a private agent that is its creator, an agent-grant holder or the org owner, so
174
+ an org admin who is none of these is refused. The approver decides with
175
+ `POST /developer/supervision-access-requests/:requestId/decide` (`{ decision: 'approve' | 'deny' }`). Revoke and
176
+ decide need a signed-in person who is an active member of the org; a typed API key is refused.
177
+ - Not available offline (`supervision_offline_unsupported`).
178
+
179
+ ### Asking for "ship" in chat, and upgrading a grant
180
+
181
+ Two more switches, each off until enabled on lua-api: `LUA_SUPERVISION_ACCESS_BINDINGS_ENABLED` and
182
+ `LUA_SUPERVISION_ACCESS_UPGRADES_ENABLED`.
183
+
184
+ ```typescript
185
+ // Ask for everything a "watch, suggest and ship" supervisor needs, in one request.
186
+ await Agents.requestAccess('base_agent-tp', {
187
+ actions: ['observe', 'observe-content', 'preview', 'act', 'release'],
188
+ reason: 'Stage and release the fixes you approve',
189
+ releaseWorkflowIds: [svReleaseId, svRollbackId], // YOUR workflows whose code steps may release
190
+ unattendedWorkflowIds: [svImproveId], // YOUR workflow whose Job-tier steps may stage code unattended
191
+ dataCollections: ['leads'], // the target's collections you may read
192
+ notifyOwner: true, // ask the owner to let you message them (see Agents.notifyOwner)
193
+ });
194
+ ```
195
+
196
+ - **Bindings** (`LUA_SUPERVISION_ACCESS_BINDINGS_ENABLED`; otherwise `supervision_access_bindings_disabled`):
197
+ - Workflow ids must be live workflows of the **requesting** agent (`supervision_access_workflow_not_owned`).
198
+ Release still happens only inside a code step of one of those workflows.
199
+ - The grant's own rules apply to the result: `releaseWorkflowIds` needs `release`; `unattendedWorkflowIds` needs
200
+ `act` or `release`, plus `observe` and `observe-content`; `dataCollections` needs `observe`. At most 10 ids per
201
+ workflow list and 20 collections; `*` is refused.
202
+ - An unattended Job step's staging key is owned by the person who approved, and can never do more than they can.
203
+ - **Upgrades** (`LUA_SUPERVISION_ACCESS_UPGRADES_ENABLED`; otherwise approval refuses
204
+ `supervision_access_grant_exists`, as before). When you already hold an admin (`pap`) or access-request grant on
205
+ the agent, a new request **adds** to it:
206
+ - The request records the grant as it is now (`upgradeOf`), and the approver's card lists exactly what approval
207
+ adds (`adds`), plus what you already have. A request that adds nothing is refused
208
+ (`supervision_access_nothing_to_add`).
209
+ - Approval merges: nothing is ever removed, and the grant keeps its source and creator.
210
+ - If your access changed after you asked (revoked, narrowed, widened), approval is refused
211
+ (`supervision_access_grant_changed`) and the request stays pending: the approver denies it and you ask again.
212
+ - An admin grant never expires, so an upgrade of one cannot set `expiresAt`. An access-request grant takes the new
213
+ expiry when you give one.
214
+ - Only the grant's creator can approve adding `unattendedWorkflowIds` to it (they own the staging key).
215
+ - Grants created by agent provisioning are never upgraded.
216
+
217
+ ## Evals on a supervised agent
218
+
219
+ `Agents.evals(agentId)` runs the target's eval suites (the ones its owner pushed with `lua evals push`) against a
220
+ staged version. Off until the platform enables evals (`LUA_AGENT_EVALS_ENABLED` on lua-core, lua-api and lua-agents);
221
+ `run` also needs `LUA_SUPERVISION_PREVIEW_ENABLED`.
222
+
223
+ ```typescript
224
+ const ev = Agents.evals('base_agent-tp');
225
+ const suites = await ev.suites(); // [{ key, revision, caseCount, minPassRate }]
226
+ const { runId } = await ev.run({ suite: 'release-gate', version: 9 }); // a staged version, or 'active'
227
+ const run = await ev.getRun(runId); // poll until status is completed / failed / cancelled / budget_exhausted
228
+ if (run.status === 'completed' && run.passed === false) {
229
+ const t = await ev.transcript(runId, run.cases.find((c) => c.status === 'failed')!.caseId); // observe-content
230
+ }
231
+ await ev.cancel(runId); // only runs you started
232
+ ```
233
+
234
+ | Member | Needs | Notes |
235
+ | ------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
236
+ | `suites()`, `suite(key)` | `observe` | The suite's cases; its author shows as `user` / `apiKey` / `service:agent` |
237
+ | `run({ suite, version, maxCredits?, caseIds? })` | `preview` + the preview switch | Runs are attributed to your agent. Bounded by the suite budget, the agent's monthly eval allowance and 10 supervised runs per agent per day (`LUA_SUPERVISION_EVAL_RUNS_PER_DAY`, `supervision_eval_runs_rate_limited`; `supervision_eval_cap_unavailable` when the counter is down) |
238
+ | `runs(opts?)`, `getRun(runId)` | `observe` | Per-check `detail` and the judge's `reasoning` only with `observe-content`; another principal's runs show a coarse `initiatedBy` |
239
+ | `transcript(runId, caseId)` | `observe` + `observe-content` | `supervision_content_not_granted` without it; a holdout case is not found |
240
+ | `cancel(runId)` | `preview` | Only runs you started. Works even with evals or preview switched off (stopping work is always safe) |
241
+
242
+ - Supervisors never write suites: a suite decides which live tools an eval turn may use, which is the owner's call.
243
+ - Eval turns run with built-in tools isolated, and live tools only where the owner opted them in on the suite.
244
+ - Offline (`lua test`): reads use your developer credential; `run` and `cancel` are refused.
245
+ - A Job-tier plugin step can use `lua evals` (reads, start, cancel) on its OWN agent through the `:8092` proxy when
246
+ `LUA_JOB_STEP_EVALS_ENABLED` is on. On a supervised (foreign) target it gets no eval routes: use `Agents.evals(...)`
247
+ in a code step, which carries the grant checks, the daily cap and the audit row. Suite writes are always refused.
248
+
249
+ ## Messaging a supervised agent's owner
250
+
251
+ `Agents.notifyOwner(agentId, { message })` posts a plain message (no model turn) into the **target owner's** home
252
+ thread with your agent, from any run, a schedule or an event trigger included. `Threads.followUp` from those runs
253
+ reaches only your own notify list.
254
+
255
+ ```typescript
256
+ // in an event trigger or a scheduled sweep
257
+ await Agents.notifyOwner('base_agent-tp', {
258
+ message: 'Lead Desk: the order lookup started failing at 14:05 (12 errors, signed out of Shopify).',
259
+ });
260
+ ```
261
+
262
+ - Needs `observe` on the agent and the **owner's opt-in on your grant**: the owner approved a
263
+ `requestAccess({ notifyOwner: true })` themselves, or turned it on with
264
+ `PUT /developer/agents/:agentId/supervisors/:grantId/owner-notify` (`{ enabled }`, the agent's creator only). An org
265
+ admin approving a shared agent's request cannot opt its owner in.
266
+ - Refused `supervision_owner_notify_not_opted_in` without the opt-in, after the agent changes owner, or once the owner
267
+ has left the org; `{ prompt }` is refused (`supervision_owner_notify_invalid`): it would start a turn as the owner.
268
+ - Shares `Threads.followUp`'s limits (12 per thread and 120 per agent per hour) and its audit row; the target's logs
269
+ get a `supervisor_access` row as for every supervision call.
270
+ - The platform does not inspect the text. An owner who opts in should know the supervisor may summarise what it
271
+ observes on any agent it supervises; replying in that thread talks to the supervisor as the owner.
272
+ - Off until `LUA_SUPERVISION_OWNER_NOTIFY_ENABLED` is on (lua-core and lua-api): `supervision_owner_notify_disabled`.
273
+ Not available offline.
274
+ - Add an `idempotencyKey` to make retries safe:
275
+ `Agents.notifyOwner(id, { message, idempotencyKey: 'incident-7' })`. A second call from your agent with the same key
276
+ within 24 hours posts nothing and returns the first receipt, with the same `followUpId` and `threadId`. A
277
+ concurrent duplicate waits for the first, or is refused `thread_follow_up_in_progress`; retry it. A first call
278
+ refused before it wrote anything frees the key; a failed write keeps it reserved for about two minutes, and a later
279
+ re-post replaces the same message. Delivery is at most once unless the key store fails mid-delivery. The key is 1-256 characters, and an invalid one is `supervision_owner_notify_invalid`, as is
280
+ a key already used to notify the owner of a different agent. Keys are separate from `Threads.followUp` keys.
281
+ Without a key, every call posts.
282
+
283
+ ## Events about failing tools
284
+
285
+ Subscribe to two more event kinds with `Agents.events(id).subscribe({ kinds, triggerName })`:
286
+
287
+ | Kind | When | `data` |
288
+ | ---------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
289
+ | `tool.auth_failed` | A tool or integration call of the agent failed with HTTP 401 / 403 or an expired / invalid token | `toolName`, `logSource`, `reason` (`unauthorized` / `forbidden` / `token_expired`), `windowStart` |
290
+ | `tool.errors_repeated` | The same tool (or job, webhook…) failed 5 times inside 10 minutes | `toolName`, `logSource`, `count`, `windowSeconds`, `windowStart` |
291
+ | `trigger.held` | A delivery to an inactive or paused trigger was held for replay (at most one per trigger per hour) | `triggerName`, `reason` |
292
+ | `agent.paused` | The agent was paused | `reason` (`owner_reason_given` / `none`, never the text), `pausedBy: 'user'` |
293
+
294
+ - `tool.*` events never carry the error text, and fire once per agent, tool and 10-minute window. Test traffic
295
+ (previews, eval runs, `lua dev`) is not counted. They need `LUA_SUPERVISION_TOOL_FAILURE_EVENTS_ENABLED` on lua-core
296
+ as well as `LUA_SUPERVISION_EVENTS_ENABLED`.