lua-cli 3.55.0 → 3.56.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 +70 -20
- package/dist/api-exports.js +5 -1
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +24 -5
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +18 -18
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +3 -3
- package/docs/api/PreProcessor.md +66 -56
- package/docs/api/Threads.md +14 -0
- package/package.json +1 -1
- package/template/package.json +1 -1
package/docs/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# lua-cli v3.
|
|
1
|
+
# lua-cli v3.56.0
|
|
2
2
|
|
|
3
|
-
Welcome to the comprehensive API documentation for lua-cli v3.
|
|
3
|
+
Welcome to the comprehensive API documentation for lua-cli v3.56.0. This guide covers every API, class, and function exported by the package.
|
|
4
4
|
|
|
5
5
|
## 📚 Documentation Index
|
|
6
6
|
|
|
@@ -263,7 +263,7 @@ Each API doc includes:
|
|
|
263
263
|
|
|
264
264
|
---
|
|
265
265
|
|
|
266
|
-
**Version:** 3.
|
|
266
|
+
**Version:** 3.56.0
|
|
267
267
|
**Last Updated:** October 2026
|
|
268
268
|
|
|
269
269
|
---
|
package/docs/api/PreProcessor.md
CHANGED
|
@@ -11,6 +11,7 @@ import { PreProcessor } from 'lua-cli';
|
|
|
11
11
|
## Overview
|
|
12
12
|
|
|
13
13
|
PreProcessors enable:
|
|
14
|
+
|
|
14
15
|
- Filter and block inappropriate content
|
|
15
16
|
- Modify messages before agent processing
|
|
16
17
|
- Add context and enrichment to messages
|
|
@@ -60,11 +61,7 @@ interface PreProcessorConfig {
|
|
|
60
61
|
/**
|
|
61
62
|
* Function that processes messages before sending to agent.
|
|
62
63
|
*/
|
|
63
|
-
execute: (
|
|
64
|
-
user: UserDataInstance,
|
|
65
|
-
messages: ChatMessage[],
|
|
66
|
-
channel: string
|
|
67
|
-
) => Promise<PreProcessorResult>;
|
|
64
|
+
execute: (user: UserDataInstance, messages: ChatMessage[], channel: string) => Promise<PreProcessorResult>;
|
|
68
65
|
}
|
|
69
66
|
```
|
|
70
67
|
|
|
@@ -90,10 +87,22 @@ Continue to the next preprocessor or agent:
|
|
|
90
87
|
{
|
|
91
88
|
action: 'proceed',
|
|
92
89
|
modifiedMessage?: ChatMessage[], // Optional modified messages
|
|
93
|
-
metadata?:
|
|
90
|
+
metadata?: { turnContext?: string; [key: string]: any } // Optional metadata
|
|
94
91
|
}
|
|
95
92
|
```
|
|
96
93
|
|
|
94
|
+
#### Turn context
|
|
95
|
+
|
|
96
|
+
Set `metadata.turnContext` to give the model a short note for this turn only, such as state read from your own data:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
return { action: 'proceed', metadata: { turnContext: 'Checklist in progress: section 12 of 48.' } };
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The platform adds it after the conversation, labelled as system-provided context the user did not write. It is never stored, never in chat history and never shown to the user. Notes from several preprocessors are joined in run order and capped at 2,000 characters in total; anything that is not a string is ignored.
|
|
103
|
+
|
|
104
|
+
The model is told the note is data, not instructions, because it may quote text users wrote. Do not use it to steer the agent's behaviour; put rules in the persona or a skill.
|
|
105
|
+
|
|
97
106
|
## Message Types
|
|
98
107
|
|
|
99
108
|
```typescript
|
|
@@ -113,7 +122,7 @@ import { PreProcessor } from 'lua-cli';
|
|
|
113
122
|
export const contentFilter = new PreProcessor({
|
|
114
123
|
name: 'content-filter',
|
|
115
124
|
description: 'Filters inappropriate content',
|
|
116
|
-
priority: 10,
|
|
125
|
+
priority: 10, // Run early
|
|
117
126
|
|
|
118
127
|
execute: async (user, messages, channel) => {
|
|
119
128
|
const blockedWords = ['spam', 'scam', 'phishing'];
|
|
@@ -130,8 +139,8 @@ export const contentFilter = new PreProcessor({
|
|
|
130
139
|
response: 'Your message contains inappropriate content and cannot be processed.',
|
|
131
140
|
metadata: {
|
|
132
141
|
reason: 'blocked_word',
|
|
133
|
-
word
|
|
134
|
-
}
|
|
142
|
+
word,
|
|
143
|
+
},
|
|
135
144
|
};
|
|
136
145
|
}
|
|
137
146
|
}
|
|
@@ -139,7 +148,7 @@ export const contentFilter = new PreProcessor({
|
|
|
139
148
|
}
|
|
140
149
|
|
|
141
150
|
return { action: 'proceed' };
|
|
142
|
-
}
|
|
151
|
+
},
|
|
143
152
|
});
|
|
144
153
|
```
|
|
145
154
|
|
|
@@ -151,7 +160,7 @@ import { PreProcessor } from 'lua-cli';
|
|
|
151
160
|
export const userVerification = new PreProcessor({
|
|
152
161
|
name: 'user-verification',
|
|
153
162
|
description: 'Verifies user has completed onboarding',
|
|
154
|
-
priority: 5,
|
|
163
|
+
priority: 5, // Run very early
|
|
155
164
|
|
|
156
165
|
execute: async (user, messages, channel) => {
|
|
157
166
|
const userData = await user.get();
|
|
@@ -162,8 +171,8 @@ export const userVerification = new PreProcessor({
|
|
|
162
171
|
action: 'block',
|
|
163
172
|
response: 'Please complete your profile setup before using the assistant. Type "/start" to begin.',
|
|
164
173
|
metadata: {
|
|
165
|
-
reason: 'onboarding_incomplete'
|
|
166
|
-
}
|
|
174
|
+
reason: 'onboarding_incomplete',
|
|
175
|
+
},
|
|
167
176
|
};
|
|
168
177
|
}
|
|
169
178
|
|
|
@@ -173,13 +182,13 @@ export const userVerification = new PreProcessor({
|
|
|
173
182
|
action: 'block',
|
|
174
183
|
response: 'Your account has been suspended. Please contact support.',
|
|
175
184
|
metadata: {
|
|
176
|
-
reason: 'account_suspended'
|
|
177
|
-
}
|
|
185
|
+
reason: 'account_suspended',
|
|
186
|
+
},
|
|
178
187
|
};
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
return { action: 'proceed' };
|
|
182
|
-
}
|
|
191
|
+
},
|
|
183
192
|
});
|
|
184
193
|
```
|
|
185
194
|
|
|
@@ -199,7 +208,7 @@ export const messageEnrichment = new PreProcessor({
|
|
|
199
208
|
// Add context message with user info
|
|
200
209
|
const contextMessage = {
|
|
201
210
|
type: 'text' as const,
|
|
202
|
-
text: `[User Context: Name: ${userData.data?.name || 'Unknown'}, Plan: ${userData.data?.plan || 'free'}, Location: ${userData.data?.location || 'Unknown'}]
|
|
211
|
+
text: `[User Context: Name: ${userData.data?.name || 'Unknown'}, Plan: ${userData.data?.plan || 'free'}, Location: ${userData.data?.location || 'Unknown'}]`,
|
|
203
212
|
};
|
|
204
213
|
|
|
205
214
|
// Prepend context to messages
|
|
@@ -210,10 +219,10 @@ export const messageEnrichment = new PreProcessor({
|
|
|
210
219
|
modifiedMessage: enrichedMessages,
|
|
211
220
|
metadata: {
|
|
212
221
|
enriched: true,
|
|
213
|
-
userPlan: userData.data?.plan
|
|
214
|
-
}
|
|
222
|
+
userPlan: userData.data?.plan,
|
|
223
|
+
},
|
|
215
224
|
};
|
|
216
|
-
}
|
|
225
|
+
},
|
|
217
226
|
});
|
|
218
227
|
```
|
|
219
228
|
|
|
@@ -230,27 +239,26 @@ export const rateLimiter = new PreProcessor({
|
|
|
230
239
|
execute: async (user, messages, channel) => {
|
|
231
240
|
const userId = user.id;
|
|
232
241
|
const now = Date.now();
|
|
233
|
-
const windowMs = 60 * 1000;
|
|
242
|
+
const windowMs = 60 * 1000; // 1 minute
|
|
234
243
|
const maxMessages = 10;
|
|
235
244
|
|
|
236
245
|
// Get user's recent messages
|
|
237
246
|
const rateLimitKey = `rate_limit_${userId}`;
|
|
238
247
|
const records = await Data.get('rate_limits');
|
|
239
|
-
let userRecord = records.find(r => r.data.key === rateLimitKey);
|
|
248
|
+
let userRecord = records.find((r) => r.data.key === rateLimitKey);
|
|
240
249
|
|
|
241
250
|
if (!userRecord) {
|
|
242
251
|
// Create new record
|
|
243
252
|
await Data.create('rate_limits', {
|
|
244
253
|
key: rateLimitKey,
|
|
245
254
|
messages: [now],
|
|
246
|
-
windowStart: now
|
|
255
|
+
windowStart: now,
|
|
247
256
|
});
|
|
248
257
|
return { action: 'proceed' };
|
|
249
258
|
}
|
|
250
259
|
|
|
251
260
|
// Filter messages within window
|
|
252
|
-
const recentMessages = userRecord.data.messages
|
|
253
|
-
.filter((timestamp: number) => now - timestamp < windowMs);
|
|
261
|
+
const recentMessages = userRecord.data.messages.filter((timestamp: number) => now - timestamp < windowMs);
|
|
254
262
|
|
|
255
263
|
// Check if limit exceeded
|
|
256
264
|
if (recentMessages.length >= maxMessages) {
|
|
@@ -259,19 +267,19 @@ export const rateLimiter = new PreProcessor({
|
|
|
259
267
|
response: 'You are sending messages too quickly. Please wait a moment and try again.',
|
|
260
268
|
metadata: {
|
|
261
269
|
reason: 'rate_limit_exceeded',
|
|
262
|
-
retryAfter: windowMs - (now - recentMessages[0])
|
|
263
|
-
}
|
|
270
|
+
retryAfter: windowMs - (now - recentMessages[0]),
|
|
271
|
+
},
|
|
264
272
|
};
|
|
265
273
|
}
|
|
266
274
|
|
|
267
275
|
// Update record
|
|
268
276
|
await Data.update('rate_limits', userRecord.id, {
|
|
269
277
|
...userRecord.data,
|
|
270
|
-
messages: [...recentMessages, now]
|
|
278
|
+
messages: [...recentMessages, now],
|
|
271
279
|
});
|
|
272
280
|
|
|
273
281
|
return { action: 'proceed' };
|
|
274
|
-
}
|
|
282
|
+
},
|
|
275
283
|
});
|
|
276
284
|
```
|
|
277
285
|
|
|
@@ -284,17 +292,17 @@ export const spamDetector = new PreProcessor({
|
|
|
284
292
|
name: 'spam-detector',
|
|
285
293
|
description: 'AI-powered spam detection',
|
|
286
294
|
priority: 20,
|
|
287
|
-
async: true,
|
|
295
|
+
async: true, // Can run in background
|
|
288
296
|
|
|
289
297
|
execute: async (user, messages, channel) => {
|
|
290
298
|
// Only check text messages
|
|
291
|
-
const textMessages = messages.filter(m => m.type === 'text');
|
|
299
|
+
const textMessages = messages.filter((m) => m.type === 'text');
|
|
292
300
|
|
|
293
301
|
if (textMessages.length === 0) {
|
|
294
302
|
return { action: 'proceed' };
|
|
295
303
|
}
|
|
296
304
|
|
|
297
|
-
const messageText = textMessages.map(m => m.text).join(' ');
|
|
305
|
+
const messageText = textMessages.map((m) => m.text).join(' ');
|
|
298
306
|
|
|
299
307
|
// Use AI to detect spam
|
|
300
308
|
const analysis = await AI.generate(
|
|
@@ -312,8 +320,8 @@ export const spamDetector = new PreProcessor({
|
|
|
312
320
|
metadata: {
|
|
313
321
|
reason: 'spam_detected',
|
|
314
322
|
confidence: result.confidence,
|
|
315
|
-
analysis: result.reason
|
|
316
|
-
}
|
|
323
|
+
analysis: result.reason,
|
|
324
|
+
},
|
|
317
325
|
};
|
|
318
326
|
}
|
|
319
327
|
} catch (error) {
|
|
@@ -322,7 +330,7 @@ export const spamDetector = new PreProcessor({
|
|
|
322
330
|
}
|
|
323
331
|
|
|
324
332
|
return { action: 'proceed' };
|
|
325
|
-
}
|
|
333
|
+
},
|
|
326
334
|
});
|
|
327
335
|
```
|
|
328
336
|
|
|
@@ -349,7 +357,7 @@ export const businessHoursChecker = new PreProcessor({
|
|
|
349
357
|
// Add notice to message
|
|
350
358
|
const noticeMessage = {
|
|
351
359
|
type: 'text' as const,
|
|
352
|
-
text: '[Note: You are contacting us outside business hours. Our team will respond when we return (Monday-Friday, 9 AM - 5 PM).]'
|
|
360
|
+
text: '[Note: You are contacting us outside business hours. Our team will respond when we return (Monday-Friday, 9 AM - 5 PM).]',
|
|
353
361
|
};
|
|
354
362
|
|
|
355
363
|
return {
|
|
@@ -357,13 +365,13 @@ export const businessHoursChecker = new PreProcessor({
|
|
|
357
365
|
modifiedMessage: [noticeMessage, ...messages],
|
|
358
366
|
metadata: {
|
|
359
367
|
outsideBusinessHours: true,
|
|
360
|
-
timestamp: now.toISOString()
|
|
361
|
-
}
|
|
368
|
+
timestamp: now.toISOString(),
|
|
369
|
+
},
|
|
362
370
|
};
|
|
363
371
|
}
|
|
364
372
|
|
|
365
373
|
return { action: 'proceed' };
|
|
366
|
-
}
|
|
374
|
+
},
|
|
367
375
|
});
|
|
368
376
|
```
|
|
369
377
|
|
|
@@ -379,7 +387,7 @@ export const languageTranslator = new PreProcessor({
|
|
|
379
387
|
async: true,
|
|
380
388
|
|
|
381
389
|
execute: async (user, messages, channel) => {
|
|
382
|
-
const textMessages = messages.filter(m => m.type === 'text');
|
|
390
|
+
const textMessages = messages.filter((m) => m.type === 'text');
|
|
383
391
|
|
|
384
392
|
if (textMessages.length === 0) {
|
|
385
393
|
return { action: 'proceed' };
|
|
@@ -403,7 +411,7 @@ export const languageTranslator = new PreProcessor({
|
|
|
403
411
|
);
|
|
404
412
|
|
|
405
413
|
// Replace with translated text
|
|
406
|
-
const translatedMessages = messages.map(msg => {
|
|
414
|
+
const translatedMessages = messages.map((msg) => {
|
|
407
415
|
if (msg.type === 'text') {
|
|
408
416
|
return { type: 'text' as const, text: translation };
|
|
409
417
|
}
|
|
@@ -415,13 +423,13 @@ export const languageTranslator = new PreProcessor({
|
|
|
415
423
|
modifiedMessage: translatedMessages,
|
|
416
424
|
metadata: {
|
|
417
425
|
originalLanguage: language,
|
|
418
|
-
translated: true
|
|
419
|
-
}
|
|
426
|
+
translated: true,
|
|
427
|
+
},
|
|
420
428
|
};
|
|
421
429
|
}
|
|
422
430
|
|
|
423
431
|
return { action: 'proceed' };
|
|
424
|
-
}
|
|
432
|
+
},
|
|
425
433
|
});
|
|
426
434
|
```
|
|
427
435
|
|
|
@@ -448,7 +456,7 @@ export const priorityRouter = new PreProcessor({
|
|
|
448
456
|
// Mark as high priority
|
|
449
457
|
const priorityMessage = {
|
|
450
458
|
type: 'text' as const,
|
|
451
|
-
text: `[HIGH PRIORITY] ${message.text}
|
|
459
|
+
text: `[HIGH PRIORITY] ${message.text}`,
|
|
452
460
|
};
|
|
453
461
|
|
|
454
462
|
return {
|
|
@@ -457,8 +465,8 @@ export const priorityRouter = new PreProcessor({
|
|
|
457
465
|
metadata: {
|
|
458
466
|
priority: 'high',
|
|
459
467
|
keyword,
|
|
460
|
-
flagged: true
|
|
461
|
-
}
|
|
468
|
+
flagged: true,
|
|
469
|
+
},
|
|
462
470
|
};
|
|
463
471
|
}
|
|
464
472
|
}
|
|
@@ -468,10 +476,10 @@ export const priorityRouter = new PreProcessor({
|
|
|
468
476
|
return {
|
|
469
477
|
action: 'proceed',
|
|
470
478
|
metadata: {
|
|
471
|
-
priority: 'normal'
|
|
472
|
-
}
|
|
479
|
+
priority: 'normal',
|
|
480
|
+
},
|
|
473
481
|
};
|
|
474
|
-
}
|
|
482
|
+
},
|
|
475
483
|
});
|
|
476
484
|
```
|
|
477
485
|
|
|
@@ -493,11 +501,13 @@ new PreProcessor({ priority: 50, ... });
|
|
|
493
501
|
## Async vs Sync
|
|
494
502
|
|
|
495
503
|
### Synchronous (default)
|
|
504
|
+
|
|
496
505
|
- Blocks message processing until complete
|
|
497
506
|
- Best for fast operations (< 100ms)
|
|
498
507
|
- Use for filtering, validation, simple transforms
|
|
499
508
|
|
|
500
509
|
### Asynchronous
|
|
510
|
+
|
|
501
511
|
- Runs in background, doesn't block
|
|
502
512
|
- Best for slow operations (API calls, AI)
|
|
503
513
|
- Set `async: true` in config
|
|
@@ -568,13 +578,13 @@ export const safePreProcessor = new PreProcessor({
|
|
|
568
578
|
await Data.create('preprocessor_errors', {
|
|
569
579
|
error: error.message,
|
|
570
580
|
userId: user.id,
|
|
571
|
-
timestamp: new Date().toISOString()
|
|
581
|
+
timestamp: new Date().toISOString(),
|
|
572
582
|
});
|
|
573
583
|
|
|
574
584
|
// Proceed anyway (fail open)
|
|
575
585
|
return { action: 'proceed' };
|
|
576
586
|
}
|
|
577
|
-
}
|
|
587
|
+
},
|
|
578
588
|
});
|
|
579
589
|
```
|
|
580
590
|
|
|
@@ -586,7 +596,7 @@ export const safePreProcessor = new PreProcessor({
|
|
|
586
596
|
if (shouldBlock) {
|
|
587
597
|
return {
|
|
588
598
|
action: 'block',
|
|
589
|
-
response: 'Message blocked'
|
|
599
|
+
response: 'Message blocked',
|
|
590
600
|
};
|
|
591
601
|
}
|
|
592
602
|
return { action: 'proceed' };
|
|
@@ -595,7 +605,7 @@ return { action: 'proceed' };
|
|
|
595
605
|
### Message Modification
|
|
596
606
|
|
|
597
607
|
```typescript
|
|
598
|
-
const modifiedMessages = messages.map(msg => {
|
|
608
|
+
const modifiedMessages = messages.map((msg) => {
|
|
599
609
|
if (msg.type === 'text') {
|
|
600
610
|
return { type: 'text', text: msg.text.toUpperCase() };
|
|
601
611
|
}
|
|
@@ -604,7 +614,7 @@ const modifiedMessages = messages.map(msg => {
|
|
|
604
614
|
|
|
605
615
|
return {
|
|
606
616
|
action: 'proceed',
|
|
607
|
-
modifiedMessage: modifiedMessages
|
|
617
|
+
modifiedMessage: modifiedMessages,
|
|
608
618
|
};
|
|
609
619
|
```
|
|
610
620
|
|
|
@@ -614,7 +624,7 @@ return {
|
|
|
614
624
|
const contextMsg = { type: 'text' as const, text: '[Context info]' };
|
|
615
625
|
return {
|
|
616
626
|
action: 'proceed',
|
|
617
|
-
modifiedMessage: [contextMsg, ...messages]
|
|
627
|
+
modifiedMessage: [contextMsg, ...messages],
|
|
618
628
|
};
|
|
619
629
|
```
|
|
620
630
|
|
package/docs/api/Threads.md
CHANGED
|
@@ -120,3 +120,17 @@ await Workflows.start('refund', input, { originThreadId: threadId, callback: tru
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
This works only for the run's own user. A user-less, agent-level run cannot target a home thread yet.
|
|
123
|
+
|
|
124
|
+
## Threads.resetContext
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
// In a tool, after a checklist section is recorded:
|
|
128
|
+
await Threads.resetContext(); // { status: 'reset' } or { status: 'skipped', reason }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Starts the agent's context afresh from the current turn. From the next turn on, the agent reads this turn and what follows, not the earlier messages or the observation log built from them. The person still sees the whole conversation, and nothing is deleted.
|
|
132
|
+
|
|
133
|
+
- **Where.** Only from a tool or processor of a 1:1 chat turn. A job, webhook, trigger, room turn or workflow step gets `skipped: not_chat_turn`.
|
|
134
|
+
- **When it applies.** The agent's `contextReset` feature must be on, and the agent must use observational memory (the default). The same feature also resets once a day: the first turn after 04:00 in the person's timezone drops everything from earlier days.
|
|
135
|
+
- **Never throws.** A reset is advisory. It answers `skipped` with a reason when the feature is off (`disabled`), memory is off (`memory_off`), nothing older is left (`nothing_to_reset`), memory was busy (`busy`; the next reset will apply), or offline in `lua test` / `lua dev` (`unavailable`).
|
|
136
|
+
- **Give the agent what it still needs.** After a reset the agent no longer sees earlier turns. Put where the person is in a task in your own data and add it to each turn, for example in a preprocessor.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lua-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.56.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",
|