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/index.cjs
CHANGED
|
@@ -375,10 +375,21 @@ function resolveRulesTemplate(rules, context) {
|
|
|
375
375
|
}
|
|
376
376
|
return [resolved];
|
|
377
377
|
}
|
|
378
|
-
|
|
378
|
+
function resolveHintTemplate(template, defaultValue, context) {
|
|
379
|
+
const resolved = template ?? defaultValue;
|
|
380
|
+
if (typeof resolved === "function") {
|
|
381
|
+
return resolved(context);
|
|
382
|
+
}
|
|
383
|
+
return resolved.replace(/\{iteration\}/g, String(context.iteration)).replace(/\{maxIterations\}/g, String(context.maxIterations)).replace(/\{remaining\}/g, String(context.remaining));
|
|
384
|
+
}
|
|
385
|
+
var DEFAULT_HINTS, DEFAULT_PROMPTS;
|
|
379
386
|
var init_prompt_config = __esm({
|
|
380
387
|
"src/core/prompt-config.ts"() {
|
|
381
388
|
"use strict";
|
|
389
|
+
DEFAULT_HINTS = {
|
|
390
|
+
parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.",
|
|
391
|
+
iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly."
|
|
392
|
+
};
|
|
382
393
|
DEFAULT_PROMPTS = {
|
|
383
394
|
mainInstruction: [
|
|
384
395
|
"\u26A0\uFE0F CRITICAL: RESPOND ONLY WITH GADGET INVOCATIONS",
|
|
@@ -1129,6 +1140,417 @@ var init_output_viewer = __esm({
|
|
|
1129
1140
|
}
|
|
1130
1141
|
});
|
|
1131
1142
|
|
|
1143
|
+
// src/agent/compaction/config.ts
|
|
1144
|
+
function resolveCompactionConfig(config = {}) {
|
|
1145
|
+
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
1146
|
+
const target = config.targetPercent ?? DEFAULT_COMPACTION_CONFIG.targetPercent;
|
|
1147
|
+
if (target >= trigger) {
|
|
1148
|
+
console.warn(
|
|
1149
|
+
`[llmist/compaction] targetPercent (${target}) should be less than triggerThresholdPercent (${trigger}) to be effective.`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
const strategy = config.strategy ?? DEFAULT_COMPACTION_CONFIG.strategy;
|
|
1153
|
+
const strategyName = typeof strategy === "object" && "name" in strategy ? strategy.name : strategy;
|
|
1154
|
+
return {
|
|
1155
|
+
enabled: config.enabled ?? DEFAULT_COMPACTION_CONFIG.enabled,
|
|
1156
|
+
strategy: strategyName,
|
|
1157
|
+
triggerThresholdPercent: trigger,
|
|
1158
|
+
targetPercent: target,
|
|
1159
|
+
preserveRecentTurns: config.preserveRecentTurns ?? DEFAULT_COMPACTION_CONFIG.preserveRecentTurns,
|
|
1160
|
+
summarizationModel: config.summarizationModel,
|
|
1161
|
+
summarizationPrompt: config.summarizationPrompt ?? DEFAULT_SUMMARIZATION_PROMPT,
|
|
1162
|
+
onCompaction: config.onCompaction
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
var DEFAULT_COMPACTION_CONFIG, DEFAULT_SUMMARIZATION_PROMPT;
|
|
1166
|
+
var init_config = __esm({
|
|
1167
|
+
"src/agent/compaction/config.ts"() {
|
|
1168
|
+
"use strict";
|
|
1169
|
+
DEFAULT_COMPACTION_CONFIG = {
|
|
1170
|
+
enabled: true,
|
|
1171
|
+
strategy: "hybrid",
|
|
1172
|
+
triggerThresholdPercent: 80,
|
|
1173
|
+
targetPercent: 50,
|
|
1174
|
+
preserveRecentTurns: 5
|
|
1175
|
+
};
|
|
1176
|
+
DEFAULT_SUMMARIZATION_PROMPT = `Summarize this conversation history concisely, preserving:
|
|
1177
|
+
1. Key decisions made and their rationale
|
|
1178
|
+
2. Important facts and data discovered
|
|
1179
|
+
3. Errors encountered and how they were resolved
|
|
1180
|
+
4. Current task context and goals
|
|
1181
|
+
|
|
1182
|
+
Format as a brief narrative paragraph, not bullet points.
|
|
1183
|
+
Previous conversation:`;
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
// src/agent/compaction/strategy.ts
|
|
1188
|
+
function groupIntoTurns(messages) {
|
|
1189
|
+
const turns = [];
|
|
1190
|
+
let currentTurn = [];
|
|
1191
|
+
for (const msg of messages) {
|
|
1192
|
+
if (msg.role === "user" && currentTurn.length > 0) {
|
|
1193
|
+
turns.push({
|
|
1194
|
+
messages: currentTurn,
|
|
1195
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1196
|
+
});
|
|
1197
|
+
currentTurn = [msg];
|
|
1198
|
+
} else {
|
|
1199
|
+
currentTurn.push(msg);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (currentTurn.length > 0) {
|
|
1203
|
+
turns.push({
|
|
1204
|
+
messages: currentTurn,
|
|
1205
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
return turns;
|
|
1209
|
+
}
|
|
1210
|
+
function estimateTurnTokens(messages) {
|
|
1211
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
1212
|
+
}
|
|
1213
|
+
function flattenTurns(turns) {
|
|
1214
|
+
return turns.flatMap((turn) => turn.messages);
|
|
1215
|
+
}
|
|
1216
|
+
var init_strategy = __esm({
|
|
1217
|
+
"src/agent/compaction/strategy.ts"() {
|
|
1218
|
+
"use strict";
|
|
1219
|
+
}
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
// src/agent/compaction/strategies/sliding-window.ts
|
|
1223
|
+
var TRUNCATION_MARKER_TEMPLATE, SlidingWindowStrategy;
|
|
1224
|
+
var init_sliding_window = __esm({
|
|
1225
|
+
"src/agent/compaction/strategies/sliding-window.ts"() {
|
|
1226
|
+
"use strict";
|
|
1227
|
+
init_strategy();
|
|
1228
|
+
TRUNCATION_MARKER_TEMPLATE = "[Previous conversation truncated. Removed {count} turn(s) to fit context window.]";
|
|
1229
|
+
SlidingWindowStrategy = class {
|
|
1230
|
+
name = "sliding-window";
|
|
1231
|
+
async compact(messages, config, context) {
|
|
1232
|
+
const turns = groupIntoTurns(messages);
|
|
1233
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1234
|
+
if (turns.length <= preserveCount) {
|
|
1235
|
+
return {
|
|
1236
|
+
messages,
|
|
1237
|
+
strategyName: this.name,
|
|
1238
|
+
metadata: {
|
|
1239
|
+
originalCount: messages.length,
|
|
1240
|
+
compactedCount: messages.length,
|
|
1241
|
+
tokensBefore: context.currentTokens,
|
|
1242
|
+
tokensAfter: context.currentTokens
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1247
|
+
const turnsRemoved = turns.length - preserveCount;
|
|
1248
|
+
const truncationMarker = {
|
|
1249
|
+
role: "user",
|
|
1250
|
+
content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
|
|
1251
|
+
};
|
|
1252
|
+
const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
|
|
1253
|
+
const tokensAfter = Math.ceil(
|
|
1254
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1255
|
+
);
|
|
1256
|
+
return {
|
|
1257
|
+
messages: compactedMessages,
|
|
1258
|
+
strategyName: this.name,
|
|
1259
|
+
metadata: {
|
|
1260
|
+
originalCount: messages.length,
|
|
1261
|
+
compactedCount: compactedMessages.length,
|
|
1262
|
+
tokensBefore: context.currentTokens,
|
|
1263
|
+
tokensAfter
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
// src/agent/compaction/strategies/summarization.ts
|
|
1272
|
+
var SummarizationStrategy;
|
|
1273
|
+
var init_summarization = __esm({
|
|
1274
|
+
"src/agent/compaction/strategies/summarization.ts"() {
|
|
1275
|
+
"use strict";
|
|
1276
|
+
init_strategy();
|
|
1277
|
+
SummarizationStrategy = class {
|
|
1278
|
+
name = "summarization";
|
|
1279
|
+
async compact(messages, config, context) {
|
|
1280
|
+
const turns = groupIntoTurns(messages);
|
|
1281
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1282
|
+
if (turns.length <= preserveCount) {
|
|
1283
|
+
return {
|
|
1284
|
+
messages,
|
|
1285
|
+
strategyName: this.name,
|
|
1286
|
+
metadata: {
|
|
1287
|
+
originalCount: messages.length,
|
|
1288
|
+
compactedCount: messages.length,
|
|
1289
|
+
tokensBefore: context.currentTokens,
|
|
1290
|
+
tokensAfter: context.currentTokens
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
const turnsToSummarize = turns.slice(0, -preserveCount);
|
|
1295
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1296
|
+
const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
|
|
1297
|
+
const summary = await this.generateSummary(conversationToSummarize, config, context);
|
|
1298
|
+
const summaryMessage = {
|
|
1299
|
+
role: "user",
|
|
1300
|
+
content: `[Previous conversation summary]
|
|
1301
|
+
${summary}
|
|
1302
|
+
[End of summary - conversation continues below]`
|
|
1303
|
+
};
|
|
1304
|
+
const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
|
|
1305
|
+
const tokensAfter = Math.ceil(
|
|
1306
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1307
|
+
);
|
|
1308
|
+
return {
|
|
1309
|
+
messages: compactedMessages,
|
|
1310
|
+
summary,
|
|
1311
|
+
strategyName: this.name,
|
|
1312
|
+
metadata: {
|
|
1313
|
+
originalCount: messages.length,
|
|
1314
|
+
compactedCount: compactedMessages.length,
|
|
1315
|
+
tokensBefore: context.currentTokens,
|
|
1316
|
+
tokensAfter
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Formats messages into a readable conversation format for summarization.
|
|
1322
|
+
*/
|
|
1323
|
+
formatTurnsForSummary(messages) {
|
|
1324
|
+
return messages.map((msg) => {
|
|
1325
|
+
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
1326
|
+
return `${role}: ${msg.content}`;
|
|
1327
|
+
}).join("\n\n");
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Generates a summary using the configured LLM.
|
|
1331
|
+
*/
|
|
1332
|
+
async generateSummary(conversation, config, context) {
|
|
1333
|
+
const model = config.summarizationModel ?? context.model;
|
|
1334
|
+
const prompt = `${config.summarizationPrompt}
|
|
1335
|
+
|
|
1336
|
+
${conversation}`;
|
|
1337
|
+
const response = await context.client.complete(prompt, {
|
|
1338
|
+
model,
|
|
1339
|
+
temperature: 0.3
|
|
1340
|
+
// Low temperature for factual summarization
|
|
1341
|
+
});
|
|
1342
|
+
return response.trim();
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
// src/agent/compaction/strategies/hybrid.ts
|
|
1349
|
+
var MIN_TURNS_FOR_SUMMARIZATION, HybridStrategy;
|
|
1350
|
+
var init_hybrid = __esm({
|
|
1351
|
+
"src/agent/compaction/strategies/hybrid.ts"() {
|
|
1352
|
+
"use strict";
|
|
1353
|
+
init_strategy();
|
|
1354
|
+
init_sliding_window();
|
|
1355
|
+
init_summarization();
|
|
1356
|
+
MIN_TURNS_FOR_SUMMARIZATION = 3;
|
|
1357
|
+
HybridStrategy = class {
|
|
1358
|
+
name = "hybrid";
|
|
1359
|
+
slidingWindow = new SlidingWindowStrategy();
|
|
1360
|
+
summarization = new SummarizationStrategy();
|
|
1361
|
+
async compact(messages, config, context) {
|
|
1362
|
+
const turns = groupIntoTurns(messages);
|
|
1363
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1364
|
+
if (turns.length <= preserveCount) {
|
|
1365
|
+
return {
|
|
1366
|
+
messages,
|
|
1367
|
+
strategyName: this.name,
|
|
1368
|
+
metadata: {
|
|
1369
|
+
originalCount: messages.length,
|
|
1370
|
+
compactedCount: messages.length,
|
|
1371
|
+
tokensBefore: context.currentTokens,
|
|
1372
|
+
tokensAfter: context.currentTokens
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
const turnsToSummarize = turns.length - preserveCount;
|
|
1377
|
+
if (turnsToSummarize < MIN_TURNS_FOR_SUMMARIZATION) {
|
|
1378
|
+
return this.slidingWindow.compact(messages, config, context);
|
|
1379
|
+
}
|
|
1380
|
+
return this.summarization.compact(messages, config, context);
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
// src/agent/compaction/strategies/index.ts
|
|
1387
|
+
var init_strategies = __esm({
|
|
1388
|
+
"src/agent/compaction/strategies/index.ts"() {
|
|
1389
|
+
"use strict";
|
|
1390
|
+
init_sliding_window();
|
|
1391
|
+
init_summarization();
|
|
1392
|
+
init_hybrid();
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
// src/agent/compaction/manager.ts
|
|
1397
|
+
function createStrategy(name) {
|
|
1398
|
+
switch (name) {
|
|
1399
|
+
case "sliding-window":
|
|
1400
|
+
return new SlidingWindowStrategy();
|
|
1401
|
+
case "summarization":
|
|
1402
|
+
return new SummarizationStrategy();
|
|
1403
|
+
case "hybrid":
|
|
1404
|
+
return new HybridStrategy();
|
|
1405
|
+
default:
|
|
1406
|
+
throw new Error(`Unknown compaction strategy: ${name}`);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
var CompactionManager;
|
|
1410
|
+
var init_manager = __esm({
|
|
1411
|
+
"src/agent/compaction/manager.ts"() {
|
|
1412
|
+
"use strict";
|
|
1413
|
+
init_config();
|
|
1414
|
+
init_strategies();
|
|
1415
|
+
CompactionManager = class {
|
|
1416
|
+
client;
|
|
1417
|
+
model;
|
|
1418
|
+
config;
|
|
1419
|
+
strategy;
|
|
1420
|
+
modelLimits;
|
|
1421
|
+
// Statistics
|
|
1422
|
+
totalCompactions = 0;
|
|
1423
|
+
totalTokensSaved = 0;
|
|
1424
|
+
lastTokenCount = 0;
|
|
1425
|
+
constructor(client, model, config = {}) {
|
|
1426
|
+
this.client = client;
|
|
1427
|
+
this.model = model;
|
|
1428
|
+
this.config = resolveCompactionConfig(config);
|
|
1429
|
+
if (typeof config.strategy === "object" && "compact" in config.strategy) {
|
|
1430
|
+
this.strategy = config.strategy;
|
|
1431
|
+
} else {
|
|
1432
|
+
this.strategy = createStrategy(this.config.strategy);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Check if compaction is needed and perform it if so.
|
|
1437
|
+
*
|
|
1438
|
+
* @param conversation - The conversation manager to compact
|
|
1439
|
+
* @param iteration - Current agent iteration (for event metadata)
|
|
1440
|
+
* @returns CompactionEvent if compaction was performed, null otherwise
|
|
1441
|
+
*/
|
|
1442
|
+
async checkAndCompact(conversation, iteration) {
|
|
1443
|
+
if (!this.config.enabled) {
|
|
1444
|
+
return null;
|
|
1445
|
+
}
|
|
1446
|
+
if (!this.modelLimits) {
|
|
1447
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1448
|
+
if (!this.modelLimits) {
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (!this.client.countTokens) {
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
const messages = conversation.getMessages();
|
|
1456
|
+
const currentTokens = await this.client.countTokens(this.model, messages);
|
|
1457
|
+
this.lastTokenCount = currentTokens;
|
|
1458
|
+
const usagePercent = currentTokens / this.modelLimits.contextWindow * 100;
|
|
1459
|
+
if (usagePercent < this.config.triggerThresholdPercent) {
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
const historyMessages = conversation.getHistoryMessages();
|
|
1463
|
+
const baseMessages = conversation.getBaseMessages();
|
|
1464
|
+
const historyTokens = await this.client.countTokens(this.model, historyMessages);
|
|
1465
|
+
const baseTokens = await this.client.countTokens(this.model, baseMessages);
|
|
1466
|
+
return this.compact(conversation, iteration, {
|
|
1467
|
+
historyMessages,
|
|
1468
|
+
baseMessages,
|
|
1469
|
+
historyTokens,
|
|
1470
|
+
baseTokens,
|
|
1471
|
+
currentTokens: historyTokens + baseTokens
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Force compaction regardless of threshold.
|
|
1476
|
+
*
|
|
1477
|
+
* @param conversation - The conversation manager to compact
|
|
1478
|
+
* @param iteration - Current agent iteration (for event metadata). Use -1 for manual compaction.
|
|
1479
|
+
* @param precomputed - Optional pre-computed token counts (passed from checkAndCompact for efficiency)
|
|
1480
|
+
* @returns CompactionEvent with compaction details
|
|
1481
|
+
*/
|
|
1482
|
+
async compact(conversation, iteration, precomputed) {
|
|
1483
|
+
if (!this.modelLimits) {
|
|
1484
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1485
|
+
if (!this.modelLimits) {
|
|
1486
|
+
return null;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
const historyMessages = precomputed?.historyMessages ?? conversation.getHistoryMessages();
|
|
1490
|
+
const baseMessages = precomputed?.baseMessages ?? conversation.getBaseMessages();
|
|
1491
|
+
const historyTokens = precomputed?.historyTokens ?? await this.client.countTokens(this.model, historyMessages);
|
|
1492
|
+
const baseTokens = precomputed?.baseTokens ?? await this.client.countTokens(this.model, baseMessages);
|
|
1493
|
+
const currentTokens = precomputed?.currentTokens ?? historyTokens + baseTokens;
|
|
1494
|
+
const targetTotalTokens = Math.floor(
|
|
1495
|
+
this.modelLimits.contextWindow * this.config.targetPercent / 100
|
|
1496
|
+
);
|
|
1497
|
+
const targetHistoryTokens = Math.max(0, targetTotalTokens - baseTokens);
|
|
1498
|
+
const result = await this.strategy.compact(historyMessages, this.config, {
|
|
1499
|
+
currentTokens: historyTokens,
|
|
1500
|
+
targetTokens: targetHistoryTokens,
|
|
1501
|
+
modelLimits: this.modelLimits,
|
|
1502
|
+
client: this.client,
|
|
1503
|
+
model: this.config.summarizationModel ?? this.model
|
|
1504
|
+
});
|
|
1505
|
+
conversation.replaceHistory(result.messages);
|
|
1506
|
+
const afterTokens = await this.client.countTokens(this.model, conversation.getMessages());
|
|
1507
|
+
const tokensSaved = currentTokens - afterTokens;
|
|
1508
|
+
this.totalCompactions++;
|
|
1509
|
+
this.totalTokensSaved += tokensSaved;
|
|
1510
|
+
this.lastTokenCount = afterTokens;
|
|
1511
|
+
const event = {
|
|
1512
|
+
strategy: result.strategyName,
|
|
1513
|
+
tokensBefore: currentTokens,
|
|
1514
|
+
tokensAfter: afterTokens,
|
|
1515
|
+
messagesBefore: historyMessages.length + baseMessages.length,
|
|
1516
|
+
messagesAfter: result.messages.length + baseMessages.length,
|
|
1517
|
+
summary: result.summary,
|
|
1518
|
+
iteration
|
|
1519
|
+
};
|
|
1520
|
+
if (this.config.onCompaction) {
|
|
1521
|
+
try {
|
|
1522
|
+
this.config.onCompaction(event);
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
console.warn("[llmist/compaction] onCompaction callback error:", err);
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
return event;
|
|
1528
|
+
}
|
|
1529
|
+
/**
|
|
1530
|
+
* Get compaction statistics.
|
|
1531
|
+
*/
|
|
1532
|
+
getStats() {
|
|
1533
|
+
const contextWindow = this.modelLimits?.contextWindow ?? 0;
|
|
1534
|
+
return {
|
|
1535
|
+
totalCompactions: this.totalCompactions,
|
|
1536
|
+
totalTokensSaved: this.totalTokensSaved,
|
|
1537
|
+
currentUsage: {
|
|
1538
|
+
tokens: this.lastTokenCount,
|
|
1539
|
+
percent: contextWindow > 0 ? this.lastTokenCount / contextWindow * 100 : 0
|
|
1540
|
+
},
|
|
1541
|
+
contextWindow
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Check if compaction is enabled.
|
|
1546
|
+
*/
|
|
1547
|
+
isEnabled() {
|
|
1548
|
+
return this.config.enabled;
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1132
1554
|
// src/agent/gadget-output-store.ts
|
|
1133
1555
|
var import_node_crypto, GadgetOutputStore;
|
|
1134
1556
|
var init_gadget_output_store = __esm({
|
|
@@ -1231,10 +1653,16 @@ var init_conversation_manager = __esm({
|
|
|
1231
1653
|
baseMessages;
|
|
1232
1654
|
initialMessages;
|
|
1233
1655
|
historyBuilder;
|
|
1656
|
+
startPrefix;
|
|
1657
|
+
endPrefix;
|
|
1658
|
+
argPrefix;
|
|
1234
1659
|
constructor(baseMessages, initialMessages, options = {}) {
|
|
1235
1660
|
this.baseMessages = baseMessages;
|
|
1236
1661
|
this.initialMessages = initialMessages;
|
|
1237
1662
|
this.historyBuilder = new LLMMessageBuilder();
|
|
1663
|
+
this.startPrefix = options.startPrefix;
|
|
1664
|
+
this.endPrefix = options.endPrefix;
|
|
1665
|
+
this.argPrefix = options.argPrefix;
|
|
1238
1666
|
if (options.startPrefix && options.endPrefix) {
|
|
1239
1667
|
this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
|
|
1240
1668
|
}
|
|
@@ -1251,6 +1679,25 @@ var init_conversation_manager = __esm({
|
|
|
1251
1679
|
getMessages() {
|
|
1252
1680
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
1253
1681
|
}
|
|
1682
|
+
getHistoryMessages() {
|
|
1683
|
+
return this.historyBuilder.build();
|
|
1684
|
+
}
|
|
1685
|
+
getBaseMessages() {
|
|
1686
|
+
return [...this.baseMessages, ...this.initialMessages];
|
|
1687
|
+
}
|
|
1688
|
+
replaceHistory(newHistory) {
|
|
1689
|
+
this.historyBuilder = new LLMMessageBuilder();
|
|
1690
|
+
if (this.startPrefix && this.endPrefix) {
|
|
1691
|
+
this.historyBuilder.withPrefixes(this.startPrefix, this.endPrefix, this.argPrefix);
|
|
1692
|
+
}
|
|
1693
|
+
for (const msg of newHistory) {
|
|
1694
|
+
if (msg.role === "user") {
|
|
1695
|
+
this.historyBuilder.addUser(msg.content);
|
|
1696
|
+
} else if (msg.role === "assistant") {
|
|
1697
|
+
this.historyBuilder.addAssistant(msg.content);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1254
1701
|
};
|
|
1255
1702
|
}
|
|
1256
1703
|
});
|
|
@@ -1493,334 +1940,241 @@ var init_hook_validators = __esm({
|
|
|
1493
1940
|
}
|
|
1494
1941
|
});
|
|
1495
1942
|
|
|
1496
|
-
// src/gadgets/
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1943
|
+
// src/gadgets/schema-introspector.ts
|
|
1944
|
+
function getDef(schema) {
|
|
1945
|
+
return schema._def;
|
|
1946
|
+
}
|
|
1947
|
+
function getTypeName(schema) {
|
|
1948
|
+
const def = getDef(schema);
|
|
1949
|
+
return def?.type ?? def?.typeName;
|
|
1950
|
+
}
|
|
1951
|
+
function getShape(schema) {
|
|
1952
|
+
const def = getDef(schema);
|
|
1953
|
+
if (typeof def?.shape === "function") {
|
|
1954
|
+
return def.shape();
|
|
1955
|
+
}
|
|
1956
|
+
return def?.shape;
|
|
1957
|
+
}
|
|
1958
|
+
var SchemaIntrospector;
|
|
1959
|
+
var init_schema_introspector = __esm({
|
|
1960
|
+
"src/gadgets/schema-introspector.ts"() {
|
|
1500
1961
|
"use strict";
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
constructor(options = {}) {
|
|
1507
|
-
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1508
|
-
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
1509
|
-
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
1962
|
+
SchemaIntrospector = class {
|
|
1963
|
+
schema;
|
|
1964
|
+
cache = /* @__PURE__ */ new Map();
|
|
1965
|
+
constructor(schema) {
|
|
1966
|
+
this.schema = schema;
|
|
1510
1967
|
}
|
|
1511
1968
|
/**
|
|
1512
|
-
*
|
|
1969
|
+
* Get the expected type at a JSON pointer path.
|
|
1513
1970
|
*
|
|
1514
|
-
* @param
|
|
1515
|
-
* @
|
|
1516
|
-
* @param gadget - The gadget instance (for generating instructions)
|
|
1517
|
-
* @returns Formatted error message with usage instructions
|
|
1971
|
+
* @param pointer - JSON pointer path without leading / (e.g., "config/timeout", "items/0")
|
|
1972
|
+
* @returns Type hint for coercion decision
|
|
1518
1973
|
*/
|
|
1519
|
-
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
const path = issue.path.join(".") || "root";
|
|
1524
|
-
parts.push(` - ${path}: ${issue.message}`);
|
|
1974
|
+
getTypeAtPath(pointer) {
|
|
1975
|
+
const cached = this.cache.get(pointer);
|
|
1976
|
+
if (cached !== void 0) {
|
|
1977
|
+
return cached;
|
|
1525
1978
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
return parts.join("\n");
|
|
1979
|
+
const result = this.resolveTypeAtPath(pointer);
|
|
1980
|
+
this.cache.set(pointer, result);
|
|
1981
|
+
return result;
|
|
1530
1982
|
}
|
|
1531
1983
|
/**
|
|
1532
|
-
*
|
|
1533
|
-
*
|
|
1534
|
-
* @param gadgetName - Name of the gadget that was called
|
|
1535
|
-
* @param parseError - The parse error message
|
|
1536
|
-
* @param gadget - The gadget instance if found (for generating instructions)
|
|
1537
|
-
* @returns Formatted error message with format reference
|
|
1984
|
+
* Internal method to resolve type at path without caching.
|
|
1538
1985
|
*/
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1986
|
+
resolveTypeAtPath(pointer) {
|
|
1987
|
+
if (!pointer) {
|
|
1988
|
+
return this.getBaseType(this.schema);
|
|
1989
|
+
}
|
|
1990
|
+
const segments = pointer.split("/");
|
|
1991
|
+
let current = this.schema;
|
|
1992
|
+
for (const segment of segments) {
|
|
1993
|
+
current = this.unwrapSchema(current);
|
|
1994
|
+
const typeName = getTypeName(current);
|
|
1995
|
+
if (typeName === "object" || typeName === "ZodObject") {
|
|
1996
|
+
const shape = getShape(current);
|
|
1997
|
+
if (!shape || !(segment in shape)) {
|
|
1998
|
+
return "unknown";
|
|
1999
|
+
}
|
|
2000
|
+
current = shape[segment];
|
|
2001
|
+
} else if (typeName === "array" || typeName === "ZodArray") {
|
|
2002
|
+
if (!/^\d+$/.test(segment)) {
|
|
2003
|
+
return "unknown";
|
|
2004
|
+
}
|
|
2005
|
+
const def = getDef(current);
|
|
2006
|
+
const elementType = def?.element ?? def?.type;
|
|
2007
|
+
if (!elementType) {
|
|
2008
|
+
return "unknown";
|
|
2009
|
+
}
|
|
2010
|
+
current = elementType;
|
|
2011
|
+
} else if (typeName === "tuple" || typeName === "ZodTuple") {
|
|
2012
|
+
if (!/^\d+$/.test(segment)) {
|
|
2013
|
+
return "unknown";
|
|
2014
|
+
}
|
|
2015
|
+
const index = parseInt(segment, 10);
|
|
2016
|
+
const def = getDef(current);
|
|
2017
|
+
const items = def?.items;
|
|
2018
|
+
if (!items || index >= items.length) {
|
|
2019
|
+
return "unknown";
|
|
2020
|
+
}
|
|
2021
|
+
current = items[index];
|
|
2022
|
+
} else if (typeName === "record" || typeName === "ZodRecord") {
|
|
2023
|
+
const def = getDef(current);
|
|
2024
|
+
const valueType = def?.valueType;
|
|
2025
|
+
if (!valueType) {
|
|
2026
|
+
return "unknown";
|
|
2027
|
+
}
|
|
2028
|
+
current = valueType;
|
|
2029
|
+
} else {
|
|
2030
|
+
return "unknown";
|
|
2031
|
+
}
|
|
1547
2032
|
}
|
|
1548
|
-
|
|
1549
|
-
parts.push("Block Format Reference:");
|
|
1550
|
-
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
1551
|
-
parts.push(` ${this.argPrefix}parameterName`);
|
|
1552
|
-
parts.push(" parameter value here");
|
|
1553
|
-
parts.push(` ${this.endPrefix}`);
|
|
1554
|
-
return parts.join("\n");
|
|
2033
|
+
return this.getBaseType(current);
|
|
1555
2034
|
}
|
|
1556
2035
|
/**
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1559
|
-
* @param gadgetName - Name of the gadget that was not found
|
|
1560
|
-
* @param availableGadgets - List of available gadget names
|
|
1561
|
-
* @returns Formatted error message with available gadgets
|
|
2036
|
+
* Unwrap schema modifiers (optional, default, nullable, branded, etc.)
|
|
2037
|
+
* to get to the underlying type.
|
|
1562
2038
|
*/
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
2039
|
+
unwrapSchema(schema) {
|
|
2040
|
+
let current = schema;
|
|
2041
|
+
let iterations = 0;
|
|
2042
|
+
const maxIterations = 20;
|
|
2043
|
+
while (iterations < maxIterations) {
|
|
2044
|
+
const typeName = getTypeName(current);
|
|
2045
|
+
const wrapperTypes = [
|
|
2046
|
+
"optional",
|
|
2047
|
+
"nullable",
|
|
2048
|
+
"default",
|
|
2049
|
+
"catch",
|
|
2050
|
+
"branded",
|
|
2051
|
+
"readonly",
|
|
2052
|
+
"pipeline",
|
|
2053
|
+
"ZodOptional",
|
|
2054
|
+
"ZodNullable",
|
|
2055
|
+
"ZodDefault",
|
|
2056
|
+
"ZodCatch",
|
|
2057
|
+
"ZodBranded",
|
|
2058
|
+
"ZodReadonly",
|
|
2059
|
+
"ZodPipeline"
|
|
2060
|
+
];
|
|
2061
|
+
if (typeName && wrapperTypes.includes(typeName)) {
|
|
2062
|
+
const def = getDef(current);
|
|
2063
|
+
const inner = def?.innerType ?? def?.in ?? def?.type;
|
|
2064
|
+
if (!inner || inner === current) break;
|
|
2065
|
+
current = inner;
|
|
2066
|
+
iterations++;
|
|
2067
|
+
continue;
|
|
2068
|
+
}
|
|
2069
|
+
break;
|
|
1572
2070
|
}
|
|
1573
|
-
return
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1578
|
-
|
|
1579
|
-
// src/gadgets/exceptions.ts
|
|
1580
|
-
var BreakLoopException, HumanInputException, TimeoutException;
|
|
1581
|
-
var init_exceptions = __esm({
|
|
1582
|
-
"src/gadgets/exceptions.ts"() {
|
|
1583
|
-
"use strict";
|
|
1584
|
-
BreakLoopException = class extends Error {
|
|
1585
|
-
constructor(message) {
|
|
1586
|
-
super(message ?? "Agent loop terminated by gadget");
|
|
1587
|
-
this.name = "BreakLoopException";
|
|
1588
|
-
}
|
|
1589
|
-
};
|
|
1590
|
-
HumanInputException = class extends Error {
|
|
1591
|
-
question;
|
|
1592
|
-
constructor(question) {
|
|
1593
|
-
super(`Human input required: ${question}`);
|
|
1594
|
-
this.name = "HumanInputException";
|
|
1595
|
-
this.question = question;
|
|
2071
|
+
return current;
|
|
1596
2072
|
}
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
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
|
-
return
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
this.logger.debug("Executing gadget with timeout", {
|
|
1703
|
-
gadgetName: call.gadgetName,
|
|
1704
|
-
timeoutMs
|
|
1705
|
-
});
|
|
1706
|
-
result = await Promise.race([
|
|
1707
|
-
Promise.resolve(gadget.execute(validatedParameters)),
|
|
1708
|
-
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
1709
|
-
]);
|
|
1710
|
-
} else {
|
|
1711
|
-
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
1712
|
-
}
|
|
1713
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1714
|
-
this.logger.info("Gadget executed successfully", {
|
|
1715
|
-
gadgetName: call.gadgetName,
|
|
1716
|
-
invocationId: call.invocationId,
|
|
1717
|
-
executionTimeMs
|
|
1718
|
-
});
|
|
1719
|
-
this.logger.debug("Gadget result", {
|
|
1720
|
-
gadgetName: call.gadgetName,
|
|
1721
|
-
invocationId: call.invocationId,
|
|
1722
|
-
parameters: validatedParameters,
|
|
1723
|
-
result,
|
|
1724
|
-
executionTimeMs
|
|
1725
|
-
});
|
|
1726
|
-
return {
|
|
1727
|
-
gadgetName: call.gadgetName,
|
|
1728
|
-
invocationId: call.invocationId,
|
|
1729
|
-
parameters: validatedParameters,
|
|
1730
|
-
result,
|
|
1731
|
-
executionTimeMs
|
|
1732
|
-
};
|
|
1733
|
-
} catch (error) {
|
|
1734
|
-
if (error instanceof BreakLoopException) {
|
|
1735
|
-
this.logger.info("Gadget requested loop termination", {
|
|
1736
|
-
gadgetName: call.gadgetName,
|
|
1737
|
-
message: error.message
|
|
1738
|
-
});
|
|
1739
|
-
return {
|
|
1740
|
-
gadgetName: call.gadgetName,
|
|
1741
|
-
invocationId: call.invocationId,
|
|
1742
|
-
parameters: validatedParameters,
|
|
1743
|
-
result: error.message,
|
|
1744
|
-
breaksLoop: true,
|
|
1745
|
-
executionTimeMs: Date.now() - startTime
|
|
1746
|
-
};
|
|
1747
|
-
}
|
|
1748
|
-
if (error instanceof TimeoutException) {
|
|
1749
|
-
this.logger.error("Gadget execution timed out", {
|
|
1750
|
-
gadgetName: call.gadgetName,
|
|
1751
|
-
timeoutMs: error.timeoutMs,
|
|
1752
|
-
executionTimeMs: Date.now() - startTime
|
|
1753
|
-
});
|
|
1754
|
-
return {
|
|
1755
|
-
gadgetName: call.gadgetName,
|
|
1756
|
-
invocationId: call.invocationId,
|
|
1757
|
-
parameters: validatedParameters,
|
|
1758
|
-
error: error.message,
|
|
1759
|
-
executionTimeMs: Date.now() - startTime
|
|
1760
|
-
};
|
|
1761
|
-
}
|
|
1762
|
-
if (error instanceof HumanInputException) {
|
|
1763
|
-
this.logger.info("Gadget requested human input", {
|
|
1764
|
-
gadgetName: call.gadgetName,
|
|
1765
|
-
question: error.question
|
|
1766
|
-
});
|
|
1767
|
-
if (this.onHumanInputRequired) {
|
|
1768
|
-
try {
|
|
1769
|
-
const answer = await this.onHumanInputRequired(error.question);
|
|
1770
|
-
this.logger.debug("Human input received", {
|
|
1771
|
-
gadgetName: call.gadgetName,
|
|
1772
|
-
answerLength: answer.length
|
|
1773
|
-
});
|
|
1774
|
-
return {
|
|
1775
|
-
gadgetName: call.gadgetName,
|
|
1776
|
-
invocationId: call.invocationId,
|
|
1777
|
-
parameters: validatedParameters,
|
|
1778
|
-
result: answer,
|
|
1779
|
-
executionTimeMs: Date.now() - startTime
|
|
1780
|
-
};
|
|
1781
|
-
} catch (inputError) {
|
|
1782
|
-
this.logger.error("Human input callback error", {
|
|
1783
|
-
gadgetName: call.gadgetName,
|
|
1784
|
-
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
1785
|
-
});
|
|
1786
|
-
return {
|
|
1787
|
-
gadgetName: call.gadgetName,
|
|
1788
|
-
invocationId: call.invocationId,
|
|
1789
|
-
parameters: validatedParameters,
|
|
1790
|
-
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
1791
|
-
executionTimeMs: Date.now() - startTime
|
|
1792
|
-
};
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
this.logger.warn("Human input required but no callback provided", {
|
|
1796
|
-
gadgetName: call.gadgetName
|
|
1797
|
-
});
|
|
1798
|
-
return {
|
|
1799
|
-
gadgetName: call.gadgetName,
|
|
1800
|
-
invocationId: call.invocationId,
|
|
1801
|
-
parameters: validatedParameters,
|
|
1802
|
-
error: "Human input required but not available (stdin is not interactive)",
|
|
1803
|
-
executionTimeMs: Date.now() - startTime
|
|
1804
|
-
};
|
|
1805
|
-
}
|
|
1806
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1807
|
-
this.logger.error("Gadget execution failed", {
|
|
1808
|
-
gadgetName: call.gadgetName,
|
|
1809
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1810
|
-
executionTimeMs
|
|
1811
|
-
});
|
|
1812
|
-
return {
|
|
1813
|
-
gadgetName: call.gadgetName,
|
|
1814
|
-
invocationId: call.invocationId,
|
|
1815
|
-
parameters: validatedParameters,
|
|
1816
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1817
|
-
executionTimeMs
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
// Execute multiple gadget calls in parallel
|
|
1822
|
-
async executeAll(calls) {
|
|
1823
|
-
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2073
|
+
/**
|
|
2074
|
+
* Get the primitive type hint from an unwrapped schema.
|
|
2075
|
+
*/
|
|
2076
|
+
getBaseType(schema) {
|
|
2077
|
+
const unwrapped = this.unwrapSchema(schema);
|
|
2078
|
+
const typeName = getTypeName(unwrapped);
|
|
2079
|
+
switch (typeName) {
|
|
2080
|
+
// Primitive types
|
|
2081
|
+
case "string":
|
|
2082
|
+
case "ZodString":
|
|
2083
|
+
return "string";
|
|
2084
|
+
case "number":
|
|
2085
|
+
case "ZodNumber":
|
|
2086
|
+
case "bigint":
|
|
2087
|
+
case "ZodBigInt":
|
|
2088
|
+
return "number";
|
|
2089
|
+
case "boolean":
|
|
2090
|
+
case "ZodBoolean":
|
|
2091
|
+
return "boolean";
|
|
2092
|
+
// Literal types - check the literal value type
|
|
2093
|
+
case "literal":
|
|
2094
|
+
case "ZodLiteral": {
|
|
2095
|
+
const def = getDef(unwrapped);
|
|
2096
|
+
const values = def?.values;
|
|
2097
|
+
const value = values?.[0] ?? def?.value;
|
|
2098
|
+
if (typeof value === "string") return "string";
|
|
2099
|
+
if (typeof value === "number" || typeof value === "bigint")
|
|
2100
|
+
return "number";
|
|
2101
|
+
if (typeof value === "boolean") return "boolean";
|
|
2102
|
+
return "unknown";
|
|
2103
|
+
}
|
|
2104
|
+
// Enum - always string keys
|
|
2105
|
+
case "enum":
|
|
2106
|
+
case "ZodEnum":
|
|
2107
|
+
case "nativeEnum":
|
|
2108
|
+
case "ZodNativeEnum":
|
|
2109
|
+
return "string";
|
|
2110
|
+
// Union - return 'unknown' to let auto-coercion decide
|
|
2111
|
+
// Since multiple types are valid, we can't definitively say what the LLM intended
|
|
2112
|
+
// Auto-coercion will handle common cases (numbers, booleans) appropriately
|
|
2113
|
+
case "union":
|
|
2114
|
+
case "ZodUnion":
|
|
2115
|
+
return "unknown";
|
|
2116
|
+
// Discriminated union - complex, return unknown
|
|
2117
|
+
case "discriminatedUnion":
|
|
2118
|
+
case "ZodDiscriminatedUnion":
|
|
2119
|
+
return "unknown";
|
|
2120
|
+
// Intersection - check both sides
|
|
2121
|
+
case "intersection":
|
|
2122
|
+
case "ZodIntersection": {
|
|
2123
|
+
const def = getDef(unwrapped);
|
|
2124
|
+
const left = def?.left;
|
|
2125
|
+
const right = def?.right;
|
|
2126
|
+
if (!left || !right) return "unknown";
|
|
2127
|
+
const leftType = this.getBaseType(left);
|
|
2128
|
+
const rightType = this.getBaseType(right);
|
|
2129
|
+
if (leftType === rightType) return leftType;
|
|
2130
|
+
if (leftType === "string" || rightType === "string") return "string";
|
|
2131
|
+
return "unknown";
|
|
2132
|
+
}
|
|
2133
|
+
// Effects/transforms - return unknown to let Zod handle it
|
|
2134
|
+
case "effects":
|
|
2135
|
+
case "ZodEffects":
|
|
2136
|
+
return "unknown";
|
|
2137
|
+
// Lazy - can't resolve without evaluating
|
|
2138
|
+
case "lazy":
|
|
2139
|
+
case "ZodLazy":
|
|
2140
|
+
return "unknown";
|
|
2141
|
+
// Complex types - return unknown
|
|
2142
|
+
case "object":
|
|
2143
|
+
case "ZodObject":
|
|
2144
|
+
case "array":
|
|
2145
|
+
case "ZodArray":
|
|
2146
|
+
case "tuple":
|
|
2147
|
+
case "ZodTuple":
|
|
2148
|
+
case "record":
|
|
2149
|
+
case "ZodRecord":
|
|
2150
|
+
case "map":
|
|
2151
|
+
case "ZodMap":
|
|
2152
|
+
case "set":
|
|
2153
|
+
case "ZodSet":
|
|
2154
|
+
case "function":
|
|
2155
|
+
case "ZodFunction":
|
|
2156
|
+
case "promise":
|
|
2157
|
+
case "ZodPromise":
|
|
2158
|
+
case "date":
|
|
2159
|
+
case "ZodDate":
|
|
2160
|
+
return "unknown";
|
|
2161
|
+
// Unknown/any/never/void/undefined/null
|
|
2162
|
+
case "unknown":
|
|
2163
|
+
case "ZodUnknown":
|
|
2164
|
+
case "any":
|
|
2165
|
+
case "ZodAny":
|
|
2166
|
+
case "never":
|
|
2167
|
+
case "ZodNever":
|
|
2168
|
+
case "void":
|
|
2169
|
+
case "ZodVoid":
|
|
2170
|
+
case "undefined":
|
|
2171
|
+
case "ZodUndefined":
|
|
2172
|
+
case "null":
|
|
2173
|
+
case "ZodNull":
|
|
2174
|
+
return "unknown";
|
|
2175
|
+
default:
|
|
2176
|
+
return "unknown";
|
|
2177
|
+
}
|
|
1824
2178
|
}
|
|
1825
2179
|
};
|
|
1826
2180
|
}
|
|
@@ -1831,6 +2185,7 @@ function parseBlockParams(content, options) {
|
|
|
1831
2185
|
const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1832
2186
|
const result = {};
|
|
1833
2187
|
const seenPointers = /* @__PURE__ */ new Set();
|
|
2188
|
+
const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
|
|
1834
2189
|
const parts = content.split(argPrefix);
|
|
1835
2190
|
for (let i = 1; i < parts.length; i++) {
|
|
1836
2191
|
const part = parts[i];
|
|
@@ -1842,7 +2197,7 @@ function parseBlockParams(content, options) {
|
|
|
1842
2197
|
throw new Error(`Duplicate pointer: ${pointer2}`);
|
|
1843
2198
|
}
|
|
1844
2199
|
seenPointers.add(pointer2);
|
|
1845
|
-
setByPointer(result, pointer2, "");
|
|
2200
|
+
setByPointer(result, pointer2, "", introspector);
|
|
1846
2201
|
}
|
|
1847
2202
|
continue;
|
|
1848
2203
|
}
|
|
@@ -1858,15 +2213,30 @@ function parseBlockParams(content, options) {
|
|
|
1858
2213
|
throw new Error(`Duplicate pointer: ${pointer}`);
|
|
1859
2214
|
}
|
|
1860
2215
|
seenPointers.add(pointer);
|
|
1861
|
-
setByPointer(result, pointer, value);
|
|
2216
|
+
setByPointer(result, pointer, value, introspector);
|
|
1862
2217
|
}
|
|
1863
2218
|
return result;
|
|
1864
2219
|
}
|
|
1865
|
-
function coerceValue(value) {
|
|
2220
|
+
function coerceValue(value, expectedType) {
|
|
1866
2221
|
if (value.includes("\n")) {
|
|
1867
2222
|
return value;
|
|
1868
2223
|
}
|
|
1869
2224
|
const trimmed = value.trim();
|
|
2225
|
+
if (expectedType === "string") {
|
|
2226
|
+
return value;
|
|
2227
|
+
}
|
|
2228
|
+
if (expectedType === "boolean") {
|
|
2229
|
+
if (trimmed === "true") return true;
|
|
2230
|
+
if (trimmed === "false") return false;
|
|
2231
|
+
return value;
|
|
2232
|
+
}
|
|
2233
|
+
if (expectedType === "number") {
|
|
2234
|
+
const num = Number(trimmed);
|
|
2235
|
+
if (!isNaN(num) && isFinite(num) && trimmed !== "") {
|
|
2236
|
+
return num;
|
|
2237
|
+
}
|
|
2238
|
+
return value;
|
|
2239
|
+
}
|
|
1870
2240
|
if (trimmed === "true") return true;
|
|
1871
2241
|
if (trimmed === "false") return false;
|
|
1872
2242
|
if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
@@ -1877,7 +2247,7 @@ function coerceValue(value) {
|
|
|
1877
2247
|
}
|
|
1878
2248
|
return value;
|
|
1879
2249
|
}
|
|
1880
|
-
function setByPointer(obj, pointer, value) {
|
|
2250
|
+
function setByPointer(obj, pointer, value, introspector) {
|
|
1881
2251
|
const segments = pointer.split("/");
|
|
1882
2252
|
let current = obj;
|
|
1883
2253
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
@@ -1905,7 +2275,8 @@ function setByPointer(obj, pointer, value) {
|
|
|
1905
2275
|
}
|
|
1906
2276
|
}
|
|
1907
2277
|
const lastSegment = segments[segments.length - 1];
|
|
1908
|
-
const
|
|
2278
|
+
const expectedType = introspector?.getTypeAtPath(pointer);
|
|
2279
|
+
const coercedValue = coerceValue(value, expectedType);
|
|
1909
2280
|
if (Array.isArray(current)) {
|
|
1910
2281
|
const index = parseInt(lastSegment, 10);
|
|
1911
2282
|
if (isNaN(index) || index < 0) {
|
|
@@ -1923,6 +2294,122 @@ var init_block_params = __esm({
|
|
|
1923
2294
|
"src/gadgets/block-params.ts"() {
|
|
1924
2295
|
"use strict";
|
|
1925
2296
|
init_constants();
|
|
2297
|
+
init_schema_introspector();
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2300
|
+
|
|
2301
|
+
// src/gadgets/error-formatter.ts
|
|
2302
|
+
var GadgetErrorFormatter;
|
|
2303
|
+
var init_error_formatter = __esm({
|
|
2304
|
+
"src/gadgets/error-formatter.ts"() {
|
|
2305
|
+
"use strict";
|
|
2306
|
+
init_constants();
|
|
2307
|
+
GadgetErrorFormatter = class {
|
|
2308
|
+
argPrefix;
|
|
2309
|
+
startPrefix;
|
|
2310
|
+
endPrefix;
|
|
2311
|
+
constructor(options = {}) {
|
|
2312
|
+
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2313
|
+
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
2314
|
+
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
2315
|
+
}
|
|
2316
|
+
/**
|
|
2317
|
+
* Format a Zod validation error with full gadget instructions.
|
|
2318
|
+
*
|
|
2319
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2320
|
+
* @param zodError - The Zod validation error
|
|
2321
|
+
* @param gadget - The gadget instance (for generating instructions)
|
|
2322
|
+
* @returns Formatted error message with usage instructions
|
|
2323
|
+
*/
|
|
2324
|
+
formatValidationError(gadgetName, zodError, gadget) {
|
|
2325
|
+
const parts = [];
|
|
2326
|
+
parts.push(`Error: Invalid parameters for '${gadgetName}':`);
|
|
2327
|
+
for (const issue of zodError.issues) {
|
|
2328
|
+
const path = issue.path.join(".") || "root";
|
|
2329
|
+
parts.push(` - ${path}: ${issue.message}`);
|
|
2330
|
+
}
|
|
2331
|
+
parts.push("");
|
|
2332
|
+
parts.push("Gadget Usage:");
|
|
2333
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2334
|
+
return parts.join("\n");
|
|
2335
|
+
}
|
|
2336
|
+
/**
|
|
2337
|
+
* Format a parse error with block format reference.
|
|
2338
|
+
*
|
|
2339
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2340
|
+
* @param parseError - The parse error message
|
|
2341
|
+
* @param gadget - The gadget instance if found (for generating instructions)
|
|
2342
|
+
* @returns Formatted error message with format reference
|
|
2343
|
+
*/
|
|
2344
|
+
formatParseError(gadgetName, parseError, gadget) {
|
|
2345
|
+
const parts = [];
|
|
2346
|
+
parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
|
|
2347
|
+
parts.push(` ${parseError}`);
|
|
2348
|
+
if (gadget) {
|
|
2349
|
+
parts.push("");
|
|
2350
|
+
parts.push("Gadget Usage:");
|
|
2351
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2352
|
+
}
|
|
2353
|
+
parts.push("");
|
|
2354
|
+
parts.push("Block Format Reference:");
|
|
2355
|
+
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
2356
|
+
parts.push(` ${this.argPrefix}parameterName`);
|
|
2357
|
+
parts.push(" parameter value here");
|
|
2358
|
+
parts.push(` ${this.endPrefix}`);
|
|
2359
|
+
return parts.join("\n");
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Format a registry error (gadget not found) with available gadgets list.
|
|
2363
|
+
*
|
|
2364
|
+
* @param gadgetName - Name of the gadget that was not found
|
|
2365
|
+
* @param availableGadgets - List of available gadget names
|
|
2366
|
+
* @returns Formatted error message with available gadgets
|
|
2367
|
+
*/
|
|
2368
|
+
formatRegistryError(gadgetName, availableGadgets) {
|
|
2369
|
+
const parts = [];
|
|
2370
|
+
parts.push(`Error: Gadget '${gadgetName}' not found.`);
|
|
2371
|
+
if (availableGadgets.length > 0) {
|
|
2372
|
+
parts.push("");
|
|
2373
|
+
parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
|
|
2374
|
+
} else {
|
|
2375
|
+
parts.push("");
|
|
2376
|
+
parts.push("No gadgets are currently registered.");
|
|
2377
|
+
}
|
|
2378
|
+
return parts.join("\n");
|
|
2379
|
+
}
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
});
|
|
2383
|
+
|
|
2384
|
+
// src/gadgets/exceptions.ts
|
|
2385
|
+
var BreakLoopException, HumanInputException, TimeoutException;
|
|
2386
|
+
var init_exceptions = __esm({
|
|
2387
|
+
"src/gadgets/exceptions.ts"() {
|
|
2388
|
+
"use strict";
|
|
2389
|
+
BreakLoopException = class extends Error {
|
|
2390
|
+
constructor(message) {
|
|
2391
|
+
super(message ?? "Agent loop terminated by gadget");
|
|
2392
|
+
this.name = "BreakLoopException";
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
HumanInputException = class extends Error {
|
|
2396
|
+
question;
|
|
2397
|
+
constructor(question) {
|
|
2398
|
+
super(`Human input required: ${question}`);
|
|
2399
|
+
this.name = "HumanInputException";
|
|
2400
|
+
this.question = question;
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
TimeoutException = class extends Error {
|
|
2404
|
+
timeoutMs;
|
|
2405
|
+
gadgetName;
|
|
2406
|
+
constructor(gadgetName, timeoutMs) {
|
|
2407
|
+
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
2408
|
+
this.name = "TimeoutException";
|
|
2409
|
+
this.gadgetName = gadgetName;
|
|
2410
|
+
this.timeoutMs = timeoutMs;
|
|
2411
|
+
}
|
|
2412
|
+
};
|
|
1926
2413
|
}
|
|
1927
2414
|
});
|
|
1928
2415
|
|
|
@@ -2052,52 +2539,329 @@ var init_parser = __esm({
|
|
|
2052
2539
|
parseError
|
|
2053
2540
|
}
|
|
2054
2541
|
};
|
|
2055
|
-
startIndex = partEndIndex + endMarkerLength;
|
|
2056
|
-
this.lastReportedTextLength = startIndex;
|
|
2057
|
-
}
|
|
2058
|
-
if (startIndex > 0) {
|
|
2059
|
-
this.buffer = this.buffer.substring(startIndex);
|
|
2060
|
-
this.lastReportedTextLength = 0;
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
// Finalize parsing and return remaining text or incomplete gadgets
|
|
2064
|
-
*finalize() {
|
|
2065
|
-
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
|
|
2066
|
-
if (startIndex !== -1) {
|
|
2067
|
-
const textBefore = this.takeTextUntil(startIndex);
|
|
2068
|
-
if (textBefore !== void 0) {
|
|
2069
|
-
yield { type: "text", content: textBefore };
|
|
2542
|
+
startIndex = partEndIndex + endMarkerLength;
|
|
2543
|
+
this.lastReportedTextLength = startIndex;
|
|
2544
|
+
}
|
|
2545
|
+
if (startIndex > 0) {
|
|
2546
|
+
this.buffer = this.buffer.substring(startIndex);
|
|
2547
|
+
this.lastReportedTextLength = 0;
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
// Finalize parsing and return remaining text or incomplete gadgets
|
|
2551
|
+
*finalize() {
|
|
2552
|
+
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
|
|
2553
|
+
if (startIndex !== -1) {
|
|
2554
|
+
const textBefore = this.takeTextUntil(startIndex);
|
|
2555
|
+
if (textBefore !== void 0) {
|
|
2556
|
+
yield { type: "text", content: textBefore };
|
|
2557
|
+
}
|
|
2558
|
+
const metadataStartIndex = startIndex + this.startPrefix.length;
|
|
2559
|
+
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
2560
|
+
if (metadataEndIndex !== -1) {
|
|
2561
|
+
const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
2562
|
+
const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
|
|
2563
|
+
const contentStartIndex = metadataEndIndex + 1;
|
|
2564
|
+
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
2565
|
+
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
2566
|
+
yield {
|
|
2567
|
+
type: "gadget_call",
|
|
2568
|
+
call: {
|
|
2569
|
+
gadgetName: actualGadgetName,
|
|
2570
|
+
invocationId,
|
|
2571
|
+
parametersRaw,
|
|
2572
|
+
parameters,
|
|
2573
|
+
parseError
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
const remainingText = this.takeTextUntil(this.buffer.length);
|
|
2580
|
+
if (remainingText !== void 0) {
|
|
2581
|
+
yield { type: "text", content: remainingText };
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
2585
|
+
reset() {
|
|
2586
|
+
this.buffer = "";
|
|
2587
|
+
this.lastReportedTextLength = 0;
|
|
2588
|
+
}
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
});
|
|
2592
|
+
|
|
2593
|
+
// src/gadgets/executor.ts
|
|
2594
|
+
var GadgetExecutor;
|
|
2595
|
+
var init_executor = __esm({
|
|
2596
|
+
"src/gadgets/executor.ts"() {
|
|
2597
|
+
"use strict";
|
|
2598
|
+
init_constants();
|
|
2599
|
+
init_logger();
|
|
2600
|
+
init_block_params();
|
|
2601
|
+
init_error_formatter();
|
|
2602
|
+
init_exceptions();
|
|
2603
|
+
init_parser();
|
|
2604
|
+
GadgetExecutor = class {
|
|
2605
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
|
|
2606
|
+
this.registry = registry;
|
|
2607
|
+
this.onHumanInputRequired = onHumanInputRequired;
|
|
2608
|
+
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
2609
|
+
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
2610
|
+
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
2611
|
+
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2612
|
+
}
|
|
2613
|
+
logger;
|
|
2614
|
+
errorFormatter;
|
|
2615
|
+
argPrefix;
|
|
2616
|
+
/**
|
|
2617
|
+
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
2618
|
+
*/
|
|
2619
|
+
createTimeoutPromise(gadgetName, timeoutMs) {
|
|
2620
|
+
return new Promise((_, reject) => {
|
|
2621
|
+
setTimeout(() => {
|
|
2622
|
+
reject(new TimeoutException(gadgetName, timeoutMs));
|
|
2623
|
+
}, timeoutMs);
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
// Execute a gadget call asynchronously
|
|
2627
|
+
async execute(call) {
|
|
2628
|
+
const startTime = Date.now();
|
|
2629
|
+
this.logger.debug("Executing gadget", {
|
|
2630
|
+
gadgetName: call.gadgetName,
|
|
2631
|
+
invocationId: call.invocationId,
|
|
2632
|
+
parameters: call.parameters
|
|
2633
|
+
});
|
|
2634
|
+
const rawParameters = call.parameters ?? {};
|
|
2635
|
+
let validatedParameters = rawParameters;
|
|
2636
|
+
try {
|
|
2637
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
2638
|
+
if (!gadget) {
|
|
2639
|
+
this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
|
|
2640
|
+
const availableGadgets = this.registry.getNames();
|
|
2641
|
+
return {
|
|
2642
|
+
gadgetName: call.gadgetName,
|
|
2643
|
+
invocationId: call.invocationId,
|
|
2644
|
+
parameters: call.parameters ?? {},
|
|
2645
|
+
error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
|
|
2646
|
+
executionTimeMs: Date.now() - startTime
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
if (call.parseError || !call.parameters) {
|
|
2650
|
+
this.logger.error("Gadget parameter parse error", {
|
|
2651
|
+
gadgetName: call.gadgetName,
|
|
2652
|
+
parseError: call.parseError,
|
|
2653
|
+
rawParameters: call.parametersRaw
|
|
2654
|
+
});
|
|
2655
|
+
const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
|
|
2656
|
+
return {
|
|
2657
|
+
gadgetName: call.gadgetName,
|
|
2658
|
+
invocationId: call.invocationId,
|
|
2659
|
+
parameters: {},
|
|
2660
|
+
error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
|
|
2661
|
+
executionTimeMs: Date.now() - startTime
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
let schemaAwareParameters = rawParameters;
|
|
2665
|
+
const hasBlockFormat = call.parametersRaw?.includes(this.argPrefix);
|
|
2666
|
+
if (gadget.parameterSchema && hasBlockFormat) {
|
|
2667
|
+
try {
|
|
2668
|
+
const cleanedRaw = stripMarkdownFences(call.parametersRaw);
|
|
2669
|
+
const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
|
|
2670
|
+
const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
|
|
2671
|
+
if (parametersWereModified) {
|
|
2672
|
+
this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
|
|
2673
|
+
gadgetName: call.gadgetName
|
|
2674
|
+
});
|
|
2675
|
+
schemaAwareParameters = rawParameters;
|
|
2676
|
+
} else {
|
|
2677
|
+
schemaAwareParameters = parseBlockParams(cleanedRaw, {
|
|
2678
|
+
argPrefix: this.argPrefix,
|
|
2679
|
+
schema: gadget.parameterSchema
|
|
2680
|
+
});
|
|
2681
|
+
this.logger.debug("Re-parsed parameters with schema", {
|
|
2682
|
+
gadgetName: call.gadgetName,
|
|
2683
|
+
original: rawParameters,
|
|
2684
|
+
schemaAware: schemaAwareParameters
|
|
2685
|
+
});
|
|
2686
|
+
}
|
|
2687
|
+
} catch (error) {
|
|
2688
|
+
this.logger.warn("Schema-aware re-parsing failed, using original parameters", {
|
|
2689
|
+
gadgetName: call.gadgetName,
|
|
2690
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2691
|
+
});
|
|
2692
|
+
schemaAwareParameters = rawParameters;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (gadget.parameterSchema) {
|
|
2696
|
+
const validationResult = gadget.parameterSchema.safeParse(schemaAwareParameters);
|
|
2697
|
+
if (!validationResult.success) {
|
|
2698
|
+
const validationError = this.errorFormatter.formatValidationError(
|
|
2699
|
+
call.gadgetName,
|
|
2700
|
+
validationResult.error,
|
|
2701
|
+
gadget
|
|
2702
|
+
);
|
|
2703
|
+
this.logger.error("Gadget parameter validation failed", {
|
|
2704
|
+
gadgetName: call.gadgetName,
|
|
2705
|
+
issueCount: validationResult.error.issues.length
|
|
2706
|
+
});
|
|
2707
|
+
return {
|
|
2708
|
+
gadgetName: call.gadgetName,
|
|
2709
|
+
invocationId: call.invocationId,
|
|
2710
|
+
parameters: schemaAwareParameters,
|
|
2711
|
+
error: validationError,
|
|
2712
|
+
executionTimeMs: Date.now() - startTime
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
validatedParameters = validationResult.data;
|
|
2716
|
+
} else {
|
|
2717
|
+
validatedParameters = schemaAwareParameters;
|
|
2718
|
+
}
|
|
2719
|
+
const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
|
|
2720
|
+
let result;
|
|
2721
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
2722
|
+
this.logger.debug("Executing gadget with timeout", {
|
|
2723
|
+
gadgetName: call.gadgetName,
|
|
2724
|
+
timeoutMs
|
|
2725
|
+
});
|
|
2726
|
+
result = await Promise.race([
|
|
2727
|
+
Promise.resolve(gadget.execute(validatedParameters)),
|
|
2728
|
+
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
2729
|
+
]);
|
|
2730
|
+
} else {
|
|
2731
|
+
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
2732
|
+
}
|
|
2733
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2734
|
+
this.logger.info("Gadget executed successfully", {
|
|
2735
|
+
gadgetName: call.gadgetName,
|
|
2736
|
+
invocationId: call.invocationId,
|
|
2737
|
+
executionTimeMs
|
|
2738
|
+
});
|
|
2739
|
+
this.logger.debug("Gadget result", {
|
|
2740
|
+
gadgetName: call.gadgetName,
|
|
2741
|
+
invocationId: call.invocationId,
|
|
2742
|
+
parameters: validatedParameters,
|
|
2743
|
+
result,
|
|
2744
|
+
executionTimeMs
|
|
2745
|
+
});
|
|
2746
|
+
return {
|
|
2747
|
+
gadgetName: call.gadgetName,
|
|
2748
|
+
invocationId: call.invocationId,
|
|
2749
|
+
parameters: validatedParameters,
|
|
2750
|
+
result,
|
|
2751
|
+
executionTimeMs
|
|
2752
|
+
};
|
|
2753
|
+
} catch (error) {
|
|
2754
|
+
if (error instanceof BreakLoopException) {
|
|
2755
|
+
this.logger.info("Gadget requested loop termination", {
|
|
2756
|
+
gadgetName: call.gadgetName,
|
|
2757
|
+
message: error.message
|
|
2758
|
+
});
|
|
2759
|
+
return {
|
|
2760
|
+
gadgetName: call.gadgetName,
|
|
2761
|
+
invocationId: call.invocationId,
|
|
2762
|
+
parameters: validatedParameters,
|
|
2763
|
+
result: error.message,
|
|
2764
|
+
breaksLoop: true,
|
|
2765
|
+
executionTimeMs: Date.now() - startTime
|
|
2766
|
+
};
|
|
2070
2767
|
}
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2768
|
+
if (error instanceof TimeoutException) {
|
|
2769
|
+
this.logger.error("Gadget execution timed out", {
|
|
2770
|
+
gadgetName: call.gadgetName,
|
|
2771
|
+
timeoutMs: error.timeoutMs,
|
|
2772
|
+
executionTimeMs: Date.now() - startTime
|
|
2773
|
+
});
|
|
2774
|
+
return {
|
|
2775
|
+
gadgetName: call.gadgetName,
|
|
2776
|
+
invocationId: call.invocationId,
|
|
2777
|
+
parameters: validatedParameters,
|
|
2778
|
+
error: error.message,
|
|
2779
|
+
executionTimeMs: Date.now() - startTime
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
if (error instanceof HumanInputException) {
|
|
2783
|
+
this.logger.info("Gadget requested human input", {
|
|
2784
|
+
gadgetName: call.gadgetName,
|
|
2785
|
+
question: error.question
|
|
2786
|
+
});
|
|
2787
|
+
if (this.onHumanInputRequired) {
|
|
2788
|
+
try {
|
|
2789
|
+
const answer = await this.onHumanInputRequired(error.question);
|
|
2790
|
+
this.logger.debug("Human input received", {
|
|
2791
|
+
gadgetName: call.gadgetName,
|
|
2792
|
+
answerLength: answer.length
|
|
2793
|
+
});
|
|
2794
|
+
return {
|
|
2795
|
+
gadgetName: call.gadgetName,
|
|
2796
|
+
invocationId: call.invocationId,
|
|
2797
|
+
parameters: validatedParameters,
|
|
2798
|
+
result: answer,
|
|
2799
|
+
executionTimeMs: Date.now() - startTime
|
|
2800
|
+
};
|
|
2801
|
+
} catch (inputError) {
|
|
2802
|
+
this.logger.error("Human input callback error", {
|
|
2803
|
+
gadgetName: call.gadgetName,
|
|
2804
|
+
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
2805
|
+
});
|
|
2806
|
+
return {
|
|
2807
|
+
gadgetName: call.gadgetName,
|
|
2808
|
+
invocationId: call.invocationId,
|
|
2809
|
+
parameters: validatedParameters,
|
|
2810
|
+
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
2811
|
+
executionTimeMs: Date.now() - startTime
|
|
2812
|
+
};
|
|
2087
2813
|
}
|
|
2814
|
+
}
|
|
2815
|
+
this.logger.warn("Human input required but no callback provided", {
|
|
2816
|
+
gadgetName: call.gadgetName
|
|
2817
|
+
});
|
|
2818
|
+
return {
|
|
2819
|
+
gadgetName: call.gadgetName,
|
|
2820
|
+
invocationId: call.invocationId,
|
|
2821
|
+
parameters: validatedParameters,
|
|
2822
|
+
error: "Human input required but not available (stdin is not interactive)",
|
|
2823
|
+
executionTimeMs: Date.now() - startTime
|
|
2088
2824
|
};
|
|
2089
|
-
return;
|
|
2090
2825
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2826
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2827
|
+
this.logger.error("Gadget execution failed", {
|
|
2828
|
+
gadgetName: call.gadgetName,
|
|
2829
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2830
|
+
executionTimeMs
|
|
2831
|
+
});
|
|
2832
|
+
return {
|
|
2833
|
+
gadgetName: call.gadgetName,
|
|
2834
|
+
invocationId: call.invocationId,
|
|
2835
|
+
parameters: validatedParameters,
|
|
2836
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2837
|
+
executionTimeMs
|
|
2838
|
+
};
|
|
2095
2839
|
}
|
|
2096
2840
|
}
|
|
2097
|
-
//
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2841
|
+
// Execute multiple gadget calls in parallel
|
|
2842
|
+
async executeAll(calls) {
|
|
2843
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* Deep equality check for objects/arrays.
|
|
2847
|
+
* Used to detect if parameters were modified by an interceptor.
|
|
2848
|
+
*/
|
|
2849
|
+
deepEquals(a, b) {
|
|
2850
|
+
if (a === b) return true;
|
|
2851
|
+
if (a === null || b === null) return a === b;
|
|
2852
|
+
if (typeof a !== typeof b) return false;
|
|
2853
|
+
if (typeof a !== "object") return a === b;
|
|
2854
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
2855
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
2856
|
+
if (a.length !== b.length) return false;
|
|
2857
|
+
return a.every((val, i) => this.deepEquals(val, b[i]));
|
|
2858
|
+
}
|
|
2859
|
+
const aObj = a;
|
|
2860
|
+
const bObj = b;
|
|
2861
|
+
const aKeys = Object.keys(aObj);
|
|
2862
|
+
const bKeys = Object.keys(bObj);
|
|
2863
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
2864
|
+
return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
|
|
2101
2865
|
}
|
|
2102
2866
|
};
|
|
2103
2867
|
}
|
|
@@ -2140,7 +2904,8 @@ var init_stream_processor = __esm({
|
|
|
2140
2904
|
options.registry,
|
|
2141
2905
|
options.onHumanInputRequired,
|
|
2142
2906
|
this.logger.getSubLogger({ name: "executor" }),
|
|
2143
|
-
options.defaultGadgetTimeoutMs
|
|
2907
|
+
options.defaultGadgetTimeoutMs,
|
|
2908
|
+
{ argPrefix: options.gadgetArgPrefix }
|
|
2144
2909
|
);
|
|
2145
2910
|
}
|
|
2146
2911
|
/**
|
|
@@ -2509,6 +3274,7 @@ var init_agent = __esm({
|
|
|
2509
3274
|
init_model_shortcuts();
|
|
2510
3275
|
init_output_viewer();
|
|
2511
3276
|
init_logger();
|
|
3277
|
+
init_manager();
|
|
2512
3278
|
init_gadget_output_store();
|
|
2513
3279
|
init_agent_internal_key();
|
|
2514
3280
|
init_conversation_manager();
|
|
@@ -2539,6 +3305,8 @@ var init_agent = __esm({
|
|
|
2539
3305
|
outputStore;
|
|
2540
3306
|
outputLimitEnabled;
|
|
2541
3307
|
outputLimitCharLimit;
|
|
3308
|
+
// Context compaction
|
|
3309
|
+
compactionManager;
|
|
2542
3310
|
/**
|
|
2543
3311
|
* Creates a new Agent instance.
|
|
2544
3312
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -2598,6 +3366,14 @@ var init_agent = __esm({
|
|
|
2598
3366
|
if (options.userPrompt) {
|
|
2599
3367
|
this.conversation.addUserMessage(options.userPrompt);
|
|
2600
3368
|
}
|
|
3369
|
+
const compactionEnabled = options.compactionConfig?.enabled ?? true;
|
|
3370
|
+
if (compactionEnabled) {
|
|
3371
|
+
this.compactionManager = new CompactionManager(
|
|
3372
|
+
this.client,
|
|
3373
|
+
this.model,
|
|
3374
|
+
options.compactionConfig
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
2601
3377
|
}
|
|
2602
3378
|
/**
|
|
2603
3379
|
* Get the gadget registry for this agent.
|
|
@@ -2620,6 +3396,53 @@ var init_agent = __esm({
|
|
|
2620
3396
|
getRegistry() {
|
|
2621
3397
|
return this.registry;
|
|
2622
3398
|
}
|
|
3399
|
+
/**
|
|
3400
|
+
* Manually trigger context compaction.
|
|
3401
|
+
*
|
|
3402
|
+
* Forces compaction regardless of threshold. Useful for:
|
|
3403
|
+
* - Pre-emptive context management before expected long operations
|
|
3404
|
+
* - Testing compaction behavior
|
|
3405
|
+
*
|
|
3406
|
+
* @returns CompactionEvent if compaction was performed, null if not configured or no history
|
|
3407
|
+
*
|
|
3408
|
+
* @example
|
|
3409
|
+
* ```typescript
|
|
3410
|
+
* const agent = await LLMist.createAgent()
|
|
3411
|
+
* .withModel('sonnet')
|
|
3412
|
+
* .withCompaction()
|
|
3413
|
+
* .ask('...');
|
|
3414
|
+
*
|
|
3415
|
+
* // Manually compact before a long operation
|
|
3416
|
+
* const event = await agent.compact();
|
|
3417
|
+
* if (event) {
|
|
3418
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
3419
|
+
* }
|
|
3420
|
+
* ```
|
|
3421
|
+
*/
|
|
3422
|
+
async compact() {
|
|
3423
|
+
if (!this.compactionManager) {
|
|
3424
|
+
return null;
|
|
3425
|
+
}
|
|
3426
|
+
return this.compactionManager.compact(this.conversation, -1);
|
|
3427
|
+
}
|
|
3428
|
+
/**
|
|
3429
|
+
* Get compaction statistics.
|
|
3430
|
+
*
|
|
3431
|
+
* @returns CompactionStats if compaction is enabled, null otherwise
|
|
3432
|
+
*
|
|
3433
|
+
* @example
|
|
3434
|
+
* ```typescript
|
|
3435
|
+
* const stats = agent.getCompactionStats();
|
|
3436
|
+
* if (stats) {
|
|
3437
|
+
* console.log(`Total compactions: ${stats.totalCompactions}`);
|
|
3438
|
+
* console.log(`Tokens saved: ${stats.totalTokensSaved}`);
|
|
3439
|
+
* console.log(`Current usage: ${stats.currentUsage.percent.toFixed(1)}%`);
|
|
3440
|
+
* }
|
|
3441
|
+
* ```
|
|
3442
|
+
*/
|
|
3443
|
+
getCompactionStats() {
|
|
3444
|
+
return this.compactionManager?.getStats() ?? null;
|
|
3445
|
+
}
|
|
2623
3446
|
/**
|
|
2624
3447
|
* Run the agent loop.
|
|
2625
3448
|
* Clean, simple orchestration - all complexity is in StreamProcessor.
|
|
@@ -2640,6 +3463,30 @@ var init_agent = __esm({
|
|
|
2640
3463
|
while (currentIteration < this.maxIterations) {
|
|
2641
3464
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
2642
3465
|
try {
|
|
3466
|
+
if (this.compactionManager) {
|
|
3467
|
+
const compactionEvent = await this.compactionManager.checkAndCompact(
|
|
3468
|
+
this.conversation,
|
|
3469
|
+
currentIteration
|
|
3470
|
+
);
|
|
3471
|
+
if (compactionEvent) {
|
|
3472
|
+
this.logger.info("Context compacted", {
|
|
3473
|
+
strategy: compactionEvent.strategy,
|
|
3474
|
+
tokensBefore: compactionEvent.tokensBefore,
|
|
3475
|
+
tokensAfter: compactionEvent.tokensAfter
|
|
3476
|
+
});
|
|
3477
|
+
yield { type: "compaction", event: compactionEvent };
|
|
3478
|
+
await this.safeObserve(async () => {
|
|
3479
|
+
if (this.hooks.observers?.onCompaction) {
|
|
3480
|
+
await this.hooks.observers.onCompaction({
|
|
3481
|
+
iteration: currentIteration,
|
|
3482
|
+
event: compactionEvent,
|
|
3483
|
+
stats: this.compactionManager.getStats(),
|
|
3484
|
+
logger: this.logger
|
|
3485
|
+
});
|
|
3486
|
+
}
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
2643
3490
|
let llmOptions = {
|
|
2644
3491
|
model: this.model,
|
|
2645
3492
|
messages: this.conversation.getMessages(),
|
|
@@ -2659,6 +3506,7 @@ var init_agent = __esm({
|
|
|
2659
3506
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
2660
3507
|
const context = {
|
|
2661
3508
|
iteration: currentIteration,
|
|
3509
|
+
maxIterations: this.maxIterations,
|
|
2662
3510
|
options: llmOptions,
|
|
2663
3511
|
logger: this.logger
|
|
2664
3512
|
};
|
|
@@ -2723,12 +3571,17 @@ var init_agent = __esm({
|
|
|
2723
3571
|
});
|
|
2724
3572
|
let finalMessage = result.finalMessage;
|
|
2725
3573
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
3574
|
+
const gadgetCallCount = result.outputs.filter(
|
|
3575
|
+
(output) => output.type === "gadget_result"
|
|
3576
|
+
).length;
|
|
2726
3577
|
const context = {
|
|
2727
3578
|
iteration: currentIteration,
|
|
3579
|
+
maxIterations: this.maxIterations,
|
|
2728
3580
|
options: llmOptions,
|
|
2729
3581
|
finishReason: result.finishReason,
|
|
2730
3582
|
usage: result.usage,
|
|
2731
3583
|
finalMessage: result.finalMessage,
|
|
3584
|
+
gadgetCallCount,
|
|
2732
3585
|
logger: this.logger
|
|
2733
3586
|
};
|
|
2734
3587
|
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
@@ -4963,6 +5816,7 @@ var init_builder = __esm({
|
|
|
4963
5816
|
defaultGadgetTimeoutMs;
|
|
4964
5817
|
gadgetOutputLimit;
|
|
4965
5818
|
gadgetOutputLimitPercent;
|
|
5819
|
+
compactionConfig;
|
|
4966
5820
|
constructor(client) {
|
|
4967
5821
|
this.client = client;
|
|
4968
5822
|
}
|
|
@@ -5358,6 +6212,57 @@ var init_builder = __esm({
|
|
|
5358
6212
|
this.gadgetOutputLimitPercent = percent;
|
|
5359
6213
|
return this;
|
|
5360
6214
|
}
|
|
6215
|
+
/**
|
|
6216
|
+
* Configure context compaction.
|
|
6217
|
+
*
|
|
6218
|
+
* Context compaction automatically manages conversation history to prevent
|
|
6219
|
+
* context window overflow in long-running agent conversations.
|
|
6220
|
+
*
|
|
6221
|
+
* @param config - Compaction configuration options
|
|
6222
|
+
* @returns This builder for chaining
|
|
6223
|
+
*
|
|
6224
|
+
* @example
|
|
6225
|
+
* ```typescript
|
|
6226
|
+
* // Custom thresholds
|
|
6227
|
+
* .withCompaction({
|
|
6228
|
+
* triggerThresholdPercent: 70,
|
|
6229
|
+
* targetPercent: 40,
|
|
6230
|
+
* preserveRecentTurns: 10,
|
|
6231
|
+
* })
|
|
6232
|
+
*
|
|
6233
|
+
* // Different strategy
|
|
6234
|
+
* .withCompaction({
|
|
6235
|
+
* strategy: 'sliding-window',
|
|
6236
|
+
* })
|
|
6237
|
+
*
|
|
6238
|
+
* // With callback
|
|
6239
|
+
* .withCompaction({
|
|
6240
|
+
* onCompaction: (event) => {
|
|
6241
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
6242
|
+
* }
|
|
6243
|
+
* })
|
|
6244
|
+
* ```
|
|
6245
|
+
*/
|
|
6246
|
+
withCompaction(config) {
|
|
6247
|
+
this.compactionConfig = { ...config, enabled: config.enabled ?? true };
|
|
6248
|
+
return this;
|
|
6249
|
+
}
|
|
6250
|
+
/**
|
|
6251
|
+
* Disable context compaction.
|
|
6252
|
+
*
|
|
6253
|
+
* By default, compaction is enabled. Use this method to explicitly disable it.
|
|
6254
|
+
*
|
|
6255
|
+
* @returns This builder for chaining
|
|
6256
|
+
*
|
|
6257
|
+
* @example
|
|
6258
|
+
* ```typescript
|
|
6259
|
+
* .withoutCompaction() // Disable automatic compaction
|
|
6260
|
+
* ```
|
|
6261
|
+
*/
|
|
6262
|
+
withoutCompaction() {
|
|
6263
|
+
this.compactionConfig = { enabled: false };
|
|
6264
|
+
return this;
|
|
6265
|
+
}
|
|
5361
6266
|
/**
|
|
5362
6267
|
* Add a synthetic gadget call to the conversation history.
|
|
5363
6268
|
*
|
|
@@ -5473,7 +6378,8 @@ ${endPrefix}`
|
|
|
5473
6378
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5474
6379
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5475
6380
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5476
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6381
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6382
|
+
compactionConfig: this.compactionConfig
|
|
5477
6383
|
};
|
|
5478
6384
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5479
6385
|
}
|
|
@@ -5575,7 +6481,8 @@ ${endPrefix}`
|
|
|
5575
6481
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5576
6482
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5577
6483
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5578
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6484
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6485
|
+
compactionConfig: this.compactionConfig
|
|
5579
6486
|
};
|
|
5580
6487
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5581
6488
|
}
|
|
@@ -5590,8 +6497,12 @@ __export(index_exports, {
|
|
|
5590
6497
|
AnthropicMessagesProvider: () => AnthropicMessagesProvider,
|
|
5591
6498
|
BaseGadget: () => BaseGadget,
|
|
5592
6499
|
BreakLoopException: () => BreakLoopException,
|
|
6500
|
+
CompactionManager: () => CompactionManager,
|
|
5593
6501
|
ConversationManager: () => ConversationManager,
|
|
6502
|
+
DEFAULT_COMPACTION_CONFIG: () => DEFAULT_COMPACTION_CONFIG,
|
|
6503
|
+
DEFAULT_HINTS: () => DEFAULT_HINTS,
|
|
5594
6504
|
DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
|
|
6505
|
+
DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
|
|
5595
6506
|
Gadget: () => Gadget,
|
|
5596
6507
|
GadgetExecutor: () => GadgetExecutor,
|
|
5597
6508
|
GadgetOutputStore: () => GadgetOutputStore,
|
|
@@ -5599,6 +6510,7 @@ __export(index_exports, {
|
|
|
5599
6510
|
GeminiGenerativeProvider: () => GeminiGenerativeProvider,
|
|
5600
6511
|
HookPresets: () => HookPresets,
|
|
5601
6512
|
HumanInputException: () => HumanInputException,
|
|
6513
|
+
HybridStrategy: () => HybridStrategy,
|
|
5602
6514
|
LLMMessageBuilder: () => LLMMessageBuilder,
|
|
5603
6515
|
LLMist: () => LLMist,
|
|
5604
6516
|
MODEL_ALIASES: () => MODEL_ALIASES,
|
|
@@ -5608,8 +6520,10 @@ __export(index_exports, {
|
|
|
5608
6520
|
ModelIdentifierParser: () => ModelIdentifierParser,
|
|
5609
6521
|
ModelRegistry: () => ModelRegistry,
|
|
5610
6522
|
OpenAIChatProvider: () => OpenAIChatProvider,
|
|
6523
|
+
SlidingWindowStrategy: () => SlidingWindowStrategy,
|
|
5611
6524
|
StreamParser: () => StreamParser,
|
|
5612
6525
|
StreamProcessor: () => StreamProcessor,
|
|
6526
|
+
SummarizationStrategy: () => SummarizationStrategy,
|
|
5613
6527
|
collectEvents: () => collectEvents,
|
|
5614
6528
|
collectText: () => collectText,
|
|
5615
6529
|
complete: () => complete,
|
|
@@ -5617,6 +6531,7 @@ __export(index_exports, {
|
|
|
5617
6531
|
createGadget: () => createGadget,
|
|
5618
6532
|
createGadgetOutputViewer: () => createGadgetOutputViewer,
|
|
5619
6533
|
createGeminiProviderFromEnv: () => createGeminiProviderFromEnv,
|
|
6534
|
+
createHints: () => createHints,
|
|
5620
6535
|
createLogger: () => createLogger,
|
|
5621
6536
|
createMockAdapter: () => createMockAdapter,
|
|
5622
6537
|
createMockClient: () => createMockClient,
|
|
@@ -5629,7 +6544,10 @@ __export(index_exports, {
|
|
|
5629
6544
|
getModelId: () => getModelId,
|
|
5630
6545
|
getProvider: () => getProvider,
|
|
5631
6546
|
hasProviderPrefix: () => hasProviderPrefix,
|
|
6547
|
+
iterationProgressHint: () => iterationProgressHint,
|
|
5632
6548
|
mockLLM: () => mockLLM,
|
|
6549
|
+
parallelGadgetHint: () => parallelGadgetHint,
|
|
6550
|
+
resolveHintTemplate: () => resolveHintTemplate,
|
|
5633
6551
|
resolveModel: () => resolveModel,
|
|
5634
6552
|
resolvePromptTemplate: () => resolvePromptTemplate,
|
|
5635
6553
|
resolveRulesTemplate: () => resolveRulesTemplate,
|
|
@@ -6144,6 +7062,51 @@ var HookPresets = class _HookPresets {
|
|
|
6144
7062
|
}
|
|
6145
7063
|
};
|
|
6146
7064
|
}
|
|
7065
|
+
/**
|
|
7066
|
+
* Tracks context compaction events.
|
|
7067
|
+
*
|
|
7068
|
+
* **Output:**
|
|
7069
|
+
* - Compaction events with 🗜️ emoji
|
|
7070
|
+
* - Strategy name, tokens before/after, and savings
|
|
7071
|
+
* - Cumulative statistics
|
|
7072
|
+
*
|
|
7073
|
+
* **Use cases:**
|
|
7074
|
+
* - Monitoring long-running conversations
|
|
7075
|
+
* - Understanding when and how compaction occurs
|
|
7076
|
+
* - Debugging context management issues
|
|
7077
|
+
*
|
|
7078
|
+
* **Performance:** Minimal overhead. Simple console output.
|
|
7079
|
+
*
|
|
7080
|
+
* @returns Hook configuration that can be passed to .withHooks()
|
|
7081
|
+
*
|
|
7082
|
+
* @example
|
|
7083
|
+
* ```typescript
|
|
7084
|
+
* await LLMist.createAgent()
|
|
7085
|
+
* .withHooks(HookPresets.compactionTracking())
|
|
7086
|
+
* .ask("Your prompt");
|
|
7087
|
+
* ```
|
|
7088
|
+
*/
|
|
7089
|
+
static compactionTracking() {
|
|
7090
|
+
return {
|
|
7091
|
+
observers: {
|
|
7092
|
+
onCompaction: async (ctx) => {
|
|
7093
|
+
const saved = ctx.event.tokensBefore - ctx.event.tokensAfter;
|
|
7094
|
+
const percent = (saved / ctx.event.tokensBefore * 100).toFixed(1);
|
|
7095
|
+
console.log(
|
|
7096
|
+
`\u{1F5DC}\uFE0F Compaction (${ctx.event.strategy}): ${ctx.event.tokensBefore} \u2192 ${ctx.event.tokensAfter} tokens (saved ${saved}, ${percent}%)`
|
|
7097
|
+
);
|
|
7098
|
+
console.log(
|
|
7099
|
+
` Messages: ${ctx.event.messagesBefore} \u2192 ${ctx.event.messagesAfter}`
|
|
7100
|
+
);
|
|
7101
|
+
if (ctx.stats.totalCompactions > 1) {
|
|
7102
|
+
console.log(
|
|
7103
|
+
` Cumulative: ${ctx.stats.totalCompactions} compactions, ${ctx.stats.totalTokensSaved} tokens saved`
|
|
7104
|
+
);
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
}
|
|
7108
|
+
};
|
|
7109
|
+
}
|
|
6147
7110
|
/**
|
|
6148
7111
|
* Returns empty hook configuration for clean output without any logging.
|
|
6149
7112
|
*
|
|
@@ -6374,6 +7337,113 @@ init_conversation_manager();
|
|
|
6374
7337
|
init_stream_processor();
|
|
6375
7338
|
init_gadget_output_store();
|
|
6376
7339
|
|
|
7340
|
+
// src/agent/compaction/index.ts
|
|
7341
|
+
init_config();
|
|
7342
|
+
init_strategy();
|
|
7343
|
+
init_strategies();
|
|
7344
|
+
init_manager();
|
|
7345
|
+
|
|
7346
|
+
// src/agent/hints.ts
|
|
7347
|
+
init_prompt_config();
|
|
7348
|
+
function iterationProgressHint(options) {
|
|
7349
|
+
const { timing = "always", showUrgency = true, template } = options ?? {};
|
|
7350
|
+
return {
|
|
7351
|
+
controllers: {
|
|
7352
|
+
beforeLLMCall: async (ctx) => {
|
|
7353
|
+
const iteration = ctx.iteration + 1;
|
|
7354
|
+
const maxIterations = ctx.maxIterations;
|
|
7355
|
+
const progress = iteration / maxIterations;
|
|
7356
|
+
if (timing === "late" && progress < 0.5) {
|
|
7357
|
+
return { action: "proceed" };
|
|
7358
|
+
}
|
|
7359
|
+
if (timing === "urgent" && progress < 0.8) {
|
|
7360
|
+
return { action: "proceed" };
|
|
7361
|
+
}
|
|
7362
|
+
const remaining = maxIterations - iteration;
|
|
7363
|
+
const hintContext = {
|
|
7364
|
+
iteration,
|
|
7365
|
+
maxIterations,
|
|
7366
|
+
remaining
|
|
7367
|
+
};
|
|
7368
|
+
let hint = resolveHintTemplate(
|
|
7369
|
+
template,
|
|
7370
|
+
DEFAULT_HINTS.iterationProgressHint,
|
|
7371
|
+
hintContext
|
|
7372
|
+
);
|
|
7373
|
+
if (showUrgency && progress >= 0.8) {
|
|
7374
|
+
hint += " \u26A0\uFE0F Running low on iterations - focus on completing the task.";
|
|
7375
|
+
}
|
|
7376
|
+
const messages = [...ctx.options.messages];
|
|
7377
|
+
let lastUserIndex = -1;
|
|
7378
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7379
|
+
if (messages[i].role === "user") {
|
|
7380
|
+
lastUserIndex = i;
|
|
7381
|
+
break;
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
if (lastUserIndex >= 0) {
|
|
7385
|
+
messages.splice(lastUserIndex + 1, 0, {
|
|
7386
|
+
role: "user",
|
|
7387
|
+
content: `[System Hint] ${hint}`
|
|
7388
|
+
});
|
|
7389
|
+
} else {
|
|
7390
|
+
messages.push({
|
|
7391
|
+
role: "user",
|
|
7392
|
+
content: `[System Hint] ${hint}`
|
|
7393
|
+
});
|
|
7394
|
+
}
|
|
7395
|
+
return {
|
|
7396
|
+
action: "proceed",
|
|
7397
|
+
modifiedOptions: { messages }
|
|
7398
|
+
};
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
};
|
|
7402
|
+
}
|
|
7403
|
+
function parallelGadgetHint(options) {
|
|
7404
|
+
const {
|
|
7405
|
+
minGadgetsForEfficiency = 2,
|
|
7406
|
+
message = DEFAULT_HINTS.parallelGadgetsHint,
|
|
7407
|
+
enabled = true
|
|
7408
|
+
} = options ?? {};
|
|
7409
|
+
return {
|
|
7410
|
+
controllers: {
|
|
7411
|
+
afterLLMCall: async (ctx) => {
|
|
7412
|
+
if (!enabled) {
|
|
7413
|
+
return { action: "continue" };
|
|
7414
|
+
}
|
|
7415
|
+
if (ctx.gadgetCallCount > 0 && ctx.gadgetCallCount < minGadgetsForEfficiency) {
|
|
7416
|
+
return {
|
|
7417
|
+
action: "append_messages",
|
|
7418
|
+
messages: [
|
|
7419
|
+
{
|
|
7420
|
+
role: "user",
|
|
7421
|
+
content: `[System Hint] ${message}`
|
|
7422
|
+
}
|
|
7423
|
+
]
|
|
7424
|
+
};
|
|
7425
|
+
}
|
|
7426
|
+
return { action: "continue" };
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
};
|
|
7430
|
+
}
|
|
7431
|
+
function createHints(config) {
|
|
7432
|
+
const hooksToMerge = [];
|
|
7433
|
+
if (config.iterationProgress) {
|
|
7434
|
+
const options = typeof config.iterationProgress === "boolean" ? {} : config.iterationProgress;
|
|
7435
|
+
hooksToMerge.push(iterationProgressHint(options));
|
|
7436
|
+
}
|
|
7437
|
+
if (config.parallelGadgets) {
|
|
7438
|
+
const options = typeof config.parallelGadgets === "boolean" ? {} : config.parallelGadgets;
|
|
7439
|
+
hooksToMerge.push(parallelGadgetHint(options));
|
|
7440
|
+
}
|
|
7441
|
+
if (config.custom) {
|
|
7442
|
+
hooksToMerge.push(...config.custom);
|
|
7443
|
+
}
|
|
7444
|
+
return HookPresets.merge(...hooksToMerge);
|
|
7445
|
+
}
|
|
7446
|
+
|
|
6377
7447
|
// src/index.ts
|
|
6378
7448
|
init_client();
|
|
6379
7449
|
init_messages();
|
|
@@ -7148,14 +8218,21 @@ function createMockClient(options) {
|
|
|
7148
8218
|
|
|
7149
8219
|
// src/testing/mock-gadget.ts
|
|
7150
8220
|
init_gadget();
|
|
8221
|
+
|
|
8222
|
+
// src/testing/cli-helpers.ts
|
|
8223
|
+
var import_node_stream = require("stream");
|
|
7151
8224
|
// Annotate the CommonJS export names for ESM import in node:
|
|
7152
8225
|
0 && (module.exports = {
|
|
7153
8226
|
AgentBuilder,
|
|
7154
8227
|
AnthropicMessagesProvider,
|
|
7155
8228
|
BaseGadget,
|
|
7156
8229
|
BreakLoopException,
|
|
8230
|
+
CompactionManager,
|
|
7157
8231
|
ConversationManager,
|
|
8232
|
+
DEFAULT_COMPACTION_CONFIG,
|
|
8233
|
+
DEFAULT_HINTS,
|
|
7158
8234
|
DEFAULT_PROMPTS,
|
|
8235
|
+
DEFAULT_SUMMARIZATION_PROMPT,
|
|
7159
8236
|
Gadget,
|
|
7160
8237
|
GadgetExecutor,
|
|
7161
8238
|
GadgetOutputStore,
|
|
@@ -7163,6 +8240,7 @@ init_gadget();
|
|
|
7163
8240
|
GeminiGenerativeProvider,
|
|
7164
8241
|
HookPresets,
|
|
7165
8242
|
HumanInputException,
|
|
8243
|
+
HybridStrategy,
|
|
7166
8244
|
LLMMessageBuilder,
|
|
7167
8245
|
LLMist,
|
|
7168
8246
|
MODEL_ALIASES,
|
|
@@ -7172,8 +8250,10 @@ init_gadget();
|
|
|
7172
8250
|
ModelIdentifierParser,
|
|
7173
8251
|
ModelRegistry,
|
|
7174
8252
|
OpenAIChatProvider,
|
|
8253
|
+
SlidingWindowStrategy,
|
|
7175
8254
|
StreamParser,
|
|
7176
8255
|
StreamProcessor,
|
|
8256
|
+
SummarizationStrategy,
|
|
7177
8257
|
collectEvents,
|
|
7178
8258
|
collectText,
|
|
7179
8259
|
complete,
|
|
@@ -7181,6 +8261,7 @@ init_gadget();
|
|
|
7181
8261
|
createGadget,
|
|
7182
8262
|
createGadgetOutputViewer,
|
|
7183
8263
|
createGeminiProviderFromEnv,
|
|
8264
|
+
createHints,
|
|
7184
8265
|
createLogger,
|
|
7185
8266
|
createMockAdapter,
|
|
7186
8267
|
createMockClient,
|
|
@@ -7193,7 +8274,10 @@ init_gadget();
|
|
|
7193
8274
|
getModelId,
|
|
7194
8275
|
getProvider,
|
|
7195
8276
|
hasProviderPrefix,
|
|
8277
|
+
iterationProgressHint,
|
|
7196
8278
|
mockLLM,
|
|
8279
|
+
parallelGadgetHint,
|
|
8280
|
+
resolveHintTemplate,
|
|
7197
8281
|
resolveModel,
|
|
7198
8282
|
resolvePromptTemplate,
|
|
7199
8283
|
resolveRulesTemplate,
|