llmist 1.1.0 → 1.3.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/README.md +3 -58
- package/dist/{chunk-VXPZQZF5.js → chunk-RZTAKIDE.js} +1279 -360
- package/dist/chunk-RZTAKIDE.js.map +1 -0
- package/dist/{chunk-OIPLYP7M.js → chunk-TFIKR2RK.js} +459 -3
- package/dist/chunk-TFIKR2RK.js.map +1 -0
- package/dist/cli.cjs +1316 -393
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +49 -22
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1446 -362
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +360 -32
- package/dist/index.d.ts +360 -32
- package/dist/index.js +177 -2
- package/dist/index.js.map +1 -1
- package/dist/{mock-stream-DKF5yatf.d.cts → mock-stream-DNt-HBTn.d.cts} +525 -79
- package/dist/{mock-stream-DKF5yatf.d.ts → mock-stream-DNt-HBTn.d.ts} +525 -79
- package/dist/testing/index.cjs +1739 -362
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +437 -3
- package/dist/testing/index.d.ts +437 -3
- package/dist/testing/index.js +54 -4
- package/package.json +1 -1
- package/dist/chunk-OIPLYP7M.js.map +0 -1
- package/dist/chunk-VXPZQZF5.js.map +0 -1
package/dist/testing/index.cjs
CHANGED
|
@@ -1112,6 +1112,417 @@ var init_output_viewer = __esm({
|
|
|
1112
1112
|
}
|
|
1113
1113
|
});
|
|
1114
1114
|
|
|
1115
|
+
// src/agent/compaction/config.ts
|
|
1116
|
+
function resolveCompactionConfig(config = {}) {
|
|
1117
|
+
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
1118
|
+
const target = config.targetPercent ?? DEFAULT_COMPACTION_CONFIG.targetPercent;
|
|
1119
|
+
if (target >= trigger) {
|
|
1120
|
+
console.warn(
|
|
1121
|
+
`[llmist/compaction] targetPercent (${target}) should be less than triggerThresholdPercent (${trigger}) to be effective.`
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
const strategy = config.strategy ?? DEFAULT_COMPACTION_CONFIG.strategy;
|
|
1125
|
+
const strategyName = typeof strategy === "object" && "name" in strategy ? strategy.name : strategy;
|
|
1126
|
+
return {
|
|
1127
|
+
enabled: config.enabled ?? DEFAULT_COMPACTION_CONFIG.enabled,
|
|
1128
|
+
strategy: strategyName,
|
|
1129
|
+
triggerThresholdPercent: trigger,
|
|
1130
|
+
targetPercent: target,
|
|
1131
|
+
preserveRecentTurns: config.preserveRecentTurns ?? DEFAULT_COMPACTION_CONFIG.preserveRecentTurns,
|
|
1132
|
+
summarizationModel: config.summarizationModel,
|
|
1133
|
+
summarizationPrompt: config.summarizationPrompt ?? DEFAULT_SUMMARIZATION_PROMPT,
|
|
1134
|
+
onCompaction: config.onCompaction
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
var DEFAULT_COMPACTION_CONFIG, DEFAULT_SUMMARIZATION_PROMPT;
|
|
1138
|
+
var init_config = __esm({
|
|
1139
|
+
"src/agent/compaction/config.ts"() {
|
|
1140
|
+
"use strict";
|
|
1141
|
+
DEFAULT_COMPACTION_CONFIG = {
|
|
1142
|
+
enabled: true,
|
|
1143
|
+
strategy: "hybrid",
|
|
1144
|
+
triggerThresholdPercent: 80,
|
|
1145
|
+
targetPercent: 50,
|
|
1146
|
+
preserveRecentTurns: 5
|
|
1147
|
+
};
|
|
1148
|
+
DEFAULT_SUMMARIZATION_PROMPT = `Summarize this conversation history concisely, preserving:
|
|
1149
|
+
1. Key decisions made and their rationale
|
|
1150
|
+
2. Important facts and data discovered
|
|
1151
|
+
3. Errors encountered and how they were resolved
|
|
1152
|
+
4. Current task context and goals
|
|
1153
|
+
|
|
1154
|
+
Format as a brief narrative paragraph, not bullet points.
|
|
1155
|
+
Previous conversation:`;
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
// src/agent/compaction/strategy.ts
|
|
1160
|
+
function groupIntoTurns(messages) {
|
|
1161
|
+
const turns = [];
|
|
1162
|
+
let currentTurn = [];
|
|
1163
|
+
for (const msg of messages) {
|
|
1164
|
+
if (msg.role === "user" && currentTurn.length > 0) {
|
|
1165
|
+
turns.push({
|
|
1166
|
+
messages: currentTurn,
|
|
1167
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1168
|
+
});
|
|
1169
|
+
currentTurn = [msg];
|
|
1170
|
+
} else {
|
|
1171
|
+
currentTurn.push(msg);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (currentTurn.length > 0) {
|
|
1175
|
+
turns.push({
|
|
1176
|
+
messages: currentTurn,
|
|
1177
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
return turns;
|
|
1181
|
+
}
|
|
1182
|
+
function estimateTurnTokens(messages) {
|
|
1183
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
1184
|
+
}
|
|
1185
|
+
function flattenTurns(turns) {
|
|
1186
|
+
return turns.flatMap((turn) => turn.messages);
|
|
1187
|
+
}
|
|
1188
|
+
var init_strategy = __esm({
|
|
1189
|
+
"src/agent/compaction/strategy.ts"() {
|
|
1190
|
+
"use strict";
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
|
|
1194
|
+
// src/agent/compaction/strategies/sliding-window.ts
|
|
1195
|
+
var TRUNCATION_MARKER_TEMPLATE, SlidingWindowStrategy;
|
|
1196
|
+
var init_sliding_window = __esm({
|
|
1197
|
+
"src/agent/compaction/strategies/sliding-window.ts"() {
|
|
1198
|
+
"use strict";
|
|
1199
|
+
init_strategy();
|
|
1200
|
+
TRUNCATION_MARKER_TEMPLATE = "[Previous conversation truncated. Removed {count} turn(s) to fit context window.]";
|
|
1201
|
+
SlidingWindowStrategy = class {
|
|
1202
|
+
name = "sliding-window";
|
|
1203
|
+
async compact(messages, config, context) {
|
|
1204
|
+
const turns = groupIntoTurns(messages);
|
|
1205
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1206
|
+
if (turns.length <= preserveCount) {
|
|
1207
|
+
return {
|
|
1208
|
+
messages,
|
|
1209
|
+
strategyName: this.name,
|
|
1210
|
+
metadata: {
|
|
1211
|
+
originalCount: messages.length,
|
|
1212
|
+
compactedCount: messages.length,
|
|
1213
|
+
tokensBefore: context.currentTokens,
|
|
1214
|
+
tokensAfter: context.currentTokens
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1219
|
+
const turnsRemoved = turns.length - preserveCount;
|
|
1220
|
+
const truncationMarker = {
|
|
1221
|
+
role: "user",
|
|
1222
|
+
content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
|
|
1223
|
+
};
|
|
1224
|
+
const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
|
|
1225
|
+
const tokensAfter = Math.ceil(
|
|
1226
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1227
|
+
);
|
|
1228
|
+
return {
|
|
1229
|
+
messages: compactedMessages,
|
|
1230
|
+
strategyName: this.name,
|
|
1231
|
+
metadata: {
|
|
1232
|
+
originalCount: messages.length,
|
|
1233
|
+
compactedCount: compactedMessages.length,
|
|
1234
|
+
tokensBefore: context.currentTokens,
|
|
1235
|
+
tokensAfter
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
|
|
1243
|
+
// src/agent/compaction/strategies/summarization.ts
|
|
1244
|
+
var SummarizationStrategy;
|
|
1245
|
+
var init_summarization = __esm({
|
|
1246
|
+
"src/agent/compaction/strategies/summarization.ts"() {
|
|
1247
|
+
"use strict";
|
|
1248
|
+
init_strategy();
|
|
1249
|
+
SummarizationStrategy = class {
|
|
1250
|
+
name = "summarization";
|
|
1251
|
+
async compact(messages, config, context) {
|
|
1252
|
+
const turns = groupIntoTurns(messages);
|
|
1253
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1254
|
+
if (turns.length <= preserveCount) {
|
|
1255
|
+
return {
|
|
1256
|
+
messages,
|
|
1257
|
+
strategyName: this.name,
|
|
1258
|
+
metadata: {
|
|
1259
|
+
originalCount: messages.length,
|
|
1260
|
+
compactedCount: messages.length,
|
|
1261
|
+
tokensBefore: context.currentTokens,
|
|
1262
|
+
tokensAfter: context.currentTokens
|
|
1263
|
+
}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
const turnsToSummarize = turns.slice(0, -preserveCount);
|
|
1267
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1268
|
+
const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
|
|
1269
|
+
const summary = await this.generateSummary(conversationToSummarize, config, context);
|
|
1270
|
+
const summaryMessage = {
|
|
1271
|
+
role: "user",
|
|
1272
|
+
content: `[Previous conversation summary]
|
|
1273
|
+
${summary}
|
|
1274
|
+
[End of summary - conversation continues below]`
|
|
1275
|
+
};
|
|
1276
|
+
const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
|
|
1277
|
+
const tokensAfter = Math.ceil(
|
|
1278
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1279
|
+
);
|
|
1280
|
+
return {
|
|
1281
|
+
messages: compactedMessages,
|
|
1282
|
+
summary,
|
|
1283
|
+
strategyName: this.name,
|
|
1284
|
+
metadata: {
|
|
1285
|
+
originalCount: messages.length,
|
|
1286
|
+
compactedCount: compactedMessages.length,
|
|
1287
|
+
tokensBefore: context.currentTokens,
|
|
1288
|
+
tokensAfter
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Formats messages into a readable conversation format for summarization.
|
|
1294
|
+
*/
|
|
1295
|
+
formatTurnsForSummary(messages) {
|
|
1296
|
+
return messages.map((msg) => {
|
|
1297
|
+
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
1298
|
+
return `${role}: ${msg.content}`;
|
|
1299
|
+
}).join("\n\n");
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Generates a summary using the configured LLM.
|
|
1303
|
+
*/
|
|
1304
|
+
async generateSummary(conversation, config, context) {
|
|
1305
|
+
const model = config.summarizationModel ?? context.model;
|
|
1306
|
+
const prompt = `${config.summarizationPrompt}
|
|
1307
|
+
|
|
1308
|
+
${conversation}`;
|
|
1309
|
+
const response = await context.client.complete(prompt, {
|
|
1310
|
+
model,
|
|
1311
|
+
temperature: 0.3
|
|
1312
|
+
// Low temperature for factual summarization
|
|
1313
|
+
});
|
|
1314
|
+
return response.trim();
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
// src/agent/compaction/strategies/hybrid.ts
|
|
1321
|
+
var MIN_TURNS_FOR_SUMMARIZATION, HybridStrategy;
|
|
1322
|
+
var init_hybrid = __esm({
|
|
1323
|
+
"src/agent/compaction/strategies/hybrid.ts"() {
|
|
1324
|
+
"use strict";
|
|
1325
|
+
init_strategy();
|
|
1326
|
+
init_sliding_window();
|
|
1327
|
+
init_summarization();
|
|
1328
|
+
MIN_TURNS_FOR_SUMMARIZATION = 3;
|
|
1329
|
+
HybridStrategy = class {
|
|
1330
|
+
name = "hybrid";
|
|
1331
|
+
slidingWindow = new SlidingWindowStrategy();
|
|
1332
|
+
summarization = new SummarizationStrategy();
|
|
1333
|
+
async compact(messages, config, context) {
|
|
1334
|
+
const turns = groupIntoTurns(messages);
|
|
1335
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1336
|
+
if (turns.length <= preserveCount) {
|
|
1337
|
+
return {
|
|
1338
|
+
messages,
|
|
1339
|
+
strategyName: this.name,
|
|
1340
|
+
metadata: {
|
|
1341
|
+
originalCount: messages.length,
|
|
1342
|
+
compactedCount: messages.length,
|
|
1343
|
+
tokensBefore: context.currentTokens,
|
|
1344
|
+
tokensAfter: context.currentTokens
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
const turnsToSummarize = turns.length - preserveCount;
|
|
1349
|
+
if (turnsToSummarize < MIN_TURNS_FOR_SUMMARIZATION) {
|
|
1350
|
+
return this.slidingWindow.compact(messages, config, context);
|
|
1351
|
+
}
|
|
1352
|
+
return this.summarization.compact(messages, config, context);
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
// src/agent/compaction/strategies/index.ts
|
|
1359
|
+
var init_strategies = __esm({
|
|
1360
|
+
"src/agent/compaction/strategies/index.ts"() {
|
|
1361
|
+
"use strict";
|
|
1362
|
+
init_sliding_window();
|
|
1363
|
+
init_summarization();
|
|
1364
|
+
init_hybrid();
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
// src/agent/compaction/manager.ts
|
|
1369
|
+
function createStrategy(name) {
|
|
1370
|
+
switch (name) {
|
|
1371
|
+
case "sliding-window":
|
|
1372
|
+
return new SlidingWindowStrategy();
|
|
1373
|
+
case "summarization":
|
|
1374
|
+
return new SummarizationStrategy();
|
|
1375
|
+
case "hybrid":
|
|
1376
|
+
return new HybridStrategy();
|
|
1377
|
+
default:
|
|
1378
|
+
throw new Error(`Unknown compaction strategy: ${name}`);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
var CompactionManager;
|
|
1382
|
+
var init_manager = __esm({
|
|
1383
|
+
"src/agent/compaction/manager.ts"() {
|
|
1384
|
+
"use strict";
|
|
1385
|
+
init_config();
|
|
1386
|
+
init_strategies();
|
|
1387
|
+
CompactionManager = class {
|
|
1388
|
+
client;
|
|
1389
|
+
model;
|
|
1390
|
+
config;
|
|
1391
|
+
strategy;
|
|
1392
|
+
modelLimits;
|
|
1393
|
+
// Statistics
|
|
1394
|
+
totalCompactions = 0;
|
|
1395
|
+
totalTokensSaved = 0;
|
|
1396
|
+
lastTokenCount = 0;
|
|
1397
|
+
constructor(client, model, config = {}) {
|
|
1398
|
+
this.client = client;
|
|
1399
|
+
this.model = model;
|
|
1400
|
+
this.config = resolveCompactionConfig(config);
|
|
1401
|
+
if (typeof config.strategy === "object" && "compact" in config.strategy) {
|
|
1402
|
+
this.strategy = config.strategy;
|
|
1403
|
+
} else {
|
|
1404
|
+
this.strategy = createStrategy(this.config.strategy);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Check if compaction is needed and perform it if so.
|
|
1409
|
+
*
|
|
1410
|
+
* @param conversation - The conversation manager to compact
|
|
1411
|
+
* @param iteration - Current agent iteration (for event metadata)
|
|
1412
|
+
* @returns CompactionEvent if compaction was performed, null otherwise
|
|
1413
|
+
*/
|
|
1414
|
+
async checkAndCompact(conversation, iteration) {
|
|
1415
|
+
if (!this.config.enabled) {
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
if (!this.modelLimits) {
|
|
1419
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1420
|
+
if (!this.modelLimits) {
|
|
1421
|
+
return null;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
if (!this.client.countTokens) {
|
|
1425
|
+
return null;
|
|
1426
|
+
}
|
|
1427
|
+
const messages = conversation.getMessages();
|
|
1428
|
+
const currentTokens = await this.client.countTokens(this.model, messages);
|
|
1429
|
+
this.lastTokenCount = currentTokens;
|
|
1430
|
+
const usagePercent = currentTokens / this.modelLimits.contextWindow * 100;
|
|
1431
|
+
if (usagePercent < this.config.triggerThresholdPercent) {
|
|
1432
|
+
return null;
|
|
1433
|
+
}
|
|
1434
|
+
const historyMessages = conversation.getHistoryMessages();
|
|
1435
|
+
const baseMessages = conversation.getBaseMessages();
|
|
1436
|
+
const historyTokens = await this.client.countTokens(this.model, historyMessages);
|
|
1437
|
+
const baseTokens = await this.client.countTokens(this.model, baseMessages);
|
|
1438
|
+
return this.compact(conversation, iteration, {
|
|
1439
|
+
historyMessages,
|
|
1440
|
+
baseMessages,
|
|
1441
|
+
historyTokens,
|
|
1442
|
+
baseTokens,
|
|
1443
|
+
currentTokens: historyTokens + baseTokens
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Force compaction regardless of threshold.
|
|
1448
|
+
*
|
|
1449
|
+
* @param conversation - The conversation manager to compact
|
|
1450
|
+
* @param iteration - Current agent iteration (for event metadata). Use -1 for manual compaction.
|
|
1451
|
+
* @param precomputed - Optional pre-computed token counts (passed from checkAndCompact for efficiency)
|
|
1452
|
+
* @returns CompactionEvent with compaction details
|
|
1453
|
+
*/
|
|
1454
|
+
async compact(conversation, iteration, precomputed) {
|
|
1455
|
+
if (!this.modelLimits) {
|
|
1456
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1457
|
+
if (!this.modelLimits) {
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
const historyMessages = precomputed?.historyMessages ?? conversation.getHistoryMessages();
|
|
1462
|
+
const baseMessages = precomputed?.baseMessages ?? conversation.getBaseMessages();
|
|
1463
|
+
const historyTokens = precomputed?.historyTokens ?? await this.client.countTokens(this.model, historyMessages);
|
|
1464
|
+
const baseTokens = precomputed?.baseTokens ?? await this.client.countTokens(this.model, baseMessages);
|
|
1465
|
+
const currentTokens = precomputed?.currentTokens ?? historyTokens + baseTokens;
|
|
1466
|
+
const targetTotalTokens = Math.floor(
|
|
1467
|
+
this.modelLimits.contextWindow * this.config.targetPercent / 100
|
|
1468
|
+
);
|
|
1469
|
+
const targetHistoryTokens = Math.max(0, targetTotalTokens - baseTokens);
|
|
1470
|
+
const result = await this.strategy.compact(historyMessages, this.config, {
|
|
1471
|
+
currentTokens: historyTokens,
|
|
1472
|
+
targetTokens: targetHistoryTokens,
|
|
1473
|
+
modelLimits: this.modelLimits,
|
|
1474
|
+
client: this.client,
|
|
1475
|
+
model: this.config.summarizationModel ?? this.model
|
|
1476
|
+
});
|
|
1477
|
+
conversation.replaceHistory(result.messages);
|
|
1478
|
+
const afterTokens = await this.client.countTokens(this.model, conversation.getMessages());
|
|
1479
|
+
const tokensSaved = currentTokens - afterTokens;
|
|
1480
|
+
this.totalCompactions++;
|
|
1481
|
+
this.totalTokensSaved += tokensSaved;
|
|
1482
|
+
this.lastTokenCount = afterTokens;
|
|
1483
|
+
const event = {
|
|
1484
|
+
strategy: result.strategyName,
|
|
1485
|
+
tokensBefore: currentTokens,
|
|
1486
|
+
tokensAfter: afterTokens,
|
|
1487
|
+
messagesBefore: historyMessages.length + baseMessages.length,
|
|
1488
|
+
messagesAfter: result.messages.length + baseMessages.length,
|
|
1489
|
+
summary: result.summary,
|
|
1490
|
+
iteration
|
|
1491
|
+
};
|
|
1492
|
+
if (this.config.onCompaction) {
|
|
1493
|
+
try {
|
|
1494
|
+
this.config.onCompaction(event);
|
|
1495
|
+
} catch (err) {
|
|
1496
|
+
console.warn("[llmist/compaction] onCompaction callback error:", err);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return event;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Get compaction statistics.
|
|
1503
|
+
*/
|
|
1504
|
+
getStats() {
|
|
1505
|
+
const contextWindow = this.modelLimits?.contextWindow ?? 0;
|
|
1506
|
+
return {
|
|
1507
|
+
totalCompactions: this.totalCompactions,
|
|
1508
|
+
totalTokensSaved: this.totalTokensSaved,
|
|
1509
|
+
currentUsage: {
|
|
1510
|
+
tokens: this.lastTokenCount,
|
|
1511
|
+
percent: contextWindow > 0 ? this.lastTokenCount / contextWindow * 100 : 0
|
|
1512
|
+
},
|
|
1513
|
+
contextWindow
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Check if compaction is enabled.
|
|
1518
|
+
*/
|
|
1519
|
+
isEnabled() {
|
|
1520
|
+
return this.config.enabled;
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
});
|
|
1525
|
+
|
|
1115
1526
|
// src/agent/gadget-output-store.ts
|
|
1116
1527
|
var import_node_crypto, GadgetOutputStore;
|
|
1117
1528
|
var init_gadget_output_store = __esm({
|
|
@@ -1214,10 +1625,16 @@ var init_conversation_manager = __esm({
|
|
|
1214
1625
|
baseMessages;
|
|
1215
1626
|
initialMessages;
|
|
1216
1627
|
historyBuilder;
|
|
1628
|
+
startPrefix;
|
|
1629
|
+
endPrefix;
|
|
1630
|
+
argPrefix;
|
|
1217
1631
|
constructor(baseMessages, initialMessages, options = {}) {
|
|
1218
1632
|
this.baseMessages = baseMessages;
|
|
1219
1633
|
this.initialMessages = initialMessages;
|
|
1220
1634
|
this.historyBuilder = new LLMMessageBuilder();
|
|
1635
|
+
this.startPrefix = options.startPrefix;
|
|
1636
|
+
this.endPrefix = options.endPrefix;
|
|
1637
|
+
this.argPrefix = options.argPrefix;
|
|
1221
1638
|
if (options.startPrefix && options.endPrefix) {
|
|
1222
1639
|
this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
|
|
1223
1640
|
}
|
|
@@ -1234,6 +1651,25 @@ var init_conversation_manager = __esm({
|
|
|
1234
1651
|
getMessages() {
|
|
1235
1652
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
1236
1653
|
}
|
|
1654
|
+
getHistoryMessages() {
|
|
1655
|
+
return this.historyBuilder.build();
|
|
1656
|
+
}
|
|
1657
|
+
getBaseMessages() {
|
|
1658
|
+
return [...this.baseMessages, ...this.initialMessages];
|
|
1659
|
+
}
|
|
1660
|
+
replaceHistory(newHistory) {
|
|
1661
|
+
this.historyBuilder = new LLMMessageBuilder();
|
|
1662
|
+
if (this.startPrefix && this.endPrefix) {
|
|
1663
|
+
this.historyBuilder.withPrefixes(this.startPrefix, this.endPrefix, this.argPrefix);
|
|
1664
|
+
}
|
|
1665
|
+
for (const msg of newHistory) {
|
|
1666
|
+
if (msg.role === "user") {
|
|
1667
|
+
this.historyBuilder.addUser(msg.content);
|
|
1668
|
+
} else if (msg.role === "assistant") {
|
|
1669
|
+
this.historyBuilder.addAssistant(msg.content);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1237
1673
|
};
|
|
1238
1674
|
}
|
|
1239
1675
|
});
|
|
@@ -1446,334 +1882,241 @@ var init_hook_validators = __esm({
|
|
|
1446
1882
|
}
|
|
1447
1883
|
});
|
|
1448
1884
|
|
|
1449
|
-
// src/gadgets/
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1885
|
+
// src/gadgets/schema-introspector.ts
|
|
1886
|
+
function getDef(schema) {
|
|
1887
|
+
return schema._def;
|
|
1888
|
+
}
|
|
1889
|
+
function getTypeName(schema) {
|
|
1890
|
+
const def = getDef(schema);
|
|
1891
|
+
return def?.type ?? def?.typeName;
|
|
1892
|
+
}
|
|
1893
|
+
function getShape(schema) {
|
|
1894
|
+
const def = getDef(schema);
|
|
1895
|
+
if (typeof def?.shape === "function") {
|
|
1896
|
+
return def.shape();
|
|
1897
|
+
}
|
|
1898
|
+
return def?.shape;
|
|
1899
|
+
}
|
|
1900
|
+
var SchemaIntrospector;
|
|
1901
|
+
var init_schema_introspector = __esm({
|
|
1902
|
+
"src/gadgets/schema-introspector.ts"() {
|
|
1453
1903
|
"use strict";
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
constructor(options = {}) {
|
|
1460
|
-
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1461
|
-
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
1462
|
-
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
1904
|
+
SchemaIntrospector = class {
|
|
1905
|
+
schema;
|
|
1906
|
+
cache = /* @__PURE__ */ new Map();
|
|
1907
|
+
constructor(schema) {
|
|
1908
|
+
this.schema = schema;
|
|
1463
1909
|
}
|
|
1464
1910
|
/**
|
|
1465
|
-
*
|
|
1911
|
+
* Get the expected type at a JSON pointer path.
|
|
1466
1912
|
*
|
|
1467
|
-
* @param
|
|
1468
|
-
* @
|
|
1469
|
-
* @param gadget - The gadget instance (for generating instructions)
|
|
1470
|
-
* @returns Formatted error message with usage instructions
|
|
1913
|
+
* @param pointer - JSON pointer path without leading / (e.g., "config/timeout", "items/0")
|
|
1914
|
+
* @returns Type hint for coercion decision
|
|
1471
1915
|
*/
|
|
1472
|
-
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
const path = issue.path.join(".") || "root";
|
|
1477
|
-
parts.push(` - ${path}: ${issue.message}`);
|
|
1916
|
+
getTypeAtPath(pointer) {
|
|
1917
|
+
const cached = this.cache.get(pointer);
|
|
1918
|
+
if (cached !== void 0) {
|
|
1919
|
+
return cached;
|
|
1478
1920
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
return parts.join("\n");
|
|
1921
|
+
const result = this.resolveTypeAtPath(pointer);
|
|
1922
|
+
this.cache.set(pointer, result);
|
|
1923
|
+
return result;
|
|
1483
1924
|
}
|
|
1484
1925
|
/**
|
|
1485
|
-
*
|
|
1486
|
-
*
|
|
1487
|
-
* @param gadgetName - Name of the gadget that was called
|
|
1488
|
-
* @param parseError - The parse error message
|
|
1489
|
-
* @param gadget - The gadget instance if found (for generating instructions)
|
|
1490
|
-
* @returns Formatted error message with format reference
|
|
1926
|
+
* Internal method to resolve type at path without caching.
|
|
1491
1927
|
*/
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1928
|
+
resolveTypeAtPath(pointer) {
|
|
1929
|
+
if (!pointer) {
|
|
1930
|
+
return this.getBaseType(this.schema);
|
|
1931
|
+
}
|
|
1932
|
+
const segments = pointer.split("/");
|
|
1933
|
+
let current = this.schema;
|
|
1934
|
+
for (const segment of segments) {
|
|
1935
|
+
current = this.unwrapSchema(current);
|
|
1936
|
+
const typeName = getTypeName(current);
|
|
1937
|
+
if (typeName === "object" || typeName === "ZodObject") {
|
|
1938
|
+
const shape = getShape(current);
|
|
1939
|
+
if (!shape || !(segment in shape)) {
|
|
1940
|
+
return "unknown";
|
|
1941
|
+
}
|
|
1942
|
+
current = shape[segment];
|
|
1943
|
+
} else if (typeName === "array" || typeName === "ZodArray") {
|
|
1944
|
+
if (!/^\d+$/.test(segment)) {
|
|
1945
|
+
return "unknown";
|
|
1946
|
+
}
|
|
1947
|
+
const def = getDef(current);
|
|
1948
|
+
const elementType = def?.element ?? def?.type;
|
|
1949
|
+
if (!elementType) {
|
|
1950
|
+
return "unknown";
|
|
1951
|
+
}
|
|
1952
|
+
current = elementType;
|
|
1953
|
+
} else if (typeName === "tuple" || typeName === "ZodTuple") {
|
|
1954
|
+
if (!/^\d+$/.test(segment)) {
|
|
1955
|
+
return "unknown";
|
|
1956
|
+
}
|
|
1957
|
+
const index = parseInt(segment, 10);
|
|
1958
|
+
const def = getDef(current);
|
|
1959
|
+
const items = def?.items;
|
|
1960
|
+
if (!items || index >= items.length) {
|
|
1961
|
+
return "unknown";
|
|
1962
|
+
}
|
|
1963
|
+
current = items[index];
|
|
1964
|
+
} else if (typeName === "record" || typeName === "ZodRecord") {
|
|
1965
|
+
const def = getDef(current);
|
|
1966
|
+
const valueType = def?.valueType;
|
|
1967
|
+
if (!valueType) {
|
|
1968
|
+
return "unknown";
|
|
1969
|
+
}
|
|
1970
|
+
current = valueType;
|
|
1971
|
+
} else {
|
|
1972
|
+
return "unknown";
|
|
1973
|
+
}
|
|
1500
1974
|
}
|
|
1501
|
-
|
|
1502
|
-
parts.push("Block Format Reference:");
|
|
1503
|
-
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
1504
|
-
parts.push(` ${this.argPrefix}parameterName`);
|
|
1505
|
-
parts.push(" parameter value here");
|
|
1506
|
-
parts.push(` ${this.endPrefix}`);
|
|
1507
|
-
return parts.join("\n");
|
|
1975
|
+
return this.getBaseType(current);
|
|
1508
1976
|
}
|
|
1509
1977
|
/**
|
|
1510
|
-
*
|
|
1511
|
-
*
|
|
1512
|
-
* @param gadgetName - Name of the gadget that was not found
|
|
1513
|
-
* @param availableGadgets - List of available gadget names
|
|
1514
|
-
* @returns Formatted error message with available gadgets
|
|
1978
|
+
* Unwrap schema modifiers (optional, default, nullable, branded, etc.)
|
|
1979
|
+
* to get to the underlying type.
|
|
1515
1980
|
*/
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1981
|
+
unwrapSchema(schema) {
|
|
1982
|
+
let current = schema;
|
|
1983
|
+
let iterations = 0;
|
|
1984
|
+
const maxIterations = 20;
|
|
1985
|
+
while (iterations < maxIterations) {
|
|
1986
|
+
const typeName = getTypeName(current);
|
|
1987
|
+
const wrapperTypes = [
|
|
1988
|
+
"optional",
|
|
1989
|
+
"nullable",
|
|
1990
|
+
"default",
|
|
1991
|
+
"catch",
|
|
1992
|
+
"branded",
|
|
1993
|
+
"readonly",
|
|
1994
|
+
"pipeline",
|
|
1995
|
+
"ZodOptional",
|
|
1996
|
+
"ZodNullable",
|
|
1997
|
+
"ZodDefault",
|
|
1998
|
+
"ZodCatch",
|
|
1999
|
+
"ZodBranded",
|
|
2000
|
+
"ZodReadonly",
|
|
2001
|
+
"ZodPipeline"
|
|
2002
|
+
];
|
|
2003
|
+
if (typeName && wrapperTypes.includes(typeName)) {
|
|
2004
|
+
const def = getDef(current);
|
|
2005
|
+
const inner = def?.innerType ?? def?.in ?? def?.type;
|
|
2006
|
+
if (!inner || inner === current) break;
|
|
2007
|
+
current = inner;
|
|
2008
|
+
iterations++;
|
|
2009
|
+
continue;
|
|
2010
|
+
}
|
|
2011
|
+
break;
|
|
1525
2012
|
}
|
|
1526
|
-
return
|
|
1527
|
-
}
|
|
1528
|
-
};
|
|
1529
|
-
}
|
|
1530
|
-
});
|
|
1531
|
-
|
|
1532
|
-
// src/gadgets/exceptions.ts
|
|
1533
|
-
var BreakLoopException, HumanInputException, TimeoutException;
|
|
1534
|
-
var init_exceptions = __esm({
|
|
1535
|
-
"src/gadgets/exceptions.ts"() {
|
|
1536
|
-
"use strict";
|
|
1537
|
-
BreakLoopException = class extends Error {
|
|
1538
|
-
constructor(message) {
|
|
1539
|
-
super(message ?? "Agent loop terminated by gadget");
|
|
1540
|
-
this.name = "BreakLoopException";
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
HumanInputException = class extends Error {
|
|
1544
|
-
question;
|
|
1545
|
-
constructor(question) {
|
|
1546
|
-
super(`Human input required: ${question}`);
|
|
1547
|
-
this.name = "HumanInputException";
|
|
1548
|
-
this.question = question;
|
|
2013
|
+
return current;
|
|
1549
2014
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
return
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
this.logger.debug("Executing gadget with timeout", {
|
|
1656
|
-
gadgetName: call.gadgetName,
|
|
1657
|
-
timeoutMs
|
|
1658
|
-
});
|
|
1659
|
-
result = await Promise.race([
|
|
1660
|
-
Promise.resolve(gadget.execute(validatedParameters)),
|
|
1661
|
-
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
1662
|
-
]);
|
|
1663
|
-
} else {
|
|
1664
|
-
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
1665
|
-
}
|
|
1666
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1667
|
-
this.logger.info("Gadget executed successfully", {
|
|
1668
|
-
gadgetName: call.gadgetName,
|
|
1669
|
-
invocationId: call.invocationId,
|
|
1670
|
-
executionTimeMs
|
|
1671
|
-
});
|
|
1672
|
-
this.logger.debug("Gadget result", {
|
|
1673
|
-
gadgetName: call.gadgetName,
|
|
1674
|
-
invocationId: call.invocationId,
|
|
1675
|
-
parameters: validatedParameters,
|
|
1676
|
-
result,
|
|
1677
|
-
executionTimeMs
|
|
1678
|
-
});
|
|
1679
|
-
return {
|
|
1680
|
-
gadgetName: call.gadgetName,
|
|
1681
|
-
invocationId: call.invocationId,
|
|
1682
|
-
parameters: validatedParameters,
|
|
1683
|
-
result,
|
|
1684
|
-
executionTimeMs
|
|
1685
|
-
};
|
|
1686
|
-
} catch (error) {
|
|
1687
|
-
if (error instanceof BreakLoopException) {
|
|
1688
|
-
this.logger.info("Gadget requested loop termination", {
|
|
1689
|
-
gadgetName: call.gadgetName,
|
|
1690
|
-
message: error.message
|
|
1691
|
-
});
|
|
1692
|
-
return {
|
|
1693
|
-
gadgetName: call.gadgetName,
|
|
1694
|
-
invocationId: call.invocationId,
|
|
1695
|
-
parameters: validatedParameters,
|
|
1696
|
-
result: error.message,
|
|
1697
|
-
breaksLoop: true,
|
|
1698
|
-
executionTimeMs: Date.now() - startTime
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
if (error instanceof TimeoutException) {
|
|
1702
|
-
this.logger.error("Gadget execution timed out", {
|
|
1703
|
-
gadgetName: call.gadgetName,
|
|
1704
|
-
timeoutMs: error.timeoutMs,
|
|
1705
|
-
executionTimeMs: Date.now() - startTime
|
|
1706
|
-
});
|
|
1707
|
-
return {
|
|
1708
|
-
gadgetName: call.gadgetName,
|
|
1709
|
-
invocationId: call.invocationId,
|
|
1710
|
-
parameters: validatedParameters,
|
|
1711
|
-
error: error.message,
|
|
1712
|
-
executionTimeMs: Date.now() - startTime
|
|
1713
|
-
};
|
|
1714
|
-
}
|
|
1715
|
-
if (error instanceof HumanInputException) {
|
|
1716
|
-
this.logger.info("Gadget requested human input", {
|
|
1717
|
-
gadgetName: call.gadgetName,
|
|
1718
|
-
question: error.question
|
|
1719
|
-
});
|
|
1720
|
-
if (this.onHumanInputRequired) {
|
|
1721
|
-
try {
|
|
1722
|
-
const answer = await this.onHumanInputRequired(error.question);
|
|
1723
|
-
this.logger.debug("Human input received", {
|
|
1724
|
-
gadgetName: call.gadgetName,
|
|
1725
|
-
answerLength: answer.length
|
|
1726
|
-
});
|
|
1727
|
-
return {
|
|
1728
|
-
gadgetName: call.gadgetName,
|
|
1729
|
-
invocationId: call.invocationId,
|
|
1730
|
-
parameters: validatedParameters,
|
|
1731
|
-
result: answer,
|
|
1732
|
-
executionTimeMs: Date.now() - startTime
|
|
1733
|
-
};
|
|
1734
|
-
} catch (inputError) {
|
|
1735
|
-
this.logger.error("Human input callback error", {
|
|
1736
|
-
gadgetName: call.gadgetName,
|
|
1737
|
-
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
1738
|
-
});
|
|
1739
|
-
return {
|
|
1740
|
-
gadgetName: call.gadgetName,
|
|
1741
|
-
invocationId: call.invocationId,
|
|
1742
|
-
parameters: validatedParameters,
|
|
1743
|
-
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
1744
|
-
executionTimeMs: Date.now() - startTime
|
|
1745
|
-
};
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
this.logger.warn("Human input required but no callback provided", {
|
|
1749
|
-
gadgetName: call.gadgetName
|
|
1750
|
-
});
|
|
1751
|
-
return {
|
|
1752
|
-
gadgetName: call.gadgetName,
|
|
1753
|
-
invocationId: call.invocationId,
|
|
1754
|
-
parameters: validatedParameters,
|
|
1755
|
-
error: "Human input required but not available (stdin is not interactive)",
|
|
1756
|
-
executionTimeMs: Date.now() - startTime
|
|
1757
|
-
};
|
|
1758
|
-
}
|
|
1759
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1760
|
-
this.logger.error("Gadget execution failed", {
|
|
1761
|
-
gadgetName: call.gadgetName,
|
|
1762
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1763
|
-
executionTimeMs
|
|
1764
|
-
});
|
|
1765
|
-
return {
|
|
1766
|
-
gadgetName: call.gadgetName,
|
|
1767
|
-
invocationId: call.invocationId,
|
|
1768
|
-
parameters: validatedParameters,
|
|
1769
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1770
|
-
executionTimeMs
|
|
1771
|
-
};
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
// Execute multiple gadget calls in parallel
|
|
1775
|
-
async executeAll(calls) {
|
|
1776
|
-
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2015
|
+
/**
|
|
2016
|
+
* Get the primitive type hint from an unwrapped schema.
|
|
2017
|
+
*/
|
|
2018
|
+
getBaseType(schema) {
|
|
2019
|
+
const unwrapped = this.unwrapSchema(schema);
|
|
2020
|
+
const typeName = getTypeName(unwrapped);
|
|
2021
|
+
switch (typeName) {
|
|
2022
|
+
// Primitive types
|
|
2023
|
+
case "string":
|
|
2024
|
+
case "ZodString":
|
|
2025
|
+
return "string";
|
|
2026
|
+
case "number":
|
|
2027
|
+
case "ZodNumber":
|
|
2028
|
+
case "bigint":
|
|
2029
|
+
case "ZodBigInt":
|
|
2030
|
+
return "number";
|
|
2031
|
+
case "boolean":
|
|
2032
|
+
case "ZodBoolean":
|
|
2033
|
+
return "boolean";
|
|
2034
|
+
// Literal types - check the literal value type
|
|
2035
|
+
case "literal":
|
|
2036
|
+
case "ZodLiteral": {
|
|
2037
|
+
const def = getDef(unwrapped);
|
|
2038
|
+
const values = def?.values;
|
|
2039
|
+
const value = values?.[0] ?? def?.value;
|
|
2040
|
+
if (typeof value === "string") return "string";
|
|
2041
|
+
if (typeof value === "number" || typeof value === "bigint")
|
|
2042
|
+
return "number";
|
|
2043
|
+
if (typeof value === "boolean") return "boolean";
|
|
2044
|
+
return "unknown";
|
|
2045
|
+
}
|
|
2046
|
+
// Enum - always string keys
|
|
2047
|
+
case "enum":
|
|
2048
|
+
case "ZodEnum":
|
|
2049
|
+
case "nativeEnum":
|
|
2050
|
+
case "ZodNativeEnum":
|
|
2051
|
+
return "string";
|
|
2052
|
+
// Union - return 'unknown' to let auto-coercion decide
|
|
2053
|
+
// Since multiple types are valid, we can't definitively say what the LLM intended
|
|
2054
|
+
// Auto-coercion will handle common cases (numbers, booleans) appropriately
|
|
2055
|
+
case "union":
|
|
2056
|
+
case "ZodUnion":
|
|
2057
|
+
return "unknown";
|
|
2058
|
+
// Discriminated union - complex, return unknown
|
|
2059
|
+
case "discriminatedUnion":
|
|
2060
|
+
case "ZodDiscriminatedUnion":
|
|
2061
|
+
return "unknown";
|
|
2062
|
+
// Intersection - check both sides
|
|
2063
|
+
case "intersection":
|
|
2064
|
+
case "ZodIntersection": {
|
|
2065
|
+
const def = getDef(unwrapped);
|
|
2066
|
+
const left = def?.left;
|
|
2067
|
+
const right = def?.right;
|
|
2068
|
+
if (!left || !right) return "unknown";
|
|
2069
|
+
const leftType = this.getBaseType(left);
|
|
2070
|
+
const rightType = this.getBaseType(right);
|
|
2071
|
+
if (leftType === rightType) return leftType;
|
|
2072
|
+
if (leftType === "string" || rightType === "string") return "string";
|
|
2073
|
+
return "unknown";
|
|
2074
|
+
}
|
|
2075
|
+
// Effects/transforms - return unknown to let Zod handle it
|
|
2076
|
+
case "effects":
|
|
2077
|
+
case "ZodEffects":
|
|
2078
|
+
return "unknown";
|
|
2079
|
+
// Lazy - can't resolve without evaluating
|
|
2080
|
+
case "lazy":
|
|
2081
|
+
case "ZodLazy":
|
|
2082
|
+
return "unknown";
|
|
2083
|
+
// Complex types - return unknown
|
|
2084
|
+
case "object":
|
|
2085
|
+
case "ZodObject":
|
|
2086
|
+
case "array":
|
|
2087
|
+
case "ZodArray":
|
|
2088
|
+
case "tuple":
|
|
2089
|
+
case "ZodTuple":
|
|
2090
|
+
case "record":
|
|
2091
|
+
case "ZodRecord":
|
|
2092
|
+
case "map":
|
|
2093
|
+
case "ZodMap":
|
|
2094
|
+
case "set":
|
|
2095
|
+
case "ZodSet":
|
|
2096
|
+
case "function":
|
|
2097
|
+
case "ZodFunction":
|
|
2098
|
+
case "promise":
|
|
2099
|
+
case "ZodPromise":
|
|
2100
|
+
case "date":
|
|
2101
|
+
case "ZodDate":
|
|
2102
|
+
return "unknown";
|
|
2103
|
+
// Unknown/any/never/void/undefined/null
|
|
2104
|
+
case "unknown":
|
|
2105
|
+
case "ZodUnknown":
|
|
2106
|
+
case "any":
|
|
2107
|
+
case "ZodAny":
|
|
2108
|
+
case "never":
|
|
2109
|
+
case "ZodNever":
|
|
2110
|
+
case "void":
|
|
2111
|
+
case "ZodVoid":
|
|
2112
|
+
case "undefined":
|
|
2113
|
+
case "ZodUndefined":
|
|
2114
|
+
case "null":
|
|
2115
|
+
case "ZodNull":
|
|
2116
|
+
return "unknown";
|
|
2117
|
+
default:
|
|
2118
|
+
return "unknown";
|
|
2119
|
+
}
|
|
1777
2120
|
}
|
|
1778
2121
|
};
|
|
1779
2122
|
}
|
|
@@ -1784,6 +2127,7 @@ function parseBlockParams(content, options) {
|
|
|
1784
2127
|
const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1785
2128
|
const result = {};
|
|
1786
2129
|
const seenPointers = /* @__PURE__ */ new Set();
|
|
2130
|
+
const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
|
|
1787
2131
|
const parts = content.split(argPrefix);
|
|
1788
2132
|
for (let i = 1; i < parts.length; i++) {
|
|
1789
2133
|
const part = parts[i];
|
|
@@ -1795,7 +2139,7 @@ function parseBlockParams(content, options) {
|
|
|
1795
2139
|
throw new Error(`Duplicate pointer: ${pointer2}`);
|
|
1796
2140
|
}
|
|
1797
2141
|
seenPointers.add(pointer2);
|
|
1798
|
-
setByPointer(result, pointer2, "");
|
|
2142
|
+
setByPointer(result, pointer2, "", introspector);
|
|
1799
2143
|
}
|
|
1800
2144
|
continue;
|
|
1801
2145
|
}
|
|
@@ -1811,15 +2155,30 @@ function parseBlockParams(content, options) {
|
|
|
1811
2155
|
throw new Error(`Duplicate pointer: ${pointer}`);
|
|
1812
2156
|
}
|
|
1813
2157
|
seenPointers.add(pointer);
|
|
1814
|
-
setByPointer(result, pointer, value);
|
|
2158
|
+
setByPointer(result, pointer, value, introspector);
|
|
1815
2159
|
}
|
|
1816
2160
|
return result;
|
|
1817
2161
|
}
|
|
1818
|
-
function coerceValue(value) {
|
|
2162
|
+
function coerceValue(value, expectedType) {
|
|
1819
2163
|
if (value.includes("\n")) {
|
|
1820
2164
|
return value;
|
|
1821
2165
|
}
|
|
1822
2166
|
const trimmed = value.trim();
|
|
2167
|
+
if (expectedType === "string") {
|
|
2168
|
+
return value;
|
|
2169
|
+
}
|
|
2170
|
+
if (expectedType === "boolean") {
|
|
2171
|
+
if (trimmed === "true") return true;
|
|
2172
|
+
if (trimmed === "false") return false;
|
|
2173
|
+
return value;
|
|
2174
|
+
}
|
|
2175
|
+
if (expectedType === "number") {
|
|
2176
|
+
const num = Number(trimmed);
|
|
2177
|
+
if (!isNaN(num) && isFinite(num) && trimmed !== "") {
|
|
2178
|
+
return num;
|
|
2179
|
+
}
|
|
2180
|
+
return value;
|
|
2181
|
+
}
|
|
1823
2182
|
if (trimmed === "true") return true;
|
|
1824
2183
|
if (trimmed === "false") return false;
|
|
1825
2184
|
if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
@@ -1830,7 +2189,7 @@ function coerceValue(value) {
|
|
|
1830
2189
|
}
|
|
1831
2190
|
return value;
|
|
1832
2191
|
}
|
|
1833
|
-
function setByPointer(obj, pointer, value) {
|
|
2192
|
+
function setByPointer(obj, pointer, value, introspector) {
|
|
1834
2193
|
const segments = pointer.split("/");
|
|
1835
2194
|
let current = obj;
|
|
1836
2195
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
@@ -1842,40 +2201,157 @@ function setByPointer(obj, pointer, value) {
|
|
|
1842
2201
|
if (isNaN(index) || index < 0) {
|
|
1843
2202
|
throw new Error(`Invalid array index: ${segment}`);
|
|
1844
2203
|
}
|
|
1845
|
-
if (index > current.length) {
|
|
1846
|
-
throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
|
|
2204
|
+
if (index > current.length) {
|
|
2205
|
+
throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
|
|
2206
|
+
}
|
|
2207
|
+
if (current[index] === void 0) {
|
|
2208
|
+
current[index] = nextIsArrayIndex ? [] : {};
|
|
2209
|
+
}
|
|
2210
|
+
current = current[index];
|
|
2211
|
+
} else {
|
|
2212
|
+
const rec = current;
|
|
2213
|
+
if (rec[segment] === void 0) {
|
|
2214
|
+
rec[segment] = nextIsArrayIndex ? [] : {};
|
|
2215
|
+
}
|
|
2216
|
+
current = rec[segment];
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
const lastSegment = segments[segments.length - 1];
|
|
2220
|
+
const expectedType = introspector?.getTypeAtPath(pointer);
|
|
2221
|
+
const coercedValue = coerceValue(value, expectedType);
|
|
2222
|
+
if (Array.isArray(current)) {
|
|
2223
|
+
const index = parseInt(lastSegment, 10);
|
|
2224
|
+
if (isNaN(index) || index < 0) {
|
|
2225
|
+
throw new Error(`Invalid array index: ${lastSegment}`);
|
|
2226
|
+
}
|
|
2227
|
+
if (index > current.length) {
|
|
2228
|
+
throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
|
|
2229
|
+
}
|
|
2230
|
+
current[index] = coercedValue;
|
|
2231
|
+
} else {
|
|
2232
|
+
current[lastSegment] = coercedValue;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
var init_block_params = __esm({
|
|
2236
|
+
"src/gadgets/block-params.ts"() {
|
|
2237
|
+
"use strict";
|
|
2238
|
+
init_constants();
|
|
2239
|
+
init_schema_introspector();
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
|
|
2243
|
+
// src/gadgets/error-formatter.ts
|
|
2244
|
+
var GadgetErrorFormatter;
|
|
2245
|
+
var init_error_formatter = __esm({
|
|
2246
|
+
"src/gadgets/error-formatter.ts"() {
|
|
2247
|
+
"use strict";
|
|
2248
|
+
init_constants();
|
|
2249
|
+
GadgetErrorFormatter = class {
|
|
2250
|
+
argPrefix;
|
|
2251
|
+
startPrefix;
|
|
2252
|
+
endPrefix;
|
|
2253
|
+
constructor(options = {}) {
|
|
2254
|
+
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2255
|
+
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
2256
|
+
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
2257
|
+
}
|
|
2258
|
+
/**
|
|
2259
|
+
* Format a Zod validation error with full gadget instructions.
|
|
2260
|
+
*
|
|
2261
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2262
|
+
* @param zodError - The Zod validation error
|
|
2263
|
+
* @param gadget - The gadget instance (for generating instructions)
|
|
2264
|
+
* @returns Formatted error message with usage instructions
|
|
2265
|
+
*/
|
|
2266
|
+
formatValidationError(gadgetName, zodError, gadget) {
|
|
2267
|
+
const parts = [];
|
|
2268
|
+
parts.push(`Error: Invalid parameters for '${gadgetName}':`);
|
|
2269
|
+
for (const issue of zodError.issues) {
|
|
2270
|
+
const path = issue.path.join(".") || "root";
|
|
2271
|
+
parts.push(` - ${path}: ${issue.message}`);
|
|
2272
|
+
}
|
|
2273
|
+
parts.push("");
|
|
2274
|
+
parts.push("Gadget Usage:");
|
|
2275
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2276
|
+
return parts.join("\n");
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Format a parse error with block format reference.
|
|
2280
|
+
*
|
|
2281
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2282
|
+
* @param parseError - The parse error message
|
|
2283
|
+
* @param gadget - The gadget instance if found (for generating instructions)
|
|
2284
|
+
* @returns Formatted error message with format reference
|
|
2285
|
+
*/
|
|
2286
|
+
formatParseError(gadgetName, parseError, gadget) {
|
|
2287
|
+
const parts = [];
|
|
2288
|
+
parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
|
|
2289
|
+
parts.push(` ${parseError}`);
|
|
2290
|
+
if (gadget) {
|
|
2291
|
+
parts.push("");
|
|
2292
|
+
parts.push("Gadget Usage:");
|
|
2293
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2294
|
+
}
|
|
2295
|
+
parts.push("");
|
|
2296
|
+
parts.push("Block Format Reference:");
|
|
2297
|
+
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
2298
|
+
parts.push(` ${this.argPrefix}parameterName`);
|
|
2299
|
+
parts.push(" parameter value here");
|
|
2300
|
+
parts.push(` ${this.endPrefix}`);
|
|
2301
|
+
return parts.join("\n");
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Format a registry error (gadget not found) with available gadgets list.
|
|
2305
|
+
*
|
|
2306
|
+
* @param gadgetName - Name of the gadget that was not found
|
|
2307
|
+
* @param availableGadgets - List of available gadget names
|
|
2308
|
+
* @returns Formatted error message with available gadgets
|
|
2309
|
+
*/
|
|
2310
|
+
formatRegistryError(gadgetName, availableGadgets) {
|
|
2311
|
+
const parts = [];
|
|
2312
|
+
parts.push(`Error: Gadget '${gadgetName}' not found.`);
|
|
2313
|
+
if (availableGadgets.length > 0) {
|
|
2314
|
+
parts.push("");
|
|
2315
|
+
parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
|
|
2316
|
+
} else {
|
|
2317
|
+
parts.push("");
|
|
2318
|
+
parts.push("No gadgets are currently registered.");
|
|
2319
|
+
}
|
|
2320
|
+
return parts.join("\n");
|
|
2321
|
+
}
|
|
2322
|
+
};
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
|
|
2326
|
+
// src/gadgets/exceptions.ts
|
|
2327
|
+
var BreakLoopException, HumanInputException, TimeoutException;
|
|
2328
|
+
var init_exceptions = __esm({
|
|
2329
|
+
"src/gadgets/exceptions.ts"() {
|
|
2330
|
+
"use strict";
|
|
2331
|
+
BreakLoopException = class extends Error {
|
|
2332
|
+
constructor(message) {
|
|
2333
|
+
super(message ?? "Agent loop terminated by gadget");
|
|
2334
|
+
this.name = "BreakLoopException";
|
|
1847
2335
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
2336
|
+
};
|
|
2337
|
+
HumanInputException = class extends Error {
|
|
2338
|
+
question;
|
|
2339
|
+
constructor(question) {
|
|
2340
|
+
super(`Human input required: ${question}`);
|
|
2341
|
+
this.name = "HumanInputException";
|
|
2342
|
+
this.question = question;
|
|
1850
2343
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
2344
|
+
};
|
|
2345
|
+
TimeoutException = class extends Error {
|
|
2346
|
+
timeoutMs;
|
|
2347
|
+
gadgetName;
|
|
2348
|
+
constructor(gadgetName, timeoutMs) {
|
|
2349
|
+
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
2350
|
+
this.name = "TimeoutException";
|
|
2351
|
+
this.gadgetName = gadgetName;
|
|
2352
|
+
this.timeoutMs = timeoutMs;
|
|
1856
2353
|
}
|
|
1857
|
-
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
const lastSegment = segments[segments.length - 1];
|
|
1861
|
-
const coercedValue = coerceValue(value);
|
|
1862
|
-
if (Array.isArray(current)) {
|
|
1863
|
-
const index = parseInt(lastSegment, 10);
|
|
1864
|
-
if (isNaN(index) || index < 0) {
|
|
1865
|
-
throw new Error(`Invalid array index: ${lastSegment}`);
|
|
1866
|
-
}
|
|
1867
|
-
if (index > current.length) {
|
|
1868
|
-
throw new Error(`Array index gap: expected ${current.length}, got ${index}`);
|
|
1869
|
-
}
|
|
1870
|
-
current[index] = coercedValue;
|
|
1871
|
-
} else {
|
|
1872
|
-
current[lastSegment] = coercedValue;
|
|
1873
|
-
}
|
|
1874
|
-
}
|
|
1875
|
-
var init_block_params = __esm({
|
|
1876
|
-
"src/gadgets/block-params.ts"() {
|
|
1877
|
-
"use strict";
|
|
1878
|
-
init_constants();
|
|
2354
|
+
};
|
|
1879
2355
|
}
|
|
1880
2356
|
});
|
|
1881
2357
|
|
|
@@ -2039,18 +2515,295 @@ var init_parser = __esm({
|
|
|
2039
2515
|
parseError
|
|
2040
2516
|
}
|
|
2041
2517
|
};
|
|
2042
|
-
return;
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
const remainingText = this.takeTextUntil(this.buffer.length);
|
|
2522
|
+
if (remainingText !== void 0) {
|
|
2523
|
+
yield { type: "text", content: remainingText };
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
2527
|
+
reset() {
|
|
2528
|
+
this.buffer = "";
|
|
2529
|
+
this.lastReportedTextLength = 0;
|
|
2530
|
+
}
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
});
|
|
2534
|
+
|
|
2535
|
+
// src/gadgets/executor.ts
|
|
2536
|
+
var GadgetExecutor;
|
|
2537
|
+
var init_executor = __esm({
|
|
2538
|
+
"src/gadgets/executor.ts"() {
|
|
2539
|
+
"use strict";
|
|
2540
|
+
init_constants();
|
|
2541
|
+
init_logger();
|
|
2542
|
+
init_block_params();
|
|
2543
|
+
init_error_formatter();
|
|
2544
|
+
init_exceptions();
|
|
2545
|
+
init_parser();
|
|
2546
|
+
GadgetExecutor = class {
|
|
2547
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
|
|
2548
|
+
this.registry = registry;
|
|
2549
|
+
this.onHumanInputRequired = onHumanInputRequired;
|
|
2550
|
+
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
2551
|
+
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
2552
|
+
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
2553
|
+
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2554
|
+
}
|
|
2555
|
+
logger;
|
|
2556
|
+
errorFormatter;
|
|
2557
|
+
argPrefix;
|
|
2558
|
+
/**
|
|
2559
|
+
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
2560
|
+
*/
|
|
2561
|
+
createTimeoutPromise(gadgetName, timeoutMs) {
|
|
2562
|
+
return new Promise((_, reject) => {
|
|
2563
|
+
setTimeout(() => {
|
|
2564
|
+
reject(new TimeoutException(gadgetName, timeoutMs));
|
|
2565
|
+
}, timeoutMs);
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2568
|
+
// Execute a gadget call asynchronously
|
|
2569
|
+
async execute(call) {
|
|
2570
|
+
const startTime = Date.now();
|
|
2571
|
+
this.logger.debug("Executing gadget", {
|
|
2572
|
+
gadgetName: call.gadgetName,
|
|
2573
|
+
invocationId: call.invocationId,
|
|
2574
|
+
parameters: call.parameters
|
|
2575
|
+
});
|
|
2576
|
+
const rawParameters = call.parameters ?? {};
|
|
2577
|
+
let validatedParameters = rawParameters;
|
|
2578
|
+
try {
|
|
2579
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
2580
|
+
if (!gadget) {
|
|
2581
|
+
this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
|
|
2582
|
+
const availableGadgets = this.registry.getNames();
|
|
2583
|
+
return {
|
|
2584
|
+
gadgetName: call.gadgetName,
|
|
2585
|
+
invocationId: call.invocationId,
|
|
2586
|
+
parameters: call.parameters ?? {},
|
|
2587
|
+
error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
|
|
2588
|
+
executionTimeMs: Date.now() - startTime
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
if (call.parseError || !call.parameters) {
|
|
2592
|
+
this.logger.error("Gadget parameter parse error", {
|
|
2593
|
+
gadgetName: call.gadgetName,
|
|
2594
|
+
parseError: call.parseError,
|
|
2595
|
+
rawParameters: call.parametersRaw
|
|
2596
|
+
});
|
|
2597
|
+
const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
|
|
2598
|
+
return {
|
|
2599
|
+
gadgetName: call.gadgetName,
|
|
2600
|
+
invocationId: call.invocationId,
|
|
2601
|
+
parameters: {},
|
|
2602
|
+
error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
|
|
2603
|
+
executionTimeMs: Date.now() - startTime
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
let schemaAwareParameters = rawParameters;
|
|
2607
|
+
const hasBlockFormat = call.parametersRaw?.includes(this.argPrefix);
|
|
2608
|
+
if (gadget.parameterSchema && hasBlockFormat) {
|
|
2609
|
+
try {
|
|
2610
|
+
const cleanedRaw = stripMarkdownFences(call.parametersRaw);
|
|
2611
|
+
const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
|
|
2612
|
+
const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
|
|
2613
|
+
if (parametersWereModified) {
|
|
2614
|
+
this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
|
|
2615
|
+
gadgetName: call.gadgetName
|
|
2616
|
+
});
|
|
2617
|
+
schemaAwareParameters = rawParameters;
|
|
2618
|
+
} else {
|
|
2619
|
+
schemaAwareParameters = parseBlockParams(cleanedRaw, {
|
|
2620
|
+
argPrefix: this.argPrefix,
|
|
2621
|
+
schema: gadget.parameterSchema
|
|
2622
|
+
});
|
|
2623
|
+
this.logger.debug("Re-parsed parameters with schema", {
|
|
2624
|
+
gadgetName: call.gadgetName,
|
|
2625
|
+
original: rawParameters,
|
|
2626
|
+
schemaAware: schemaAwareParameters
|
|
2627
|
+
});
|
|
2628
|
+
}
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
this.logger.warn("Schema-aware re-parsing failed, using original parameters", {
|
|
2631
|
+
gadgetName: call.gadgetName,
|
|
2632
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2633
|
+
});
|
|
2634
|
+
schemaAwareParameters = rawParameters;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
if (gadget.parameterSchema) {
|
|
2638
|
+
const validationResult = gadget.parameterSchema.safeParse(schemaAwareParameters);
|
|
2639
|
+
if (!validationResult.success) {
|
|
2640
|
+
const validationError = this.errorFormatter.formatValidationError(
|
|
2641
|
+
call.gadgetName,
|
|
2642
|
+
validationResult.error,
|
|
2643
|
+
gadget
|
|
2644
|
+
);
|
|
2645
|
+
this.logger.error("Gadget parameter validation failed", {
|
|
2646
|
+
gadgetName: call.gadgetName,
|
|
2647
|
+
issueCount: validationResult.error.issues.length
|
|
2648
|
+
});
|
|
2649
|
+
return {
|
|
2650
|
+
gadgetName: call.gadgetName,
|
|
2651
|
+
invocationId: call.invocationId,
|
|
2652
|
+
parameters: schemaAwareParameters,
|
|
2653
|
+
error: validationError,
|
|
2654
|
+
executionTimeMs: Date.now() - startTime
|
|
2655
|
+
};
|
|
2656
|
+
}
|
|
2657
|
+
validatedParameters = validationResult.data;
|
|
2658
|
+
} else {
|
|
2659
|
+
validatedParameters = schemaAwareParameters;
|
|
2660
|
+
}
|
|
2661
|
+
const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
|
|
2662
|
+
let result;
|
|
2663
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
2664
|
+
this.logger.debug("Executing gadget with timeout", {
|
|
2665
|
+
gadgetName: call.gadgetName,
|
|
2666
|
+
timeoutMs
|
|
2667
|
+
});
|
|
2668
|
+
result = await Promise.race([
|
|
2669
|
+
Promise.resolve(gadget.execute(validatedParameters)),
|
|
2670
|
+
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
2671
|
+
]);
|
|
2672
|
+
} else {
|
|
2673
|
+
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
2674
|
+
}
|
|
2675
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2676
|
+
this.logger.info("Gadget executed successfully", {
|
|
2677
|
+
gadgetName: call.gadgetName,
|
|
2678
|
+
invocationId: call.invocationId,
|
|
2679
|
+
executionTimeMs
|
|
2680
|
+
});
|
|
2681
|
+
this.logger.debug("Gadget result", {
|
|
2682
|
+
gadgetName: call.gadgetName,
|
|
2683
|
+
invocationId: call.invocationId,
|
|
2684
|
+
parameters: validatedParameters,
|
|
2685
|
+
result,
|
|
2686
|
+
executionTimeMs
|
|
2687
|
+
});
|
|
2688
|
+
return {
|
|
2689
|
+
gadgetName: call.gadgetName,
|
|
2690
|
+
invocationId: call.invocationId,
|
|
2691
|
+
parameters: validatedParameters,
|
|
2692
|
+
result,
|
|
2693
|
+
executionTimeMs
|
|
2694
|
+
};
|
|
2695
|
+
} catch (error) {
|
|
2696
|
+
if (error instanceof BreakLoopException) {
|
|
2697
|
+
this.logger.info("Gadget requested loop termination", {
|
|
2698
|
+
gadgetName: call.gadgetName,
|
|
2699
|
+
message: error.message
|
|
2700
|
+
});
|
|
2701
|
+
return {
|
|
2702
|
+
gadgetName: call.gadgetName,
|
|
2703
|
+
invocationId: call.invocationId,
|
|
2704
|
+
parameters: validatedParameters,
|
|
2705
|
+
result: error.message,
|
|
2706
|
+
breaksLoop: true,
|
|
2707
|
+
executionTimeMs: Date.now() - startTime
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2710
|
+
if (error instanceof TimeoutException) {
|
|
2711
|
+
this.logger.error("Gadget execution timed out", {
|
|
2712
|
+
gadgetName: call.gadgetName,
|
|
2713
|
+
timeoutMs: error.timeoutMs,
|
|
2714
|
+
executionTimeMs: Date.now() - startTime
|
|
2715
|
+
});
|
|
2716
|
+
return {
|
|
2717
|
+
gadgetName: call.gadgetName,
|
|
2718
|
+
invocationId: call.invocationId,
|
|
2719
|
+
parameters: validatedParameters,
|
|
2720
|
+
error: error.message,
|
|
2721
|
+
executionTimeMs: Date.now() - startTime
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
if (error instanceof HumanInputException) {
|
|
2725
|
+
this.logger.info("Gadget requested human input", {
|
|
2726
|
+
gadgetName: call.gadgetName,
|
|
2727
|
+
question: error.question
|
|
2728
|
+
});
|
|
2729
|
+
if (this.onHumanInputRequired) {
|
|
2730
|
+
try {
|
|
2731
|
+
const answer = await this.onHumanInputRequired(error.question);
|
|
2732
|
+
this.logger.debug("Human input received", {
|
|
2733
|
+
gadgetName: call.gadgetName,
|
|
2734
|
+
answerLength: answer.length
|
|
2735
|
+
});
|
|
2736
|
+
return {
|
|
2737
|
+
gadgetName: call.gadgetName,
|
|
2738
|
+
invocationId: call.invocationId,
|
|
2739
|
+
parameters: validatedParameters,
|
|
2740
|
+
result: answer,
|
|
2741
|
+
executionTimeMs: Date.now() - startTime
|
|
2742
|
+
};
|
|
2743
|
+
} catch (inputError) {
|
|
2744
|
+
this.logger.error("Human input callback error", {
|
|
2745
|
+
gadgetName: call.gadgetName,
|
|
2746
|
+
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
2747
|
+
});
|
|
2748
|
+
return {
|
|
2749
|
+
gadgetName: call.gadgetName,
|
|
2750
|
+
invocationId: call.invocationId,
|
|
2751
|
+
parameters: validatedParameters,
|
|
2752
|
+
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
2753
|
+
executionTimeMs: Date.now() - startTime
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
this.logger.warn("Human input required but no callback provided", {
|
|
2758
|
+
gadgetName: call.gadgetName
|
|
2759
|
+
});
|
|
2760
|
+
return {
|
|
2761
|
+
gadgetName: call.gadgetName,
|
|
2762
|
+
invocationId: call.invocationId,
|
|
2763
|
+
parameters: validatedParameters,
|
|
2764
|
+
error: "Human input required but not available (stdin is not interactive)",
|
|
2765
|
+
executionTimeMs: Date.now() - startTime
|
|
2766
|
+
};
|
|
2043
2767
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2768
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2769
|
+
this.logger.error("Gadget execution failed", {
|
|
2770
|
+
gadgetName: call.gadgetName,
|
|
2771
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2772
|
+
executionTimeMs
|
|
2773
|
+
});
|
|
2774
|
+
return {
|
|
2775
|
+
gadgetName: call.gadgetName,
|
|
2776
|
+
invocationId: call.invocationId,
|
|
2777
|
+
parameters: validatedParameters,
|
|
2778
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2779
|
+
executionTimeMs
|
|
2780
|
+
};
|
|
2048
2781
|
}
|
|
2049
2782
|
}
|
|
2050
|
-
//
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2783
|
+
// Execute multiple gadget calls in parallel
|
|
2784
|
+
async executeAll(calls) {
|
|
2785
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Deep equality check for objects/arrays.
|
|
2789
|
+
* Used to detect if parameters were modified by an interceptor.
|
|
2790
|
+
*/
|
|
2791
|
+
deepEquals(a, b) {
|
|
2792
|
+
if (a === b) return true;
|
|
2793
|
+
if (a === null || b === null) return a === b;
|
|
2794
|
+
if (typeof a !== typeof b) return false;
|
|
2795
|
+
if (typeof a !== "object") return a === b;
|
|
2796
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
2797
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
2798
|
+
if (a.length !== b.length) return false;
|
|
2799
|
+
return a.every((val, i) => this.deepEquals(val, b[i]));
|
|
2800
|
+
}
|
|
2801
|
+
const aObj = a;
|
|
2802
|
+
const bObj = b;
|
|
2803
|
+
const aKeys = Object.keys(aObj);
|
|
2804
|
+
const bKeys = Object.keys(bObj);
|
|
2805
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
2806
|
+
return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
|
|
2054
2807
|
}
|
|
2055
2808
|
};
|
|
2056
2809
|
}
|
|
@@ -2093,7 +2846,8 @@ var init_stream_processor = __esm({
|
|
|
2093
2846
|
options.registry,
|
|
2094
2847
|
options.onHumanInputRequired,
|
|
2095
2848
|
this.logger.getSubLogger({ name: "executor" }),
|
|
2096
|
-
options.defaultGadgetTimeoutMs
|
|
2849
|
+
options.defaultGadgetTimeoutMs,
|
|
2850
|
+
{ argPrefix: options.gadgetArgPrefix }
|
|
2097
2851
|
);
|
|
2098
2852
|
}
|
|
2099
2853
|
/**
|
|
@@ -2462,6 +3216,7 @@ var init_agent = __esm({
|
|
|
2462
3216
|
init_model_shortcuts();
|
|
2463
3217
|
init_output_viewer();
|
|
2464
3218
|
init_logger();
|
|
3219
|
+
init_manager();
|
|
2465
3220
|
init_gadget_output_store();
|
|
2466
3221
|
init_agent_internal_key();
|
|
2467
3222
|
init_conversation_manager();
|
|
@@ -2492,6 +3247,8 @@ var init_agent = __esm({
|
|
|
2492
3247
|
outputStore;
|
|
2493
3248
|
outputLimitEnabled;
|
|
2494
3249
|
outputLimitCharLimit;
|
|
3250
|
+
// Context compaction
|
|
3251
|
+
compactionManager;
|
|
2495
3252
|
/**
|
|
2496
3253
|
* Creates a new Agent instance.
|
|
2497
3254
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -2551,6 +3308,14 @@ var init_agent = __esm({
|
|
|
2551
3308
|
if (options.userPrompt) {
|
|
2552
3309
|
this.conversation.addUserMessage(options.userPrompt);
|
|
2553
3310
|
}
|
|
3311
|
+
const compactionEnabled = options.compactionConfig?.enabled ?? true;
|
|
3312
|
+
if (compactionEnabled) {
|
|
3313
|
+
this.compactionManager = new CompactionManager(
|
|
3314
|
+
this.client,
|
|
3315
|
+
this.model,
|
|
3316
|
+
options.compactionConfig
|
|
3317
|
+
);
|
|
3318
|
+
}
|
|
2554
3319
|
}
|
|
2555
3320
|
/**
|
|
2556
3321
|
* Get the gadget registry for this agent.
|
|
@@ -2573,6 +3338,53 @@ var init_agent = __esm({
|
|
|
2573
3338
|
getRegistry() {
|
|
2574
3339
|
return this.registry;
|
|
2575
3340
|
}
|
|
3341
|
+
/**
|
|
3342
|
+
* Manually trigger context compaction.
|
|
3343
|
+
*
|
|
3344
|
+
* Forces compaction regardless of threshold. Useful for:
|
|
3345
|
+
* - Pre-emptive context management before expected long operations
|
|
3346
|
+
* - Testing compaction behavior
|
|
3347
|
+
*
|
|
3348
|
+
* @returns CompactionEvent if compaction was performed, null if not configured or no history
|
|
3349
|
+
*
|
|
3350
|
+
* @example
|
|
3351
|
+
* ```typescript
|
|
3352
|
+
* const agent = await LLMist.createAgent()
|
|
3353
|
+
* .withModel('sonnet')
|
|
3354
|
+
* .withCompaction()
|
|
3355
|
+
* .ask('...');
|
|
3356
|
+
*
|
|
3357
|
+
* // Manually compact before a long operation
|
|
3358
|
+
* const event = await agent.compact();
|
|
3359
|
+
* if (event) {
|
|
3360
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
3361
|
+
* }
|
|
3362
|
+
* ```
|
|
3363
|
+
*/
|
|
3364
|
+
async compact() {
|
|
3365
|
+
if (!this.compactionManager) {
|
|
3366
|
+
return null;
|
|
3367
|
+
}
|
|
3368
|
+
return this.compactionManager.compact(this.conversation, -1);
|
|
3369
|
+
}
|
|
3370
|
+
/**
|
|
3371
|
+
* Get compaction statistics.
|
|
3372
|
+
*
|
|
3373
|
+
* @returns CompactionStats if compaction is enabled, null otherwise
|
|
3374
|
+
*
|
|
3375
|
+
* @example
|
|
3376
|
+
* ```typescript
|
|
3377
|
+
* const stats = agent.getCompactionStats();
|
|
3378
|
+
* if (stats) {
|
|
3379
|
+
* console.log(`Total compactions: ${stats.totalCompactions}`);
|
|
3380
|
+
* console.log(`Tokens saved: ${stats.totalTokensSaved}`);
|
|
3381
|
+
* console.log(`Current usage: ${stats.currentUsage.percent.toFixed(1)}%`);
|
|
3382
|
+
* }
|
|
3383
|
+
* ```
|
|
3384
|
+
*/
|
|
3385
|
+
getCompactionStats() {
|
|
3386
|
+
return this.compactionManager?.getStats() ?? null;
|
|
3387
|
+
}
|
|
2576
3388
|
/**
|
|
2577
3389
|
* Run the agent loop.
|
|
2578
3390
|
* Clean, simple orchestration - all complexity is in StreamProcessor.
|
|
@@ -2593,6 +3405,30 @@ var init_agent = __esm({
|
|
|
2593
3405
|
while (currentIteration < this.maxIterations) {
|
|
2594
3406
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
2595
3407
|
try {
|
|
3408
|
+
if (this.compactionManager) {
|
|
3409
|
+
const compactionEvent = await this.compactionManager.checkAndCompact(
|
|
3410
|
+
this.conversation,
|
|
3411
|
+
currentIteration
|
|
3412
|
+
);
|
|
3413
|
+
if (compactionEvent) {
|
|
3414
|
+
this.logger.info("Context compacted", {
|
|
3415
|
+
strategy: compactionEvent.strategy,
|
|
3416
|
+
tokensBefore: compactionEvent.tokensBefore,
|
|
3417
|
+
tokensAfter: compactionEvent.tokensAfter
|
|
3418
|
+
});
|
|
3419
|
+
yield { type: "compaction", event: compactionEvent };
|
|
3420
|
+
await this.safeObserve(async () => {
|
|
3421
|
+
if (this.hooks.observers?.onCompaction) {
|
|
3422
|
+
await this.hooks.observers.onCompaction({
|
|
3423
|
+
iteration: currentIteration,
|
|
3424
|
+
event: compactionEvent,
|
|
3425
|
+
stats: this.compactionManager.getStats(),
|
|
3426
|
+
logger: this.logger
|
|
3427
|
+
});
|
|
3428
|
+
}
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
2596
3432
|
let llmOptions = {
|
|
2597
3433
|
model: this.model,
|
|
2598
3434
|
messages: this.conversation.getMessages(),
|
|
@@ -2612,6 +3448,7 @@ var init_agent = __esm({
|
|
|
2612
3448
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
2613
3449
|
const context = {
|
|
2614
3450
|
iteration: currentIteration,
|
|
3451
|
+
maxIterations: this.maxIterations,
|
|
2615
3452
|
options: llmOptions,
|
|
2616
3453
|
logger: this.logger
|
|
2617
3454
|
};
|
|
@@ -2676,12 +3513,17 @@ var init_agent = __esm({
|
|
|
2676
3513
|
});
|
|
2677
3514
|
let finalMessage = result.finalMessage;
|
|
2678
3515
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
3516
|
+
const gadgetCallCount = result.outputs.filter(
|
|
3517
|
+
(output) => output.type === "gadget_result"
|
|
3518
|
+
).length;
|
|
2679
3519
|
const context = {
|
|
2680
3520
|
iteration: currentIteration,
|
|
3521
|
+
maxIterations: this.maxIterations,
|
|
2681
3522
|
options: llmOptions,
|
|
2682
3523
|
finishReason: result.finishReason,
|
|
2683
3524
|
usage: result.usage,
|
|
2684
3525
|
finalMessage: result.finalMessage,
|
|
3526
|
+
gadgetCallCount,
|
|
2685
3527
|
logger: this.logger
|
|
2686
3528
|
};
|
|
2687
3529
|
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
@@ -2943,6 +3785,7 @@ var init_builder = __esm({
|
|
|
2943
3785
|
defaultGadgetTimeoutMs;
|
|
2944
3786
|
gadgetOutputLimit;
|
|
2945
3787
|
gadgetOutputLimitPercent;
|
|
3788
|
+
compactionConfig;
|
|
2946
3789
|
constructor(client) {
|
|
2947
3790
|
this.client = client;
|
|
2948
3791
|
}
|
|
@@ -3338,6 +4181,57 @@ var init_builder = __esm({
|
|
|
3338
4181
|
this.gadgetOutputLimitPercent = percent;
|
|
3339
4182
|
return this;
|
|
3340
4183
|
}
|
|
4184
|
+
/**
|
|
4185
|
+
* Configure context compaction.
|
|
4186
|
+
*
|
|
4187
|
+
* Context compaction automatically manages conversation history to prevent
|
|
4188
|
+
* context window overflow in long-running agent conversations.
|
|
4189
|
+
*
|
|
4190
|
+
* @param config - Compaction configuration options
|
|
4191
|
+
* @returns This builder for chaining
|
|
4192
|
+
*
|
|
4193
|
+
* @example
|
|
4194
|
+
* ```typescript
|
|
4195
|
+
* // Custom thresholds
|
|
4196
|
+
* .withCompaction({
|
|
4197
|
+
* triggerThresholdPercent: 70,
|
|
4198
|
+
* targetPercent: 40,
|
|
4199
|
+
* preserveRecentTurns: 10,
|
|
4200
|
+
* })
|
|
4201
|
+
*
|
|
4202
|
+
* // Different strategy
|
|
4203
|
+
* .withCompaction({
|
|
4204
|
+
* strategy: 'sliding-window',
|
|
4205
|
+
* })
|
|
4206
|
+
*
|
|
4207
|
+
* // With callback
|
|
4208
|
+
* .withCompaction({
|
|
4209
|
+
* onCompaction: (event) => {
|
|
4210
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
4211
|
+
* }
|
|
4212
|
+
* })
|
|
4213
|
+
* ```
|
|
4214
|
+
*/
|
|
4215
|
+
withCompaction(config) {
|
|
4216
|
+
this.compactionConfig = { ...config, enabled: config.enabled ?? true };
|
|
4217
|
+
return this;
|
|
4218
|
+
}
|
|
4219
|
+
/**
|
|
4220
|
+
* Disable context compaction.
|
|
4221
|
+
*
|
|
4222
|
+
* By default, compaction is enabled. Use this method to explicitly disable it.
|
|
4223
|
+
*
|
|
4224
|
+
* @returns This builder for chaining
|
|
4225
|
+
*
|
|
4226
|
+
* @example
|
|
4227
|
+
* ```typescript
|
|
4228
|
+
* .withoutCompaction() // Disable automatic compaction
|
|
4229
|
+
* ```
|
|
4230
|
+
*/
|
|
4231
|
+
withoutCompaction() {
|
|
4232
|
+
this.compactionConfig = { enabled: false };
|
|
4233
|
+
return this;
|
|
4234
|
+
}
|
|
3341
4235
|
/**
|
|
3342
4236
|
* Add a synthetic gadget call to the conversation history.
|
|
3343
4237
|
*
|
|
@@ -3453,7 +4347,8 @@ ${endPrefix}`
|
|
|
3453
4347
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
3454
4348
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3455
4349
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
3456
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
4350
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
4351
|
+
compactionConfig: this.compactionConfig
|
|
3457
4352
|
};
|
|
3458
4353
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
3459
4354
|
}
|
|
@@ -3555,7 +4450,8 @@ ${endPrefix}`
|
|
|
3555
4450
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
3556
4451
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
3557
4452
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
3558
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
4453
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
4454
|
+
compactionConfig: this.compactionConfig
|
|
3559
4455
|
};
|
|
3560
4456
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
3561
4457
|
}
|
|
@@ -5540,19 +6436,44 @@ var init_client = __esm({
|
|
|
5540
6436
|
var testing_exports = {};
|
|
5541
6437
|
__export(testing_exports, {
|
|
5542
6438
|
MockBuilder: () => MockBuilder,
|
|
6439
|
+
MockConversationManager: () => MockConversationManager,
|
|
5543
6440
|
MockGadgetBuilder: () => MockGadgetBuilder,
|
|
5544
6441
|
MockManager: () => MockManager,
|
|
6442
|
+
MockPromptRecorder: () => MockPromptRecorder,
|
|
5545
6443
|
MockProviderAdapter: () => MockProviderAdapter,
|
|
6444
|
+
collectOutput: () => collectOutput,
|
|
6445
|
+
collectStream: () => collectStream,
|
|
6446
|
+
collectStreamText: () => collectStreamText,
|
|
6447
|
+
createAssistantMessage: () => createAssistantMessage,
|
|
6448
|
+
createConversation: () => createConversation,
|
|
6449
|
+
createConversationWithGadgets: () => createConversationWithGadgets,
|
|
6450
|
+
createEmptyStream: () => createEmptyStream,
|
|
6451
|
+
createErrorStream: () => createErrorStream,
|
|
6452
|
+
createLargeConversation: () => createLargeConversation,
|
|
6453
|
+
createMinimalConversation: () => createMinimalConversation,
|
|
5546
6454
|
createMockAdapter: () => createMockAdapter,
|
|
5547
6455
|
createMockClient: () => createMockClient,
|
|
6456
|
+
createMockConversationManager: () => createMockConversationManager,
|
|
5548
6457
|
createMockGadget: () => createMockGadget,
|
|
6458
|
+
createMockPrompt: () => createMockPrompt,
|
|
6459
|
+
createMockReadable: () => createMockReadable,
|
|
5549
6460
|
createMockStream: () => createMockStream,
|
|
6461
|
+
createMockWritable: () => createMockWritable,
|
|
6462
|
+
createSystemMessage: () => createSystemMessage,
|
|
6463
|
+
createTestEnvironment: () => createTestEnvironment,
|
|
6464
|
+
createTestStream: () => createTestStream,
|
|
5550
6465
|
createTextMockStream: () => createTextMockStream,
|
|
6466
|
+
createTextStream: () => createTextStream,
|
|
6467
|
+
createUserMessage: () => createUserMessage,
|
|
6468
|
+
estimateTokens: () => estimateTokens,
|
|
6469
|
+
getBufferedOutput: () => getBufferedOutput,
|
|
5551
6470
|
getMockManager: () => getMockManager,
|
|
6471
|
+
getStreamFinalChunk: () => getStreamFinalChunk,
|
|
5552
6472
|
mockGadget: () => mockGadget,
|
|
5553
6473
|
mockLLM: () => mockLLM,
|
|
5554
6474
|
testGadget: () => testGadget,
|
|
5555
|
-
testGadgetBatch: () => testGadgetBatch
|
|
6475
|
+
testGadgetBatch: () => testGadgetBatch,
|
|
6476
|
+
waitFor: () => waitFor
|
|
5556
6477
|
});
|
|
5557
6478
|
module.exports = __toCommonJS(testing_exports);
|
|
5558
6479
|
|
|
@@ -6461,21 +7382,477 @@ var MockGadgetBuilder = class {
|
|
|
6461
7382
|
function mockGadget() {
|
|
6462
7383
|
return new MockGadgetBuilder();
|
|
6463
7384
|
}
|
|
7385
|
+
|
|
7386
|
+
// src/testing/stream-helpers.ts
|
|
7387
|
+
function createTestStream(chunks) {
|
|
7388
|
+
return async function* () {
|
|
7389
|
+
for (const chunk of chunks) {
|
|
7390
|
+
yield chunk;
|
|
7391
|
+
}
|
|
7392
|
+
}();
|
|
7393
|
+
}
|
|
7394
|
+
function createTextStream(text, options) {
|
|
7395
|
+
return async function* () {
|
|
7396
|
+
if (options?.delayMs) {
|
|
7397
|
+
await sleep2(options.delayMs);
|
|
7398
|
+
}
|
|
7399
|
+
const chunkSize = options?.chunkSize ?? text.length;
|
|
7400
|
+
const chunks = [];
|
|
7401
|
+
for (let i = 0; i < text.length; i += chunkSize) {
|
|
7402
|
+
chunks.push(text.slice(i, i + chunkSize));
|
|
7403
|
+
}
|
|
7404
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
7405
|
+
const isLast = i === chunks.length - 1;
|
|
7406
|
+
const chunk = { text: chunks[i] };
|
|
7407
|
+
if (isLast) {
|
|
7408
|
+
chunk.finishReason = options?.finishReason ?? "stop";
|
|
7409
|
+
const inputTokens = Math.ceil(text.length / 4);
|
|
7410
|
+
const outputTokens = Math.ceil(text.length / 4);
|
|
7411
|
+
chunk.usage = options?.usage ?? {
|
|
7412
|
+
inputTokens,
|
|
7413
|
+
outputTokens,
|
|
7414
|
+
totalTokens: inputTokens + outputTokens
|
|
7415
|
+
};
|
|
7416
|
+
}
|
|
7417
|
+
yield chunk;
|
|
7418
|
+
if (options?.chunkDelayMs && !isLast) {
|
|
7419
|
+
await sleep2(options.chunkDelayMs);
|
|
7420
|
+
}
|
|
7421
|
+
}
|
|
7422
|
+
}();
|
|
7423
|
+
}
|
|
7424
|
+
async function collectStream(stream2) {
|
|
7425
|
+
const chunks = [];
|
|
7426
|
+
for await (const chunk of stream2) {
|
|
7427
|
+
chunks.push(chunk);
|
|
7428
|
+
}
|
|
7429
|
+
return chunks;
|
|
7430
|
+
}
|
|
7431
|
+
async function collectStreamText(stream2) {
|
|
7432
|
+
let text = "";
|
|
7433
|
+
for await (const chunk of stream2) {
|
|
7434
|
+
text += chunk.text ?? "";
|
|
7435
|
+
}
|
|
7436
|
+
return text;
|
|
7437
|
+
}
|
|
7438
|
+
async function getStreamFinalChunk(stream2) {
|
|
7439
|
+
let lastChunk;
|
|
7440
|
+
for await (const chunk of stream2) {
|
|
7441
|
+
lastChunk = chunk;
|
|
7442
|
+
}
|
|
7443
|
+
return lastChunk;
|
|
7444
|
+
}
|
|
7445
|
+
function createEmptyStream() {
|
|
7446
|
+
return async function* () {
|
|
7447
|
+
}();
|
|
7448
|
+
}
|
|
7449
|
+
function createErrorStream(chunksBeforeError, error) {
|
|
7450
|
+
return async function* () {
|
|
7451
|
+
for (const chunk of chunksBeforeError) {
|
|
7452
|
+
yield chunk;
|
|
7453
|
+
}
|
|
7454
|
+
throw error;
|
|
7455
|
+
}();
|
|
7456
|
+
}
|
|
7457
|
+
function sleep2(ms) {
|
|
7458
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7459
|
+
}
|
|
7460
|
+
|
|
7461
|
+
// src/testing/conversation-fixtures.ts
|
|
7462
|
+
function createConversation(turnCount, options) {
|
|
7463
|
+
const messages = [];
|
|
7464
|
+
const userPrefix = options?.userPrefix ?? "User message";
|
|
7465
|
+
const assistantPrefix = options?.assistantPrefix ?? "Assistant response";
|
|
7466
|
+
const contentLength = options?.contentLength ?? 100;
|
|
7467
|
+
for (let i = 0; i < turnCount; i++) {
|
|
7468
|
+
const padding = " ".repeat(Math.max(0, contentLength - 30));
|
|
7469
|
+
messages.push({
|
|
7470
|
+
role: "user",
|
|
7471
|
+
content: `${userPrefix} ${i + 1}: This is turn ${i + 1} of the conversation.${padding}`
|
|
7472
|
+
});
|
|
7473
|
+
messages.push({
|
|
7474
|
+
role: "assistant",
|
|
7475
|
+
content: `${assistantPrefix} ${i + 1}: I acknowledge turn ${i + 1}.${padding}`
|
|
7476
|
+
});
|
|
7477
|
+
}
|
|
7478
|
+
return messages;
|
|
7479
|
+
}
|
|
7480
|
+
function createConversationWithGadgets(turnCount, gadgetCallsPerTurn = 1, options) {
|
|
7481
|
+
const messages = [];
|
|
7482
|
+
const gadgetNames = options?.gadgetNames ?? ["search", "calculate", "read"];
|
|
7483
|
+
const contentLength = options?.contentLength ?? 50;
|
|
7484
|
+
let gadgetIndex = 0;
|
|
7485
|
+
for (let turn = 0; turn < turnCount; turn++) {
|
|
7486
|
+
messages.push({
|
|
7487
|
+
role: "user",
|
|
7488
|
+
content: `User request ${turn + 1}${"x".repeat(contentLength)}`
|
|
7489
|
+
});
|
|
7490
|
+
for (let g = 0; g < gadgetCallsPerTurn; g++) {
|
|
7491
|
+
const gadgetName = gadgetNames[gadgetIndex % gadgetNames.length];
|
|
7492
|
+
gadgetIndex++;
|
|
7493
|
+
messages.push({
|
|
7494
|
+
role: "assistant",
|
|
7495
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
7496
|
+
!!!ARG:query
|
|
7497
|
+
test query ${turn}-${g}
|
|
7498
|
+
!!!GADGET_END`
|
|
7499
|
+
});
|
|
7500
|
+
messages.push({
|
|
7501
|
+
role: "user",
|
|
7502
|
+
content: `Result: Gadget ${gadgetName} returned result for query ${turn}-${g}`
|
|
7503
|
+
});
|
|
7504
|
+
}
|
|
7505
|
+
messages.push({
|
|
7506
|
+
role: "assistant",
|
|
7507
|
+
content: `Final response for turn ${turn + 1}${"y".repeat(contentLength)}`
|
|
7508
|
+
});
|
|
7509
|
+
}
|
|
7510
|
+
return messages;
|
|
7511
|
+
}
|
|
7512
|
+
function estimateTokens(messages) {
|
|
7513
|
+
return Math.ceil(
|
|
7514
|
+
messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
7515
|
+
);
|
|
7516
|
+
}
|
|
7517
|
+
function createUserMessage(content) {
|
|
7518
|
+
return { role: "user", content };
|
|
7519
|
+
}
|
|
7520
|
+
function createAssistantMessage(content) {
|
|
7521
|
+
return { role: "assistant", content };
|
|
7522
|
+
}
|
|
7523
|
+
function createSystemMessage(content) {
|
|
7524
|
+
return { role: "system", content };
|
|
7525
|
+
}
|
|
7526
|
+
function createMinimalConversation() {
|
|
7527
|
+
return [
|
|
7528
|
+
{ role: "user", content: "Hello" },
|
|
7529
|
+
{ role: "assistant", content: "Hi there!" }
|
|
7530
|
+
];
|
|
7531
|
+
}
|
|
7532
|
+
function createLargeConversation(targetTokens, options) {
|
|
7533
|
+
const tokensPerTurn = options?.tokensPerTurn ?? 200;
|
|
7534
|
+
const turnsNeeded = Math.ceil(targetTokens / tokensPerTurn);
|
|
7535
|
+
const charsPerMessage = Math.floor(tokensPerTurn * 4 / 2);
|
|
7536
|
+
return createConversation(turnsNeeded, {
|
|
7537
|
+
contentLength: charsPerMessage
|
|
7538
|
+
});
|
|
7539
|
+
}
|
|
7540
|
+
|
|
7541
|
+
// src/testing/mock-conversation.ts
|
|
7542
|
+
var MockConversationManager = class {
|
|
7543
|
+
history;
|
|
7544
|
+
baseMessages;
|
|
7545
|
+
replacementHistory;
|
|
7546
|
+
replaceHistoryCallCount = 0;
|
|
7547
|
+
addedMessages = [];
|
|
7548
|
+
constructor(history = [], baseMessages = []) {
|
|
7549
|
+
this.history = [...history];
|
|
7550
|
+
this.baseMessages = [...baseMessages];
|
|
7551
|
+
}
|
|
7552
|
+
addUserMessage(content) {
|
|
7553
|
+
const msg = { role: "user", content };
|
|
7554
|
+
this.history.push(msg);
|
|
7555
|
+
this.addedMessages.push(msg);
|
|
7556
|
+
}
|
|
7557
|
+
addAssistantMessage(content) {
|
|
7558
|
+
const msg = { role: "assistant", content };
|
|
7559
|
+
this.history.push(msg);
|
|
7560
|
+
this.addedMessages.push(msg);
|
|
7561
|
+
}
|
|
7562
|
+
addGadgetCall(gadgetName, parameters, result) {
|
|
7563
|
+
const assistantMsg = {
|
|
7564
|
+
role: "assistant",
|
|
7565
|
+
content: `!!!GADGET_START:${gadgetName}
|
|
7566
|
+
${JSON.stringify(parameters)}
|
|
7567
|
+
!!!GADGET_END`
|
|
7568
|
+
};
|
|
7569
|
+
const resultMsg = {
|
|
7570
|
+
role: "user",
|
|
7571
|
+
content: `Result: ${result}`
|
|
7572
|
+
};
|
|
7573
|
+
this.history.push(assistantMsg);
|
|
7574
|
+
this.history.push(resultMsg);
|
|
7575
|
+
this.addedMessages.push(assistantMsg);
|
|
7576
|
+
this.addedMessages.push(resultMsg);
|
|
7577
|
+
}
|
|
7578
|
+
getMessages() {
|
|
7579
|
+
return [...this.baseMessages, ...this.history];
|
|
7580
|
+
}
|
|
7581
|
+
getHistoryMessages() {
|
|
7582
|
+
return [...this.history];
|
|
7583
|
+
}
|
|
7584
|
+
getBaseMessages() {
|
|
7585
|
+
return [...this.baseMessages];
|
|
7586
|
+
}
|
|
7587
|
+
replaceHistory(newHistory) {
|
|
7588
|
+
this.replacementHistory = [...newHistory];
|
|
7589
|
+
this.history = [...newHistory];
|
|
7590
|
+
this.replaceHistoryCallCount++;
|
|
7591
|
+
}
|
|
7592
|
+
// ============================================
|
|
7593
|
+
// Test Helper Methods
|
|
7594
|
+
// ============================================
|
|
7595
|
+
/**
|
|
7596
|
+
* Check if replaceHistory was called.
|
|
7597
|
+
*/
|
|
7598
|
+
wasReplaceHistoryCalled() {
|
|
7599
|
+
return this.replaceHistoryCallCount > 0;
|
|
7600
|
+
}
|
|
7601
|
+
/**
|
|
7602
|
+
* Get the number of times replaceHistory was called.
|
|
7603
|
+
*/
|
|
7604
|
+
getReplaceHistoryCallCount() {
|
|
7605
|
+
return this.replaceHistoryCallCount;
|
|
7606
|
+
}
|
|
7607
|
+
/**
|
|
7608
|
+
* Get the most recent history passed to replaceHistory.
|
|
7609
|
+
* Returns undefined if replaceHistory was never called.
|
|
7610
|
+
*/
|
|
7611
|
+
getReplacementHistory() {
|
|
7612
|
+
return this.replacementHistory;
|
|
7613
|
+
}
|
|
7614
|
+
/**
|
|
7615
|
+
* Get all messages that were added via add* methods.
|
|
7616
|
+
*/
|
|
7617
|
+
getAddedMessages() {
|
|
7618
|
+
return [...this.addedMessages];
|
|
7619
|
+
}
|
|
7620
|
+
/**
|
|
7621
|
+
* Reset all tracking state while preserving the conversation.
|
|
7622
|
+
*/
|
|
7623
|
+
resetTracking() {
|
|
7624
|
+
this.replacementHistory = void 0;
|
|
7625
|
+
this.replaceHistoryCallCount = 0;
|
|
7626
|
+
this.addedMessages = [];
|
|
7627
|
+
}
|
|
7628
|
+
/**
|
|
7629
|
+
* Completely reset the mock to initial state.
|
|
7630
|
+
* Note: baseMessages cannot be changed after construction.
|
|
7631
|
+
*/
|
|
7632
|
+
reset(history = []) {
|
|
7633
|
+
this.history = [...history];
|
|
7634
|
+
this.resetTracking();
|
|
7635
|
+
}
|
|
7636
|
+
/**
|
|
7637
|
+
* Set the history directly (for test setup).
|
|
7638
|
+
*/
|
|
7639
|
+
setHistory(messages) {
|
|
7640
|
+
this.history = [...messages];
|
|
7641
|
+
}
|
|
7642
|
+
/**
|
|
7643
|
+
* Get the current history length.
|
|
7644
|
+
*/
|
|
7645
|
+
getHistoryLength() {
|
|
7646
|
+
return this.history.length;
|
|
7647
|
+
}
|
|
7648
|
+
/**
|
|
7649
|
+
* Get total message count (base + history).
|
|
7650
|
+
*/
|
|
7651
|
+
getTotalMessageCount() {
|
|
7652
|
+
return this.baseMessages.length + this.history.length;
|
|
7653
|
+
}
|
|
7654
|
+
};
|
|
7655
|
+
function createMockConversationManager(turnCount, baseMessages = []) {
|
|
7656
|
+
const history = [];
|
|
7657
|
+
for (let i = 0; i < turnCount; i++) {
|
|
7658
|
+
history.push({
|
|
7659
|
+
role: "user",
|
|
7660
|
+
content: `User message ${i + 1}: This is turn ${i + 1} of the conversation.`
|
|
7661
|
+
});
|
|
7662
|
+
history.push({
|
|
7663
|
+
role: "assistant",
|
|
7664
|
+
content: `Assistant response ${i + 1}: I acknowledge turn ${i + 1}.`
|
|
7665
|
+
});
|
|
7666
|
+
}
|
|
7667
|
+
return new MockConversationManager(history, baseMessages);
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
// src/testing/cli-helpers.ts
|
|
7671
|
+
var import_node_stream = require("stream");
|
|
7672
|
+
function createTestEnvironment(options = {}) {
|
|
7673
|
+
const stdin = createMockReadable(options.stdin);
|
|
7674
|
+
const stdout = new import_node_stream.PassThrough();
|
|
7675
|
+
const stderr = new import_node_stream.PassThrough();
|
|
7676
|
+
let exitCode;
|
|
7677
|
+
return {
|
|
7678
|
+
stdin,
|
|
7679
|
+
stdout,
|
|
7680
|
+
stderr,
|
|
7681
|
+
isTTY: options.isTTY ?? false,
|
|
7682
|
+
argv: options.argv ?? ["node", "llmist"],
|
|
7683
|
+
env: { ...filterDefinedEnv(process.env), ...options.env },
|
|
7684
|
+
get exitCode() {
|
|
7685
|
+
return exitCode;
|
|
7686
|
+
},
|
|
7687
|
+
setExitCode: (code) => {
|
|
7688
|
+
exitCode = code;
|
|
7689
|
+
}
|
|
7690
|
+
};
|
|
7691
|
+
}
|
|
7692
|
+
function createMockReadable(input) {
|
|
7693
|
+
if (!input) {
|
|
7694
|
+
const stream3 = new import_node_stream.Readable({ read() {
|
|
7695
|
+
} });
|
|
7696
|
+
stream3.push(null);
|
|
7697
|
+
return stream3;
|
|
7698
|
+
}
|
|
7699
|
+
const content = Array.isArray(input) ? `${input.join("\n")}
|
|
7700
|
+
` : input;
|
|
7701
|
+
const stream2 = new import_node_stream.Readable({ read() {
|
|
7702
|
+
} });
|
|
7703
|
+
stream2.push(content);
|
|
7704
|
+
stream2.push(null);
|
|
7705
|
+
return stream2;
|
|
7706
|
+
}
|
|
7707
|
+
function createMockWritable() {
|
|
7708
|
+
const chunks = [];
|
|
7709
|
+
const stream2 = new import_node_stream.Writable({
|
|
7710
|
+
write(chunk, _encoding, callback) {
|
|
7711
|
+
chunks.push(Buffer.from(chunk));
|
|
7712
|
+
callback();
|
|
7713
|
+
}
|
|
7714
|
+
});
|
|
7715
|
+
stream2.getData = () => Buffer.concat(chunks).toString("utf8");
|
|
7716
|
+
return stream2;
|
|
7717
|
+
}
|
|
7718
|
+
async function collectOutput(stream2, timeout = 5e3) {
|
|
7719
|
+
return new Promise((resolve, reject) => {
|
|
7720
|
+
const chunks = [];
|
|
7721
|
+
const timeoutId = setTimeout(() => {
|
|
7722
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
7723
|
+
}, timeout);
|
|
7724
|
+
stream2.on("data", (chunk) => {
|
|
7725
|
+
chunks.push(Buffer.from(chunk));
|
|
7726
|
+
});
|
|
7727
|
+
stream2.on("end", () => {
|
|
7728
|
+
clearTimeout(timeoutId);
|
|
7729
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
7730
|
+
});
|
|
7731
|
+
stream2.on("error", (err) => {
|
|
7732
|
+
clearTimeout(timeoutId);
|
|
7733
|
+
reject(err);
|
|
7734
|
+
});
|
|
7735
|
+
});
|
|
7736
|
+
}
|
|
7737
|
+
function getBufferedOutput(stream2) {
|
|
7738
|
+
const chunks = [];
|
|
7739
|
+
for (; ; ) {
|
|
7740
|
+
const chunk = stream2.read();
|
|
7741
|
+
if (chunk === null) break;
|
|
7742
|
+
chunks.push(chunk);
|
|
7743
|
+
}
|
|
7744
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
7745
|
+
}
|
|
7746
|
+
function createMockPrompt(responses) {
|
|
7747
|
+
let index = 0;
|
|
7748
|
+
return async (_question) => {
|
|
7749
|
+
if (index >= responses.length) {
|
|
7750
|
+
throw new Error(`Mock prompt exhausted: no response for question ${index + 1}`);
|
|
7751
|
+
}
|
|
7752
|
+
return responses[index++];
|
|
7753
|
+
};
|
|
7754
|
+
}
|
|
7755
|
+
var MockPromptRecorder = class {
|
|
7756
|
+
responses;
|
|
7757
|
+
index = 0;
|
|
7758
|
+
questions = [];
|
|
7759
|
+
constructor(responses) {
|
|
7760
|
+
this.responses = responses;
|
|
7761
|
+
}
|
|
7762
|
+
/**
|
|
7763
|
+
* The prompt function to use in tests.
|
|
7764
|
+
*/
|
|
7765
|
+
prompt = async (question) => {
|
|
7766
|
+
this.questions.push(question);
|
|
7767
|
+
if (this.index >= this.responses.length) {
|
|
7768
|
+
throw new Error(`Mock prompt exhausted after ${this.index} questions`);
|
|
7769
|
+
}
|
|
7770
|
+
return this.responses[this.index++];
|
|
7771
|
+
};
|
|
7772
|
+
/**
|
|
7773
|
+
* Get all questions that were asked.
|
|
7774
|
+
*/
|
|
7775
|
+
getQuestions() {
|
|
7776
|
+
return [...this.questions];
|
|
7777
|
+
}
|
|
7778
|
+
/**
|
|
7779
|
+
* Get the number of questions asked.
|
|
7780
|
+
*/
|
|
7781
|
+
getQuestionCount() {
|
|
7782
|
+
return this.questions.length;
|
|
7783
|
+
}
|
|
7784
|
+
/**
|
|
7785
|
+
* Reset the recorder state.
|
|
7786
|
+
*/
|
|
7787
|
+
reset(newResponses) {
|
|
7788
|
+
this.index = 0;
|
|
7789
|
+
this.questions = [];
|
|
7790
|
+
if (newResponses) {
|
|
7791
|
+
this.responses = newResponses;
|
|
7792
|
+
}
|
|
7793
|
+
}
|
|
7794
|
+
};
|
|
7795
|
+
async function waitFor(condition, timeout = 5e3, interval = 50) {
|
|
7796
|
+
const startTime = Date.now();
|
|
7797
|
+
while (!condition()) {
|
|
7798
|
+
if (Date.now() - startTime > timeout) {
|
|
7799
|
+
throw new Error(`waitFor timed out after ${timeout}ms`);
|
|
7800
|
+
}
|
|
7801
|
+
await sleep3(interval);
|
|
7802
|
+
}
|
|
7803
|
+
}
|
|
7804
|
+
function sleep3(ms) {
|
|
7805
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7806
|
+
}
|
|
7807
|
+
function filterDefinedEnv(env) {
|
|
7808
|
+
const result = {};
|
|
7809
|
+
for (const [key, value] of Object.entries(env)) {
|
|
7810
|
+
if (value !== void 0) {
|
|
7811
|
+
result[key] = value;
|
|
7812
|
+
}
|
|
7813
|
+
}
|
|
7814
|
+
return result;
|
|
7815
|
+
}
|
|
6464
7816
|
// Annotate the CommonJS export names for ESM import in node:
|
|
6465
7817
|
0 && (module.exports = {
|
|
6466
7818
|
MockBuilder,
|
|
7819
|
+
MockConversationManager,
|
|
6467
7820
|
MockGadgetBuilder,
|
|
6468
7821
|
MockManager,
|
|
7822
|
+
MockPromptRecorder,
|
|
6469
7823
|
MockProviderAdapter,
|
|
7824
|
+
collectOutput,
|
|
7825
|
+
collectStream,
|
|
7826
|
+
collectStreamText,
|
|
7827
|
+
createAssistantMessage,
|
|
7828
|
+
createConversation,
|
|
7829
|
+
createConversationWithGadgets,
|
|
7830
|
+
createEmptyStream,
|
|
7831
|
+
createErrorStream,
|
|
7832
|
+
createLargeConversation,
|
|
7833
|
+
createMinimalConversation,
|
|
6470
7834
|
createMockAdapter,
|
|
6471
7835
|
createMockClient,
|
|
7836
|
+
createMockConversationManager,
|
|
6472
7837
|
createMockGadget,
|
|
7838
|
+
createMockPrompt,
|
|
7839
|
+
createMockReadable,
|
|
6473
7840
|
createMockStream,
|
|
7841
|
+
createMockWritable,
|
|
7842
|
+
createSystemMessage,
|
|
7843
|
+
createTestEnvironment,
|
|
7844
|
+
createTestStream,
|
|
6474
7845
|
createTextMockStream,
|
|
7846
|
+
createTextStream,
|
|
7847
|
+
createUserMessage,
|
|
7848
|
+
estimateTokens,
|
|
7849
|
+
getBufferedOutput,
|
|
6475
7850
|
getMockManager,
|
|
7851
|
+
getStreamFinalChunk,
|
|
6476
7852
|
mockGadget,
|
|
6477
7853
|
mockLLM,
|
|
6478
7854
|
testGadget,
|
|
6479
|
-
testGadgetBatch
|
|
7855
|
+
testGadgetBatch,
|
|
7856
|
+
waitFor
|
|
6480
7857
|
});
|
|
6481
7858
|
//# sourceMappingURL=index.cjs.map
|