lua-cli 3.42.0-beta.2 → 3.42.0-beta.3
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 +74 -3
- package/dist/api-exports.js +187 -10
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +272 -19
- package/dist/index.js.map +1 -1
- package/dist/workflow-builder.js +153 -7
- package/dist/workflow-builder.js.map +1 -1
- package/docs/api/Agents.md +78 -0
- package/docs/api/LuaWebhook.md +40 -2
- package/package.json +2 -2
- package/template/package.json +1 -1
package/docs/api/Agents.md
CHANGED
|
@@ -280,6 +280,84 @@ await Agents.notifyOwner('base_agent-tp', {
|
|
|
280
280
|
a key already used to notify the owner of a different agent. Keys are separate from `Threads.followUp` keys.
|
|
281
281
|
Without a key, every call posts.
|
|
282
282
|
|
|
283
|
+
## Handing a supervised agent one of its owner's connections
|
|
284
|
+
|
|
285
|
+
`Agents.connections(agentId).assign(connectionId, { approval })` mounts one of the agent **owner's own personal
|
|
286
|
+
connections** (their Gmail, their Slack…) on the agent, for example on an agent your Supervisor built for them (a
|
|
287
|
+
provisioned agent gets none of its owner's connections by itself). The owner consents on Lua's own approval card, in
|
|
288
|
+
their signed-in Desktop or inbox session:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// a code step of a workflow the OWNER started from their own turn (the run's creator is the owner)
|
|
292
|
+
const card = {
|
|
293
|
+
connectionAssignment: { connectionId, agentId, agentName, supervisorName, integration: 'googlemail', account },
|
|
294
|
+
};
|
|
295
|
+
// …previous step returns `card`, then:
|
|
296
|
+
// .approval('giveGmail', { approver: 'creator', title: `Give your googlemail connection to "${agentName}"?` })
|
|
297
|
+
await Agents.connections(agentId).assign(connectionId, { approval: { runId: ctx.runId, stepId: 'giveGmail' } });
|
|
298
|
+
// → { agentId, connectionId, integration, status: 'assigned' }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- The payload is exactly
|
|
302
|
+
`{ connectionAssignment: { connectionId, agentId, agentName, supervisorName, integration, account } }`: `agentName`
|
|
303
|
+
is the agent's current name, `supervisorName` your agent's, `integration` the connection's integration type and
|
|
304
|
+
`account` the label Lua gives the connection (its display name, else the provider's first email). Call `assign` from
|
|
305
|
+
a code step of the same run that holds the approval (`connection_handover_binding_required` otherwise); the card
|
|
306
|
+
carries no artefacts. Lua shows these facts on the card (and that your agent can reach the account through the agent it
|
|
307
|
+
supervises) and checks each against what it stores; an extra key such as `summary` is refused. The step's `title`
|
|
308
|
+
must be exactly `Give your <integration> connection to "<agentName>"?` (`connectionHandoverApprovalTitle`). A
|
|
309
|
+
connection with no label, or whose label another of the owner's connections of that kind shares, is refused
|
|
310
|
+
(`connection_handover_account_unidentified`); so is an agent whose name (or yours) has invisible format characters or
|
|
311
|
+
that shares its name with another of the owner's agents (`connection_handover_agent_unidentified`). The card shows the
|
|
312
|
+
agent's name with the last 6 characters of its id.
|
|
313
|
+
- Only the owner's own decision counts: approved in a first-party session with a proved sign-in (not a chat card, a
|
|
314
|
+
channel reply or an API key), by the person who created the agent and started your run, within 10 minutes, unedited.
|
|
315
|
+
One assignment per approval. Refusals: `connection_handover_approval_required`, `connection_handover_approval_mismatch`.
|
|
316
|
+
- The connection must be the owner's own user-owned connection (not one delegated to them) and the agent their private
|
|
317
|
+
agent, else `connection_handover_not_assignable`. Needs `act` on the agent.
|
|
318
|
+
- The owner removes it like any mount (`DELETE /developer/unifiedto/connections/:agentId/:connectionId/attach`).
|
|
319
|
+
- Off until `LUA_SUPERVISED_CONNECTION_HANDOVER_ENABLED` is on (lua-core and lua-api): `connection_handover_disabled`.
|
|
320
|
+
Not available offline.
|
|
321
|
+
|
|
322
|
+
## Keys for an agent that already exists
|
|
323
|
+
|
|
324
|
+
Ask the person for a key for one of THEIR agents; they submit it signed in, Lua tests it, then you attach it:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// in a tool, on the owner's own chat turn
|
|
328
|
+
const { requestId } = await Secrets.request({
|
|
329
|
+
provider: 'openai',
|
|
330
|
+
key: 'OPENAI_API_KEY',
|
|
331
|
+
target: { pendingFor: 'lead-desk', agentId: 'base_agent-tp' },
|
|
332
|
+
reason: 'Lead Desk summarises new leads',
|
|
333
|
+
});
|
|
334
|
+
// the OWNER checks what they are submitting, then submits it signed in (their session or their own API key):
|
|
335
|
+
// GET /developer/secrets/held-requests/<requestId> → provider, key, env key names, agent, state, expiry
|
|
336
|
+
// POST /developer/secrets/held-requests/<requestId>/submit {"values":{"apiKey":"…"}}
|
|
337
|
+
// later, on the owner's turn again:
|
|
338
|
+
await Agents.secrets('base_agent-tp').attach([requestId]); // { agentId, attached, envKeys }
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
- Asking needs `supervision:act` on the agent, an agent of your org that is not your own, created by the person in the
|
|
342
|
+
conversation, on that person's **live interactive turn** (a tool in a chat turn; never a scheduled task, Job,
|
|
343
|
+
workflow step or a user another agent chose). Keys starting `LUA_` or `NODE_` are refused. Otherwise
|
|
344
|
+
`secret_target_not_allowed` / `secret_key_invalid`.
|
|
345
|
+
- The value is HELD (encrypted), never written to the agent until `attach`. Only the owner can submit it, signed in:
|
|
346
|
+
the public secret page (and its token) refuses these requests (`secret_submit_requires_owner`).
|
|
347
|
+
- `attach` needs `supervision:act` and the owner's live interactive turn, as above. It takes only requests YOU held
|
|
348
|
+
for THIS agent that the owner submitted signed in and whose value passed Lua's test (`ok`; never `unverified`), all
|
|
349
|
+
or none (`secret_pending_not_attachable`), and never replaces a key the agent already has (`secret_key_exists`).
|
|
350
|
+
The agent's logs get a `supervisor_access` row and an outcome row (request ids and key names, never values).
|
|
351
|
+
- A key held for an existing agent is never attached to a newly provisioned one, and a `{ pendingFor }` key for a new
|
|
352
|
+
agent is never taken by `attach`. If the agent is archived, or changes owner or org after the hold, the owner can no
|
|
353
|
+
longer view or submit it.
|
|
354
|
+
- Accepted residuals: (1) "signed in" includes the owner's own API key, so an owner's PERSONAL API key placed in some
|
|
355
|
+
agent's env could submit as the owner (kept so API-only owners can submit at all); (2) the live-turn marker follows an
|
|
356
|
+
`Agents.invoke` chain that started on the owner's live turn (a tool the owner's turn invoked counts as that turn).
|
|
357
|
+
- Off until `LUA_SECRET_ATTACH_EXISTING_ENABLED` is on (lua-core and lua-api, with lua-agents on the same build), with `LUA_SECRET_REQUESTS_ENABLED`
|
|
358
|
+
(`secret_attach_existing_disabled`); while it is off, `agentId` in a target is ignored as before. Not available
|
|
359
|
+
offline.
|
|
360
|
+
|
|
283
361
|
## Events about failing tools
|
|
284
362
|
|
|
285
363
|
Subscribe to two more event kinds with `Agents.events(id).subscribe({ kinds, triggerName })`:
|
package/docs/api/LuaWebhook.md
CHANGED
|
@@ -52,11 +52,49 @@ interface LuaWebhookConfig {
|
|
|
52
52
|
/** Optional HMAC-SHA256 signing key (see Request Verification) */
|
|
53
53
|
secret?: string;
|
|
54
54
|
|
|
55
|
+
/** Reject schema-violating requests with HTTP 400 (default: log a warning only) */
|
|
56
|
+
enforceSchemas?: boolean;
|
|
57
|
+
|
|
55
58
|
/** Function that executes the webhook logic */
|
|
56
59
|
execute: (event: LuaWebhookEvent) => Promise<any>;
|
|
57
60
|
}
|
|
58
61
|
```
|
|
59
62
|
|
|
63
|
+
## Schema Enforcement
|
|
64
|
+
|
|
65
|
+
`querySchema`, `headerSchema` and `bodySchema` are checked before `execute` runs, for webhooks
|
|
66
|
+
compiled with this CLI version or later. Only the schemas you declare are applied. An absent input
|
|
67
|
+
is validated as `{}` (query/headers) or `undefined` (body), so a required `bodySchema` flags a
|
|
68
|
+
request with no body.
|
|
69
|
+
|
|
70
|
+
**Default: log only.** A violating request still reaches `execute` with the original `event`, and a
|
|
71
|
+
single warning is written to the webhook's logs (also printed by `lua test`):
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
[lua:webhook-schema] would-reject webhook=<name> parts=query,body issues=3 first=body:n:invalid_type (request still processed; ...)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The line names the webhook, the failing parts, the total issue count and the first issue as
|
|
78
|
+
`<part>:<path>:<code>`. It never includes input values or issue messages. Filter the logs on the
|
|
79
|
+
`[lua:webhook-schema] would-reject` prefix to count would-rejects.
|
|
80
|
+
|
|
81
|
+
**Opt in to rejection** with `enforceSchemas: true`. A violating request is then rejected before
|
|
82
|
+
`execute` (query is checked first, then headers, then body) with HTTP 400 when deployed, and fails
|
|
83
|
+
`lua test` with the same message:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{ "statusCode": 400, "code": "VALIDATION", "message": "Invalid body", "part": "body", "issues": [] }
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Recommended rollout: deploy, watch the would-reject warnings, fix the schemas or the senders, then
|
|
90
|
+
set `enforceSchemas: true`. Only a strict boolean `true` enforces.
|
|
91
|
+
|
|
92
|
+
- Validation only: `execute` receives the original `event`. No coercion, defaults or key stripping are applied to `event.query`, `event.headers` or `event.body`.
|
|
93
|
+
- Header names are lowercase, so keys in a `headerSchema` must be lowercase (`'x-probe'`, not `'X-Probe'`). Transport headers are present too, so avoid `z.strictObject` for headers.
|
|
94
|
+
- Webhooks that also subscribe to events receive `body: { eventType, ...payload }`; make sure `bodySchema` allows `eventType`.
|
|
95
|
+
- Event deliveries are checked too, and they arrive with EMPTY `query` and `headers`. A `querySchema` or `headerSchema` with required keys, or a `bodySchema` that rejects `eventType`, logs a warning on every event delivery. Only with `enforceSchemas: true` do those deliveries fail, get logged at ERROR and not retried. Keep these schemas permissive, or declare them only on webhooks that are never event-subscribed.
|
|
96
|
+
- Webhooks deployed from older CLI versions keep the old, unvalidated behaviour until they are re-compiled and re-pushed.
|
|
97
|
+
|
|
60
98
|
## Request Verification
|
|
61
99
|
|
|
62
100
|
A webhook URL is public. Set `secret` and Lua requires every request to carry
|
|
@@ -549,7 +587,7 @@ curl -X POST http://localhost:3002/webhook/contact-form \
|
|
|
549
587
|
|
|
550
588
|
### ✅ DO
|
|
551
589
|
|
|
552
|
-
- Always
|
|
590
|
+
- Always declare schemas (query, headers, body), watch the would-reject warnings, then opt in with `enforceSchemas: true`
|
|
553
591
|
- Verify webhook signatures when available (GitHub, Stripe, etc.)
|
|
554
592
|
- Return meaningful response objects
|
|
555
593
|
- Store webhook events for auditing
|
|
@@ -560,7 +598,7 @@ curl -X POST http://localhost:3002/webhook/contact-form \
|
|
|
560
598
|
|
|
561
599
|
### ❌ DON'T
|
|
562
600
|
|
|
563
|
-
- Don't skip validation -
|
|
601
|
+
- Don't skip validation - declare schemas and enable `enforceSchemas` once the warnings are clean
|
|
564
602
|
- Don't expose sensitive data in responses
|
|
565
603
|
- Don't perform long-running operations synchronously
|
|
566
604
|
- Don't trust incoming data without verification
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lua-cli",
|
|
3
|
-
"version": "3.42.0-beta.
|
|
3
|
+
"version": "3.42.0-beta.3",
|
|
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",
|
|
@@ -115,9 +115,9 @@
|
|
|
115
115
|
"ts-node": "^10.9.2",
|
|
116
116
|
"tsup": "^8.5.1",
|
|
117
117
|
"@lua/sandbox-runtime": "0.0.1",
|
|
118
|
-
"@lua/shared-sandbox": "0.0.1",
|
|
119
118
|
"@lua/shared-source-sync": "0.0.1",
|
|
120
119
|
"@lua/shared-types": "0.0.1",
|
|
120
|
+
"@lua/shared-sandbox": "0.0.1",
|
|
121
121
|
"@lua/workflow-graph": "0.0.1"
|
|
122
122
|
},
|
|
123
123
|
"scripts": {
|