lua-cli 3.54.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 +205 -111
- package/dist/api-exports.js +397 -112
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +1144 -310
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +100 -94
- package/dist/workflow-builder.d.ts +7 -4
- package/dist/workflow-builder.js +322 -71
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +3 -3
- package/docs/api/Data.md +19 -4
- package/docs/api/PreProcessor.md +66 -56
- package/docs/api/Threads.md +14 -0
- package/package.json +2 -1
- package/template/package.json +1 -1
- package/template-web-app/web/package.json +2 -3
- package/template-web-app/web/src/index.css +0 -2
- package/template-web-app/web/src/main.tsx +1 -9
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/Data.md
CHANGED
|
@@ -93,7 +93,7 @@ for (const movie of results) {
|
|
|
93
93
|
}
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
### `Data.create(collectionName, data,
|
|
96
|
+
### `Data.create(collectionName, data, searchTextOrOptions?)`
|
|
97
97
|
|
|
98
98
|
Creates a new entry in a collection.
|
|
99
99
|
|
|
@@ -101,7 +101,14 @@ Creates a new entry in a collection.
|
|
|
101
101
|
|
|
102
102
|
- `collectionName` (string) - Collection name
|
|
103
103
|
- `data` (object) - Data to store
|
|
104
|
-
- `
|
|
104
|
+
- `searchTextOrOptions` (string or `{ searchText?, index? }`, optional) - A bare string is the text to use for
|
|
105
|
+
vector search indexing. The options object adds `index`: the `data` fields you filter on, as a path
|
|
106
|
+
(`'business_id'`) or a compound of up to 2 paths (`['country', 'business_id']`); at most 3 per call and 5 built
|
|
107
|
+
per agent (per shared collection on `Data.shared`). The platform builds each index within minutes, reports it in
|
|
108
|
+
`collections()`, and drops it 14 days after the last declaration or filtered read. The same in `lua test`,
|
|
109
|
+
deployed agents (skills, jobs, webhooks, workflow steps other than `tier: 'job'`, which have no `Data`) and voice
|
|
110
|
+
hooks. `Data.update` (4th argument),
|
|
111
|
+
`Data.patch` (`mutation.index`), `entry.update` and `entry.save` accept it too.
|
|
105
112
|
|
|
106
113
|
**Returns:** `Promise<DataEntryInstance>`
|
|
107
114
|
|
|
@@ -309,6 +316,14 @@ const names = await map.collections();
|
|
|
309
316
|
can only ever reach its own organization's shared collections.
|
|
310
317
|
- **Audit.** Every write records the agent, the collection and the entry id (never the value) in the
|
|
311
318
|
organization's audit log.
|
|
319
|
+
- **Managed indexes.** A writer declares them like on `Data`, on its writes:
|
|
320
|
+
`map.create('api_endpoints', row, { index: [['repoKey', 'service']] })` (also `update`, `patch`, `entry.update`).
|
|
321
|
+
The index belongs to the shared collection, not to the agent that declared it: at most 5 are built across all its
|
|
322
|
+
sub-collections (`'per-shared-collection cap (5) reached'` beyond that), they serve every reader's filtered `get`,
|
|
323
|
+
and they are dropped 14 days after the last declaration or filtered read by any agent. `collections()` reports
|
|
324
|
+
each one's status (`pending`, `building`, `ready`, `failed`, `rejected`) to readers and writers. Readers cannot
|
|
325
|
+
declare (they cannot write). Declare compound indexes so their first field serves the single-field filters too:
|
|
326
|
+
`[repoKey, service]` already serves a filter on `repoKey` alone in the same sub-collection.
|
|
312
327
|
- **Test sessions** may read a shared collection but not write it (`shared_data_test_session_write_refused`, 409).
|
|
313
328
|
- **Local runs** (`lua test`) have no org ACL to consult: every call rejects with
|
|
314
329
|
`code === 'shared_data_unavailable_offline'`.
|
|
@@ -370,7 +385,7 @@ A change applies to the next `Data.shared` call. Re-creating a deleted name star
|
|
|
370
385
|
|
|
371
386
|
Data entries have convenient methods:
|
|
372
387
|
|
|
373
|
-
### `entry.update(data,
|
|
388
|
+
### `entry.update(data, searchTextOrOptions?)`
|
|
374
389
|
|
|
375
390
|
Updates this entry.
|
|
376
391
|
|
|
@@ -401,7 +416,7 @@ if (oldMovie) {
|
|
|
401
416
|
}
|
|
402
417
|
```
|
|
403
418
|
|
|
404
|
-
### `entry.save(
|
|
419
|
+
### `entry.save(searchTextOrOptions?)`
|
|
405
420
|
|
|
406
421
|
Saves modifications to this entry.
|
|
407
422
|
|
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",
|
|
@@ -114,6 +114,7 @@
|
|
|
114
114
|
"stripe": "^19.2.0",
|
|
115
115
|
"ts-node": "^10.9.2",
|
|
116
116
|
"tsup": "^8.5.1",
|
|
117
|
+
"tsx": "4.21.0",
|
|
117
118
|
"@lua/sandbox-runtime": "0.0.1",
|
|
118
119
|
"@lua/shared-sandbox": "0.0.1",
|
|
119
120
|
"@lua/shared-source-sync": "0.0.1",
|
package/template/package.json
CHANGED
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@base-ui/react": "~1.8.0",
|
|
11
|
-
"@lua-ai-global/app-
|
|
12
|
-
"@lua-ai-global/
|
|
13
|
-
"@lua-ai-global/ui": "~0.2.3",
|
|
11
|
+
"@lua-ai-global/app-client": "~0.1.0",
|
|
12
|
+
"@lua-ai-global/ui": "~0.3.0",
|
|
14
13
|
"@phosphor-icons/react": "~2.1.10",
|
|
15
14
|
"react": "~19.3.0",
|
|
16
15
|
"react-dom": "~19.3.0"
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
@import '@lua-ai-global/ui/styles.css';
|
|
2
|
-
/* The agent pane's styles: after the design system's, on its tokens. */
|
|
3
|
-
@import '@lua-ai-global/app-chat/styles.css';
|
|
4
2
|
/* Tailwind scans the design system's compiled components for the classes they use. */
|
|
5
3
|
@source '../node_modules/@lua-ai-global/ui/dist';
|
|
@@ -2,19 +2,11 @@ import { StrictMode } from 'react';
|
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { App } from './App';
|
|
4
4
|
import { lua } from '@lua-ai-global/app-client';
|
|
5
|
-
import { AgentPaneLayout } from '@lua-ai-global/app-chat';
|
|
6
5
|
import './index.css';
|
|
7
6
|
|
|
8
|
-
// Theme follows the shell: first from the bootstrap, then from the host's `init`.
|
|
9
|
-
document.documentElement.classList.toggle('dark', lua.theme === 'dark');
|
|
10
|
-
lua.onInit((init) => document.documentElement.classList.toggle('dark', init.theme === 'dark'));
|
|
11
|
-
|
|
12
7
|
createRoot(document.getElementById('root')!).render(
|
|
13
8
|
<StrictMode>
|
|
14
|
-
|
|
15
|
-
<AgentPaneLayout>
|
|
16
|
-
<App />
|
|
17
|
-
</AgentPaneLayout>
|
|
9
|
+
<App />
|
|
18
10
|
</StrictMode>
|
|
19
11
|
);
|
|
20
12
|
void lua.start();
|