lua-cli 3.43.0 → 3.44.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 +117 -25
- package/dist/api-exports.js +129 -8
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +330 -113
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +18 -18
- package/dist/workflow-builder.d.ts +4 -1
- package/dist/workflow-builder.js.map +1 -1
- package/docs/api/Agents.md +7 -0
- package/docs/api/LuaJob.md +117 -87
- package/docs/api/PostProcessor.md +73 -42
- package/docs/api/User.md +104 -74
- package/docs/workflows/workspaces-and-long-steps.md +1 -1
- package/package.json +4 -4
- package/template/examples/postprocessors/modifyResponse.ts +4 -3
- package/template/package.json +1 -1
package/docs/api/Agents.md
CHANGED
|
@@ -24,6 +24,13 @@ const result = await Agents.invoke('base_agent-other', { prompt: 'hi', threadId:
|
|
|
24
24
|
- **The target knows who called.** The target's tools see `Lua.request.invokedBy = { agentId, supervisor, scopes, delegatedUserId? }`.
|
|
25
25
|
- `supervisor` is true only when an org admin granted the caller `supervision:act` on the target.
|
|
26
26
|
- A well-behaved target refuses writes unless `supervisor` is true.
|
|
27
|
+
- **`input.signal` cancels the call.** Optional `AbortSignal`. A signal that is already aborted rejects at once and sends no request. Aborting mid-flight rejects with `signal.reason` (an `AbortError` by default) and cancels the request, which stops the target's turn best-effort: a tool call already running on the target may still finish. Under `lua test` / `lua dev` the signal is honored by closing the HTTP request. A call without a signal is unchanged.
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
setTimeout(() => controller.abort(), 10_000);
|
|
32
|
+
const result = await Agents.invoke('base_agent-other', { prompt: 'hi', signal: controller.signal });
|
|
33
|
+
```
|
|
27
34
|
|
|
28
35
|
## Supervision
|
|
29
36
|
|
package/docs/api/LuaJob.md
CHANGED
|
@@ -11,6 +11,7 @@ import { LuaJob } from 'lua-cli';
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
13
|
Jobs enable:
|
|
14
|
+
|
|
14
15
|
- Schedule tasks with cron expressions
|
|
15
16
|
- Run one-time tasks at specific times
|
|
16
17
|
- Execute tasks at fixed intervals
|
|
@@ -76,6 +77,7 @@ interface LuaJobConfig {
|
|
|
76
77
|
```
|
|
77
78
|
|
|
78
79
|
**Cron Expression Format:**
|
|
80
|
+
|
|
79
81
|
```
|
|
80
82
|
┌───────────── minute (0 - 59)
|
|
81
83
|
│ ┌───────────── hour (0 - 23)
|
|
@@ -87,6 +89,7 @@ interface LuaJobConfig {
|
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
**Examples:**
|
|
92
|
+
|
|
90
93
|
- `'0 9 * * *'` - Every day at 9:00 AM
|
|
91
94
|
- `'0 */6 * * *'` - Every 6 hours
|
|
92
95
|
- `'0 9 * * 1'` - Every Monday at 9:00 AM
|
|
@@ -131,6 +134,8 @@ interface JobInstance {
|
|
|
131
134
|
}
|
|
132
135
|
```
|
|
133
136
|
|
|
137
|
+
> **`user.send()` from a job.** `user.send()` returns a boolean and never throws: `false` means the message was not delivered, and a bare `try/catch` never fires. Check the result (log it, or retry). It goes to the user's last-interaction channel; for a webchat-only user it lands in the user's base conversation thread and is pushed live to the widget, not into a specific workspace conversation. See [`user.send`](./User.md#usersendmessages) for the targeting rules.
|
|
138
|
+
|
|
134
139
|
## Complete Examples
|
|
135
140
|
|
|
136
141
|
### Daily Cleanup Job
|
|
@@ -144,19 +149,19 @@ export const dailyCleanup = new LuaJob({
|
|
|
144
149
|
|
|
145
150
|
schedule: {
|
|
146
151
|
type: 'cron',
|
|
147
|
-
expression: '0 2 * * *',
|
|
148
|
-
timezone: 'America/New_York'
|
|
152
|
+
expression: '0 2 * * *', // 2 AM every day
|
|
153
|
+
timezone: 'America/New_York',
|
|
149
154
|
},
|
|
150
155
|
|
|
151
|
-
timeout: 600,
|
|
156
|
+
timeout: 600, // 10 minutes
|
|
152
157
|
|
|
153
158
|
retry: {
|
|
154
159
|
maxAttempts: 3,
|
|
155
|
-
backoffSeconds: 60
|
|
160
|
+
backoffSeconds: 60,
|
|
156
161
|
},
|
|
157
162
|
|
|
158
163
|
metadata: {
|
|
159
|
-
retentionDays: 30
|
|
164
|
+
retentionDays: 30,
|
|
160
165
|
},
|
|
161
166
|
|
|
162
167
|
execute: async (job) => {
|
|
@@ -180,15 +185,15 @@ export const dailyCleanup = new LuaJob({
|
|
|
180
185
|
await job.updateMetadata({
|
|
181
186
|
lastRun: new Date().toISOString(),
|
|
182
187
|
deletedCount,
|
|
183
|
-
nextRetentionCheck: new Date(cutoffDate).toISOString()
|
|
188
|
+
nextRetentionCheck: new Date(cutoffDate).toISOString(),
|
|
184
189
|
});
|
|
185
190
|
|
|
186
191
|
return {
|
|
187
192
|
success: true,
|
|
188
193
|
deletedCount,
|
|
189
|
-
retentionDays
|
|
194
|
+
retentionDays,
|
|
190
195
|
};
|
|
191
|
-
}
|
|
196
|
+
},
|
|
192
197
|
});
|
|
193
198
|
```
|
|
194
199
|
|
|
@@ -203,8 +208,8 @@ export const weeklyReport = new LuaJob({
|
|
|
203
208
|
|
|
204
209
|
schedule: {
|
|
205
210
|
type: 'cron',
|
|
206
|
-
expression: '0 9 * * 1',
|
|
207
|
-
timezone: 'UTC'
|
|
211
|
+
expression: '0 9 * * 1', // Every Monday at 9 AM
|
|
212
|
+
timezone: 'UTC',
|
|
208
213
|
},
|
|
209
214
|
|
|
210
215
|
execute: async (job) => {
|
|
@@ -215,30 +220,32 @@ export const weeklyReport = new LuaJob({
|
|
|
215
220
|
const weekAgo = new Date();
|
|
216
221
|
weekAgo.setDate(weekAgo.getDate() - 7);
|
|
217
222
|
|
|
218
|
-
const recentActivities = activities.filter(a =>
|
|
219
|
-
new Date(a.data.timestamp) > weekAgo
|
|
220
|
-
);
|
|
223
|
+
const recentActivities = activities.filter((a) => new Date(a.data.timestamp) > weekAgo);
|
|
221
224
|
|
|
222
225
|
// Generate AI summary
|
|
223
|
-
const summary = await AI.generate(
|
|
224
|
-
|
|
225
|
-
[{
|
|
226
|
+
const summary = await AI.generate("Summarize this week's activity in 2-3 bullet points.", [
|
|
227
|
+
{
|
|
226
228
|
type: 'text',
|
|
227
|
-
text: JSON.stringify(recentActivities.map(a => a.data))
|
|
228
|
-
}
|
|
229
|
-
);
|
|
229
|
+
text: JSON.stringify(recentActivities.map((a) => a.data)),
|
|
230
|
+
},
|
|
231
|
+
]);
|
|
230
232
|
|
|
231
|
-
// Send report to user
|
|
232
|
-
await user.send([
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
233
|
+
// Send report to user. user.send() never throws: check the boolean result.
|
|
234
|
+
const delivered = await user.send([
|
|
235
|
+
{
|
|
236
|
+
type: 'text',
|
|
237
|
+
text: `📊 Weekly Activity Report\n\n${summary}\n\nTotal activities: ${recentActivities.length}`,
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
if (!delivered) {
|
|
241
|
+
console.warn('Weekly report was not delivered');
|
|
242
|
+
}
|
|
236
243
|
|
|
237
244
|
return {
|
|
238
|
-
success:
|
|
239
|
-
activitiesCount: recentActivities.length
|
|
245
|
+
success: delivered,
|
|
246
|
+
activitiesCount: recentActivities.length,
|
|
240
247
|
};
|
|
241
|
-
}
|
|
248
|
+
},
|
|
242
249
|
});
|
|
243
250
|
```
|
|
244
251
|
|
|
@@ -253,16 +260,16 @@ export const healthCheck = new LuaJob({
|
|
|
253
260
|
|
|
254
261
|
schedule: {
|
|
255
262
|
type: 'interval',
|
|
256
|
-
seconds: 300
|
|
263
|
+
seconds: 300, // Every 5 minutes
|
|
257
264
|
},
|
|
258
265
|
|
|
259
|
-
timeout: 30,
|
|
266
|
+
timeout: 30, // 30 seconds
|
|
260
267
|
|
|
261
268
|
execute: async (job) => {
|
|
262
269
|
const checks = {
|
|
263
270
|
database: false,
|
|
264
271
|
api: false,
|
|
265
|
-
storage: false
|
|
272
|
+
storage: false,
|
|
266
273
|
};
|
|
267
274
|
|
|
268
275
|
// Check database
|
|
@@ -285,37 +292,42 @@ export const healthCheck = new LuaJob({
|
|
|
285
292
|
try {
|
|
286
293
|
await Data.create('health_check', {
|
|
287
294
|
timestamp: new Date().toISOString(),
|
|
288
|
-
status: 'ok'
|
|
295
|
+
status: 'ok',
|
|
289
296
|
});
|
|
290
297
|
checks.storage = true;
|
|
291
298
|
} catch (error) {
|
|
292
299
|
console.error('Storage check failed:', error);
|
|
293
300
|
}
|
|
294
301
|
|
|
295
|
-
const allHealthy = Object.values(checks).every(c => c);
|
|
302
|
+
const allHealthy = Object.values(checks).every((c) => c);
|
|
296
303
|
|
|
297
304
|
// Log health status
|
|
298
305
|
await Data.create('health_logs', {
|
|
299
306
|
timestamp: new Date().toISOString(),
|
|
300
307
|
checks,
|
|
301
|
-
healthy: allHealthy
|
|
308
|
+
healthy: allHealthy,
|
|
302
309
|
});
|
|
303
310
|
|
|
304
311
|
// Alert if unhealthy
|
|
305
312
|
if (!allHealthy) {
|
|
306
313
|
const user = await job.user();
|
|
307
|
-
await user.send([
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
314
|
+
const alerted = await user.send([
|
|
315
|
+
{
|
|
316
|
+
type: 'text',
|
|
317
|
+
text: `⚠️ System health check failed!\n${JSON.stringify(checks, null, 2)}`,
|
|
318
|
+
},
|
|
319
|
+
]);
|
|
320
|
+
if (!alerted) {
|
|
321
|
+
console.warn('Health alert was not delivered');
|
|
322
|
+
}
|
|
311
323
|
}
|
|
312
324
|
|
|
313
325
|
return {
|
|
314
326
|
success: true,
|
|
315
327
|
healthy: allHealthy,
|
|
316
|
-
checks
|
|
328
|
+
checks,
|
|
317
329
|
};
|
|
318
|
-
}
|
|
330
|
+
},
|
|
319
331
|
});
|
|
320
332
|
```
|
|
321
333
|
|
|
@@ -330,7 +342,7 @@ export const birthdayReminder = new LuaJob({
|
|
|
330
342
|
|
|
331
343
|
schedule: {
|
|
332
344
|
type: 'cron',
|
|
333
|
-
expression: '0 9 * * *',
|
|
345
|
+
expression: '0 9 * * *', // Every day at 9 AM
|
|
334
346
|
},
|
|
335
347
|
|
|
336
348
|
execute: async (job) => {
|
|
@@ -352,21 +364,27 @@ export const birthdayReminder = new LuaJob({
|
|
|
352
364
|
if (birthdayStr === todayStr) {
|
|
353
365
|
// Send birthday message
|
|
354
366
|
const user = await User.get(userEntry.data.userId);
|
|
355
|
-
await user.send([
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
367
|
+
const sent = await user.send([
|
|
368
|
+
{
|
|
369
|
+
type: 'text',
|
|
370
|
+
text: `🎉 Happy Birthday, ${userEntry.data.name}! Wishing you an amazing day!`,
|
|
371
|
+
},
|
|
372
|
+
]);
|
|
373
|
+
|
|
374
|
+
if (sent) {
|
|
375
|
+
remindersSent++;
|
|
376
|
+
} else {
|
|
377
|
+
console.warn(`Birthday message not delivered to ${userEntry.data.userId}`);
|
|
378
|
+
}
|
|
361
379
|
}
|
|
362
380
|
}
|
|
363
381
|
|
|
364
382
|
return {
|
|
365
383
|
success: true,
|
|
366
384
|
remindersSent,
|
|
367
|
-
date: today.toISOString()
|
|
385
|
+
date: today.toISOString(),
|
|
368
386
|
};
|
|
369
|
-
}
|
|
387
|
+
},
|
|
370
388
|
});
|
|
371
389
|
```
|
|
372
390
|
|
|
@@ -382,17 +400,17 @@ export const dataSyncJob = new LuaJob({
|
|
|
382
400
|
|
|
383
401
|
schedule: {
|
|
384
402
|
type: 'interval',
|
|
385
|
-
seconds: 3600
|
|
403
|
+
seconds: 3600, // Every hour
|
|
386
404
|
},
|
|
387
405
|
|
|
388
406
|
retry: {
|
|
389
407
|
maxAttempts: 5,
|
|
390
|
-
backoffSeconds: 300
|
|
408
|
+
backoffSeconds: 300, // 5 minutes
|
|
391
409
|
},
|
|
392
410
|
|
|
393
411
|
metadata: {
|
|
394
412
|
lastSyncId: null,
|
|
395
|
-
totalSynced: 0
|
|
413
|
+
totalSynced: 0,
|
|
396
414
|
},
|
|
397
415
|
|
|
398
416
|
execute: async (job) => {
|
|
@@ -402,8 +420,8 @@ export const dataSyncJob = new LuaJob({
|
|
|
402
420
|
try {
|
|
403
421
|
// Fetch updates since last sync
|
|
404
422
|
const response = await axios.get(`${apiUrl}/updates`, {
|
|
405
|
-
headers: {
|
|
406
|
-
params: { since: job.metadata.lastSyncId }
|
|
423
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
424
|
+
params: { since: job.metadata.lastSyncId },
|
|
407
425
|
});
|
|
408
426
|
|
|
409
427
|
const updates = response.data;
|
|
@@ -414,7 +432,7 @@ export const dataSyncJob = new LuaJob({
|
|
|
414
432
|
await Data.create('synced_data', {
|
|
415
433
|
externalId: update.id,
|
|
416
434
|
data: update.data,
|
|
417
|
-
syncedAt: new Date().toISOString()
|
|
435
|
+
syncedAt: new Date().toISOString(),
|
|
418
436
|
});
|
|
419
437
|
syncedCount++;
|
|
420
438
|
}
|
|
@@ -424,19 +442,19 @@ export const dataSyncJob = new LuaJob({
|
|
|
424
442
|
await job.updateMetadata({
|
|
425
443
|
lastSyncId: lastId,
|
|
426
444
|
totalSynced: job.metadata.totalSynced + syncedCount,
|
|
427
|
-
lastSyncAt: new Date().toISOString()
|
|
445
|
+
lastSyncAt: new Date().toISOString(),
|
|
428
446
|
});
|
|
429
447
|
|
|
430
448
|
return {
|
|
431
449
|
success: true,
|
|
432
450
|
syncedCount,
|
|
433
|
-
lastId
|
|
451
|
+
lastId,
|
|
434
452
|
};
|
|
435
453
|
} catch (error) {
|
|
436
454
|
console.error('Sync failed:', error);
|
|
437
|
-
throw error;
|
|
455
|
+
throw error; // Will trigger retry
|
|
438
456
|
}
|
|
439
|
-
}
|
|
457
|
+
},
|
|
440
458
|
});
|
|
441
459
|
```
|
|
442
460
|
|
|
@@ -451,30 +469,37 @@ export const sendWelcomeEmail = new LuaJob({
|
|
|
451
469
|
|
|
452
470
|
schedule: {
|
|
453
471
|
type: 'once',
|
|
454
|
-
executeAt: new Date(Date.now() + 3600000)
|
|
472
|
+
executeAt: new Date(Date.now() + 3600000), // 1 hour from now
|
|
455
473
|
},
|
|
456
474
|
|
|
457
475
|
metadata: {
|
|
458
476
|
userId: 'user_123',
|
|
459
|
-
userName: 'John Doe'
|
|
477
|
+
userName: 'John Doe',
|
|
460
478
|
},
|
|
461
479
|
|
|
462
480
|
execute: async (job) => {
|
|
463
481
|
const user = await User.get(job.metadata.userId);
|
|
464
482
|
|
|
465
|
-
await user.send([
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
483
|
+
const delivered = await user.send([
|
|
484
|
+
{
|
|
485
|
+
type: 'text',
|
|
486
|
+
text: `Welcome to our platform, ${job.metadata.userName}! 🎉\n\nWe're excited to have you here. Here are some things you can do:\n\n• Explore our features\n• Complete your profile\n• Connect with the community\n\nIf you have any questions, just ask!`,
|
|
487
|
+
},
|
|
488
|
+
]);
|
|
489
|
+
|
|
490
|
+
if (!delivered) {
|
|
491
|
+
// Keep the job so it can be retried instead of losing the welcome message
|
|
492
|
+
return { success: false, userId: job.metadata.userId };
|
|
493
|
+
}
|
|
469
494
|
|
|
470
495
|
// Delete job after execution (one-time only)
|
|
471
496
|
await job.delete();
|
|
472
497
|
|
|
473
498
|
return {
|
|
474
499
|
success: true,
|
|
475
|
-
userId: job.metadata.userId
|
|
500
|
+
userId: job.metadata.userId,
|
|
476
501
|
};
|
|
477
|
-
}
|
|
502
|
+
},
|
|
478
503
|
});
|
|
479
504
|
```
|
|
480
505
|
|
|
@@ -505,34 +530,34 @@ export const sendWelcomeEmail = new LuaJob({
|
|
|
505
530
|
|
|
506
531
|
```typescript
|
|
507
532
|
// Every minute
|
|
508
|
-
'* * * * *'
|
|
533
|
+
'* * * * *';
|
|
509
534
|
|
|
510
535
|
// Every 5 minutes
|
|
511
|
-
'*/5 * * * *'
|
|
536
|
+
'*/5 * * * *';
|
|
512
537
|
|
|
513
538
|
// Every hour at minute 0
|
|
514
|
-
'0 * * * *'
|
|
539
|
+
'0 * * * *';
|
|
515
540
|
|
|
516
541
|
// Every day at midnight
|
|
517
|
-
'0 0 * * *'
|
|
542
|
+
'0 0 * * *';
|
|
518
543
|
|
|
519
544
|
// Every day at 3:30 PM
|
|
520
|
-
'30 15 * * *'
|
|
545
|
+
'30 15 * * *';
|
|
521
546
|
|
|
522
547
|
// Every Monday at 9 AM
|
|
523
|
-
'0 9 * * 1'
|
|
548
|
+
'0 9 * * 1';
|
|
524
549
|
|
|
525
550
|
// Every weekday at 6 PM
|
|
526
|
-
'0 18 * * 1-5'
|
|
551
|
+
'0 18 * * 1-5';
|
|
527
552
|
|
|
528
553
|
// First day of every month
|
|
529
|
-
'0 0 1 * *'
|
|
554
|
+
'0 0 1 * *';
|
|
530
555
|
|
|
531
556
|
// Last day of every month (not directly supported, use workaround)
|
|
532
|
-
'0 0 28-31 * *'
|
|
557
|
+
'0 0 28-31 * *';
|
|
533
558
|
|
|
534
559
|
// Every 6 hours
|
|
535
|
-
'0 */6 * * *'
|
|
560
|
+
'0 */6 * * *';
|
|
536
561
|
```
|
|
537
562
|
|
|
538
563
|
## Error Handling
|
|
@@ -544,12 +569,12 @@ export const robustJob = new LuaJob({
|
|
|
544
569
|
|
|
545
570
|
schedule: {
|
|
546
571
|
type: 'cron',
|
|
547
|
-
expression: '0 * * * *'
|
|
572
|
+
expression: '0 * * * *', // Every hour
|
|
548
573
|
},
|
|
549
574
|
|
|
550
575
|
retry: {
|
|
551
576
|
maxAttempts: 3,
|
|
552
|
-
backoffSeconds: 60
|
|
577
|
+
backoffSeconds: 60,
|
|
553
578
|
},
|
|
554
579
|
|
|
555
580
|
execute: async (job) => {
|
|
@@ -560,12 +585,12 @@ export const robustJob = new LuaJob({
|
|
|
560
585
|
// Update success metadata
|
|
561
586
|
await job.updateMetadata({
|
|
562
587
|
lastSuccess: new Date().toISOString(),
|
|
563
|
-
consecutiveFailures: 0
|
|
588
|
+
consecutiveFailures: 0,
|
|
564
589
|
});
|
|
565
590
|
|
|
566
591
|
return {
|
|
567
592
|
success: true,
|
|
568
|
-
result
|
|
593
|
+
result,
|
|
569
594
|
};
|
|
570
595
|
} catch (error) {
|
|
571
596
|
// Track failures
|
|
@@ -573,21 +598,26 @@ export const robustJob = new LuaJob({
|
|
|
573
598
|
await job.updateMetadata({
|
|
574
599
|
lastFailure: new Date().toISOString(),
|
|
575
600
|
lastError: error.message,
|
|
576
|
-
consecutiveFailures: failures
|
|
601
|
+
consecutiveFailures: failures,
|
|
577
602
|
});
|
|
578
603
|
|
|
579
604
|
// Alert after 3 consecutive failures
|
|
580
605
|
if (failures >= 3) {
|
|
581
606
|
const user = await job.user();
|
|
582
|
-
await user.send([
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
607
|
+
const alerted = await user.send([
|
|
608
|
+
{
|
|
609
|
+
type: 'text',
|
|
610
|
+
text: `⚠️ Job "${job.metadata.name}" has failed 3 times in a row. Last error: ${error.message}`,
|
|
611
|
+
},
|
|
612
|
+
]);
|
|
613
|
+
if (!alerted) {
|
|
614
|
+
console.warn('Failure alert was not delivered');
|
|
615
|
+
}
|
|
586
616
|
}
|
|
587
617
|
|
|
588
|
-
throw error;
|
|
618
|
+
throw error; // Re-throw to trigger retry
|
|
589
619
|
}
|
|
590
|
-
}
|
|
620
|
+
},
|
|
591
621
|
});
|
|
592
622
|
```
|
|
593
623
|
|