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/cli.cjs
CHANGED
|
@@ -1113,6 +1113,417 @@ var init_output_viewer = __esm({
|
|
|
1113
1113
|
}
|
|
1114
1114
|
});
|
|
1115
1115
|
|
|
1116
|
+
// src/agent/compaction/config.ts
|
|
1117
|
+
function resolveCompactionConfig(config = {}) {
|
|
1118
|
+
const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
|
|
1119
|
+
const target = config.targetPercent ?? DEFAULT_COMPACTION_CONFIG.targetPercent;
|
|
1120
|
+
if (target >= trigger) {
|
|
1121
|
+
console.warn(
|
|
1122
|
+
`[llmist/compaction] targetPercent (${target}) should be less than triggerThresholdPercent (${trigger}) to be effective.`
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
const strategy = config.strategy ?? DEFAULT_COMPACTION_CONFIG.strategy;
|
|
1126
|
+
const strategyName = typeof strategy === "object" && "name" in strategy ? strategy.name : strategy;
|
|
1127
|
+
return {
|
|
1128
|
+
enabled: config.enabled ?? DEFAULT_COMPACTION_CONFIG.enabled,
|
|
1129
|
+
strategy: strategyName,
|
|
1130
|
+
triggerThresholdPercent: trigger,
|
|
1131
|
+
targetPercent: target,
|
|
1132
|
+
preserveRecentTurns: config.preserveRecentTurns ?? DEFAULT_COMPACTION_CONFIG.preserveRecentTurns,
|
|
1133
|
+
summarizationModel: config.summarizationModel,
|
|
1134
|
+
summarizationPrompt: config.summarizationPrompt ?? DEFAULT_SUMMARIZATION_PROMPT,
|
|
1135
|
+
onCompaction: config.onCompaction
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
var DEFAULT_COMPACTION_CONFIG, DEFAULT_SUMMARIZATION_PROMPT;
|
|
1139
|
+
var init_config = __esm({
|
|
1140
|
+
"src/agent/compaction/config.ts"() {
|
|
1141
|
+
"use strict";
|
|
1142
|
+
DEFAULT_COMPACTION_CONFIG = {
|
|
1143
|
+
enabled: true,
|
|
1144
|
+
strategy: "hybrid",
|
|
1145
|
+
triggerThresholdPercent: 80,
|
|
1146
|
+
targetPercent: 50,
|
|
1147
|
+
preserveRecentTurns: 5
|
|
1148
|
+
};
|
|
1149
|
+
DEFAULT_SUMMARIZATION_PROMPT = `Summarize this conversation history concisely, preserving:
|
|
1150
|
+
1. Key decisions made and their rationale
|
|
1151
|
+
2. Important facts and data discovered
|
|
1152
|
+
3. Errors encountered and how they were resolved
|
|
1153
|
+
4. Current task context and goals
|
|
1154
|
+
|
|
1155
|
+
Format as a brief narrative paragraph, not bullet points.
|
|
1156
|
+
Previous conversation:`;
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
// src/agent/compaction/strategy.ts
|
|
1161
|
+
function groupIntoTurns(messages) {
|
|
1162
|
+
const turns = [];
|
|
1163
|
+
let currentTurn = [];
|
|
1164
|
+
for (const msg of messages) {
|
|
1165
|
+
if (msg.role === "user" && currentTurn.length > 0) {
|
|
1166
|
+
turns.push({
|
|
1167
|
+
messages: currentTurn,
|
|
1168
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1169
|
+
});
|
|
1170
|
+
currentTurn = [msg];
|
|
1171
|
+
} else {
|
|
1172
|
+
currentTurn.push(msg);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
if (currentTurn.length > 0) {
|
|
1176
|
+
turns.push({
|
|
1177
|
+
messages: currentTurn,
|
|
1178
|
+
tokenEstimate: estimateTurnTokens(currentTurn)
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
return turns;
|
|
1182
|
+
}
|
|
1183
|
+
function estimateTurnTokens(messages) {
|
|
1184
|
+
return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
|
|
1185
|
+
}
|
|
1186
|
+
function flattenTurns(turns) {
|
|
1187
|
+
return turns.flatMap((turn) => turn.messages);
|
|
1188
|
+
}
|
|
1189
|
+
var init_strategy = __esm({
|
|
1190
|
+
"src/agent/compaction/strategy.ts"() {
|
|
1191
|
+
"use strict";
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
|
|
1195
|
+
// src/agent/compaction/strategies/sliding-window.ts
|
|
1196
|
+
var TRUNCATION_MARKER_TEMPLATE, SlidingWindowStrategy;
|
|
1197
|
+
var init_sliding_window = __esm({
|
|
1198
|
+
"src/agent/compaction/strategies/sliding-window.ts"() {
|
|
1199
|
+
"use strict";
|
|
1200
|
+
init_strategy();
|
|
1201
|
+
TRUNCATION_MARKER_TEMPLATE = "[Previous conversation truncated. Removed {count} turn(s) to fit context window.]";
|
|
1202
|
+
SlidingWindowStrategy = class {
|
|
1203
|
+
name = "sliding-window";
|
|
1204
|
+
async compact(messages, config, context) {
|
|
1205
|
+
const turns = groupIntoTurns(messages);
|
|
1206
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1207
|
+
if (turns.length <= preserveCount) {
|
|
1208
|
+
return {
|
|
1209
|
+
messages,
|
|
1210
|
+
strategyName: this.name,
|
|
1211
|
+
metadata: {
|
|
1212
|
+
originalCount: messages.length,
|
|
1213
|
+
compactedCount: messages.length,
|
|
1214
|
+
tokensBefore: context.currentTokens,
|
|
1215
|
+
tokensAfter: context.currentTokens
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1220
|
+
const turnsRemoved = turns.length - preserveCount;
|
|
1221
|
+
const truncationMarker = {
|
|
1222
|
+
role: "user",
|
|
1223
|
+
content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
|
|
1224
|
+
};
|
|
1225
|
+
const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
|
|
1226
|
+
const tokensAfter = Math.ceil(
|
|
1227
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1228
|
+
);
|
|
1229
|
+
return {
|
|
1230
|
+
messages: compactedMessages,
|
|
1231
|
+
strategyName: this.name,
|
|
1232
|
+
metadata: {
|
|
1233
|
+
originalCount: messages.length,
|
|
1234
|
+
compactedCount: compactedMessages.length,
|
|
1235
|
+
tokensBefore: context.currentTokens,
|
|
1236
|
+
tokensAfter
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
}
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
// src/agent/compaction/strategies/summarization.ts
|
|
1245
|
+
var SummarizationStrategy;
|
|
1246
|
+
var init_summarization = __esm({
|
|
1247
|
+
"src/agent/compaction/strategies/summarization.ts"() {
|
|
1248
|
+
"use strict";
|
|
1249
|
+
init_strategy();
|
|
1250
|
+
SummarizationStrategy = class {
|
|
1251
|
+
name = "summarization";
|
|
1252
|
+
async compact(messages, config, context) {
|
|
1253
|
+
const turns = groupIntoTurns(messages);
|
|
1254
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1255
|
+
if (turns.length <= preserveCount) {
|
|
1256
|
+
return {
|
|
1257
|
+
messages,
|
|
1258
|
+
strategyName: this.name,
|
|
1259
|
+
metadata: {
|
|
1260
|
+
originalCount: messages.length,
|
|
1261
|
+
compactedCount: messages.length,
|
|
1262
|
+
tokensBefore: context.currentTokens,
|
|
1263
|
+
tokensAfter: context.currentTokens
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
const turnsToSummarize = turns.slice(0, -preserveCount);
|
|
1268
|
+
const turnsToKeep = turns.slice(-preserveCount);
|
|
1269
|
+
const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
|
|
1270
|
+
const summary = await this.generateSummary(conversationToSummarize, config, context);
|
|
1271
|
+
const summaryMessage = {
|
|
1272
|
+
role: "user",
|
|
1273
|
+
content: `[Previous conversation summary]
|
|
1274
|
+
${summary}
|
|
1275
|
+
[End of summary - conversation continues below]`
|
|
1276
|
+
};
|
|
1277
|
+
const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
|
|
1278
|
+
const tokensAfter = Math.ceil(
|
|
1279
|
+
compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
|
|
1280
|
+
);
|
|
1281
|
+
return {
|
|
1282
|
+
messages: compactedMessages,
|
|
1283
|
+
summary,
|
|
1284
|
+
strategyName: this.name,
|
|
1285
|
+
metadata: {
|
|
1286
|
+
originalCount: messages.length,
|
|
1287
|
+
compactedCount: compactedMessages.length,
|
|
1288
|
+
tokensBefore: context.currentTokens,
|
|
1289
|
+
tokensAfter
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
/**
|
|
1294
|
+
* Formats messages into a readable conversation format for summarization.
|
|
1295
|
+
*/
|
|
1296
|
+
formatTurnsForSummary(messages) {
|
|
1297
|
+
return messages.map((msg) => {
|
|
1298
|
+
const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
|
|
1299
|
+
return `${role}: ${msg.content}`;
|
|
1300
|
+
}).join("\n\n");
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Generates a summary using the configured LLM.
|
|
1304
|
+
*/
|
|
1305
|
+
async generateSummary(conversation, config, context) {
|
|
1306
|
+
const model = config.summarizationModel ?? context.model;
|
|
1307
|
+
const prompt = `${config.summarizationPrompt}
|
|
1308
|
+
|
|
1309
|
+
${conversation}`;
|
|
1310
|
+
const response = await context.client.complete(prompt, {
|
|
1311
|
+
model,
|
|
1312
|
+
temperature: 0.3
|
|
1313
|
+
// Low temperature for factual summarization
|
|
1314
|
+
});
|
|
1315
|
+
return response.trim();
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
// src/agent/compaction/strategies/hybrid.ts
|
|
1322
|
+
var MIN_TURNS_FOR_SUMMARIZATION, HybridStrategy;
|
|
1323
|
+
var init_hybrid = __esm({
|
|
1324
|
+
"src/agent/compaction/strategies/hybrid.ts"() {
|
|
1325
|
+
"use strict";
|
|
1326
|
+
init_strategy();
|
|
1327
|
+
init_sliding_window();
|
|
1328
|
+
init_summarization();
|
|
1329
|
+
MIN_TURNS_FOR_SUMMARIZATION = 3;
|
|
1330
|
+
HybridStrategy = class {
|
|
1331
|
+
name = "hybrid";
|
|
1332
|
+
slidingWindow = new SlidingWindowStrategy();
|
|
1333
|
+
summarization = new SummarizationStrategy();
|
|
1334
|
+
async compact(messages, config, context) {
|
|
1335
|
+
const turns = groupIntoTurns(messages);
|
|
1336
|
+
const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
|
|
1337
|
+
if (turns.length <= preserveCount) {
|
|
1338
|
+
return {
|
|
1339
|
+
messages,
|
|
1340
|
+
strategyName: this.name,
|
|
1341
|
+
metadata: {
|
|
1342
|
+
originalCount: messages.length,
|
|
1343
|
+
compactedCount: messages.length,
|
|
1344
|
+
tokensBefore: context.currentTokens,
|
|
1345
|
+
tokensAfter: context.currentTokens
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
const turnsToSummarize = turns.length - preserveCount;
|
|
1350
|
+
if (turnsToSummarize < MIN_TURNS_FOR_SUMMARIZATION) {
|
|
1351
|
+
return this.slidingWindow.compact(messages, config, context);
|
|
1352
|
+
}
|
|
1353
|
+
return this.summarization.compact(messages, config, context);
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
// src/agent/compaction/strategies/index.ts
|
|
1360
|
+
var init_strategies = __esm({
|
|
1361
|
+
"src/agent/compaction/strategies/index.ts"() {
|
|
1362
|
+
"use strict";
|
|
1363
|
+
init_sliding_window();
|
|
1364
|
+
init_summarization();
|
|
1365
|
+
init_hybrid();
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
// src/agent/compaction/manager.ts
|
|
1370
|
+
function createStrategy(name) {
|
|
1371
|
+
switch (name) {
|
|
1372
|
+
case "sliding-window":
|
|
1373
|
+
return new SlidingWindowStrategy();
|
|
1374
|
+
case "summarization":
|
|
1375
|
+
return new SummarizationStrategy();
|
|
1376
|
+
case "hybrid":
|
|
1377
|
+
return new HybridStrategy();
|
|
1378
|
+
default:
|
|
1379
|
+
throw new Error(`Unknown compaction strategy: ${name}`);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
var CompactionManager;
|
|
1383
|
+
var init_manager = __esm({
|
|
1384
|
+
"src/agent/compaction/manager.ts"() {
|
|
1385
|
+
"use strict";
|
|
1386
|
+
init_config();
|
|
1387
|
+
init_strategies();
|
|
1388
|
+
CompactionManager = class {
|
|
1389
|
+
client;
|
|
1390
|
+
model;
|
|
1391
|
+
config;
|
|
1392
|
+
strategy;
|
|
1393
|
+
modelLimits;
|
|
1394
|
+
// Statistics
|
|
1395
|
+
totalCompactions = 0;
|
|
1396
|
+
totalTokensSaved = 0;
|
|
1397
|
+
lastTokenCount = 0;
|
|
1398
|
+
constructor(client, model, config = {}) {
|
|
1399
|
+
this.client = client;
|
|
1400
|
+
this.model = model;
|
|
1401
|
+
this.config = resolveCompactionConfig(config);
|
|
1402
|
+
if (typeof config.strategy === "object" && "compact" in config.strategy) {
|
|
1403
|
+
this.strategy = config.strategy;
|
|
1404
|
+
} else {
|
|
1405
|
+
this.strategy = createStrategy(this.config.strategy);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Check if compaction is needed and perform it if so.
|
|
1410
|
+
*
|
|
1411
|
+
* @param conversation - The conversation manager to compact
|
|
1412
|
+
* @param iteration - Current agent iteration (for event metadata)
|
|
1413
|
+
* @returns CompactionEvent if compaction was performed, null otherwise
|
|
1414
|
+
*/
|
|
1415
|
+
async checkAndCompact(conversation, iteration) {
|
|
1416
|
+
if (!this.config.enabled) {
|
|
1417
|
+
return null;
|
|
1418
|
+
}
|
|
1419
|
+
if (!this.modelLimits) {
|
|
1420
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1421
|
+
if (!this.modelLimits) {
|
|
1422
|
+
return null;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
if (!this.client.countTokens) {
|
|
1426
|
+
return null;
|
|
1427
|
+
}
|
|
1428
|
+
const messages = conversation.getMessages();
|
|
1429
|
+
const currentTokens = await this.client.countTokens(this.model, messages);
|
|
1430
|
+
this.lastTokenCount = currentTokens;
|
|
1431
|
+
const usagePercent = currentTokens / this.modelLimits.contextWindow * 100;
|
|
1432
|
+
if (usagePercent < this.config.triggerThresholdPercent) {
|
|
1433
|
+
return null;
|
|
1434
|
+
}
|
|
1435
|
+
const historyMessages = conversation.getHistoryMessages();
|
|
1436
|
+
const baseMessages = conversation.getBaseMessages();
|
|
1437
|
+
const historyTokens = await this.client.countTokens(this.model, historyMessages);
|
|
1438
|
+
const baseTokens = await this.client.countTokens(this.model, baseMessages);
|
|
1439
|
+
return this.compact(conversation, iteration, {
|
|
1440
|
+
historyMessages,
|
|
1441
|
+
baseMessages,
|
|
1442
|
+
historyTokens,
|
|
1443
|
+
baseTokens,
|
|
1444
|
+
currentTokens: historyTokens + baseTokens
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Force compaction regardless of threshold.
|
|
1449
|
+
*
|
|
1450
|
+
* @param conversation - The conversation manager to compact
|
|
1451
|
+
* @param iteration - Current agent iteration (for event metadata). Use -1 for manual compaction.
|
|
1452
|
+
* @param precomputed - Optional pre-computed token counts (passed from checkAndCompact for efficiency)
|
|
1453
|
+
* @returns CompactionEvent with compaction details
|
|
1454
|
+
*/
|
|
1455
|
+
async compact(conversation, iteration, precomputed) {
|
|
1456
|
+
if (!this.modelLimits) {
|
|
1457
|
+
this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
|
|
1458
|
+
if (!this.modelLimits) {
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
const historyMessages = precomputed?.historyMessages ?? conversation.getHistoryMessages();
|
|
1463
|
+
const baseMessages = precomputed?.baseMessages ?? conversation.getBaseMessages();
|
|
1464
|
+
const historyTokens = precomputed?.historyTokens ?? await this.client.countTokens(this.model, historyMessages);
|
|
1465
|
+
const baseTokens = precomputed?.baseTokens ?? await this.client.countTokens(this.model, baseMessages);
|
|
1466
|
+
const currentTokens = precomputed?.currentTokens ?? historyTokens + baseTokens;
|
|
1467
|
+
const targetTotalTokens = Math.floor(
|
|
1468
|
+
this.modelLimits.contextWindow * this.config.targetPercent / 100
|
|
1469
|
+
);
|
|
1470
|
+
const targetHistoryTokens = Math.max(0, targetTotalTokens - baseTokens);
|
|
1471
|
+
const result = await this.strategy.compact(historyMessages, this.config, {
|
|
1472
|
+
currentTokens: historyTokens,
|
|
1473
|
+
targetTokens: targetHistoryTokens,
|
|
1474
|
+
modelLimits: this.modelLimits,
|
|
1475
|
+
client: this.client,
|
|
1476
|
+
model: this.config.summarizationModel ?? this.model
|
|
1477
|
+
});
|
|
1478
|
+
conversation.replaceHistory(result.messages);
|
|
1479
|
+
const afterTokens = await this.client.countTokens(this.model, conversation.getMessages());
|
|
1480
|
+
const tokensSaved = currentTokens - afterTokens;
|
|
1481
|
+
this.totalCompactions++;
|
|
1482
|
+
this.totalTokensSaved += tokensSaved;
|
|
1483
|
+
this.lastTokenCount = afterTokens;
|
|
1484
|
+
const event = {
|
|
1485
|
+
strategy: result.strategyName,
|
|
1486
|
+
tokensBefore: currentTokens,
|
|
1487
|
+
tokensAfter: afterTokens,
|
|
1488
|
+
messagesBefore: historyMessages.length + baseMessages.length,
|
|
1489
|
+
messagesAfter: result.messages.length + baseMessages.length,
|
|
1490
|
+
summary: result.summary,
|
|
1491
|
+
iteration
|
|
1492
|
+
};
|
|
1493
|
+
if (this.config.onCompaction) {
|
|
1494
|
+
try {
|
|
1495
|
+
this.config.onCompaction(event);
|
|
1496
|
+
} catch (err) {
|
|
1497
|
+
console.warn("[llmist/compaction] onCompaction callback error:", err);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return event;
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Get compaction statistics.
|
|
1504
|
+
*/
|
|
1505
|
+
getStats() {
|
|
1506
|
+
const contextWindow = this.modelLimits?.contextWindow ?? 0;
|
|
1507
|
+
return {
|
|
1508
|
+
totalCompactions: this.totalCompactions,
|
|
1509
|
+
totalTokensSaved: this.totalTokensSaved,
|
|
1510
|
+
currentUsage: {
|
|
1511
|
+
tokens: this.lastTokenCount,
|
|
1512
|
+
percent: contextWindow > 0 ? this.lastTokenCount / contextWindow * 100 : 0
|
|
1513
|
+
},
|
|
1514
|
+
contextWindow
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Check if compaction is enabled.
|
|
1519
|
+
*/
|
|
1520
|
+
isEnabled() {
|
|
1521
|
+
return this.config.enabled;
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
});
|
|
1526
|
+
|
|
1116
1527
|
// src/agent/gadget-output-store.ts
|
|
1117
1528
|
var import_node_crypto, GadgetOutputStore;
|
|
1118
1529
|
var init_gadget_output_store = __esm({
|
|
@@ -1215,10 +1626,16 @@ var init_conversation_manager = __esm({
|
|
|
1215
1626
|
baseMessages;
|
|
1216
1627
|
initialMessages;
|
|
1217
1628
|
historyBuilder;
|
|
1629
|
+
startPrefix;
|
|
1630
|
+
endPrefix;
|
|
1631
|
+
argPrefix;
|
|
1218
1632
|
constructor(baseMessages, initialMessages, options = {}) {
|
|
1219
1633
|
this.baseMessages = baseMessages;
|
|
1220
1634
|
this.initialMessages = initialMessages;
|
|
1221
1635
|
this.historyBuilder = new LLMMessageBuilder();
|
|
1636
|
+
this.startPrefix = options.startPrefix;
|
|
1637
|
+
this.endPrefix = options.endPrefix;
|
|
1638
|
+
this.argPrefix = options.argPrefix;
|
|
1222
1639
|
if (options.startPrefix && options.endPrefix) {
|
|
1223
1640
|
this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
|
|
1224
1641
|
}
|
|
@@ -1235,6 +1652,25 @@ var init_conversation_manager = __esm({
|
|
|
1235
1652
|
getMessages() {
|
|
1236
1653
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
1237
1654
|
}
|
|
1655
|
+
getHistoryMessages() {
|
|
1656
|
+
return this.historyBuilder.build();
|
|
1657
|
+
}
|
|
1658
|
+
getBaseMessages() {
|
|
1659
|
+
return [...this.baseMessages, ...this.initialMessages];
|
|
1660
|
+
}
|
|
1661
|
+
replaceHistory(newHistory) {
|
|
1662
|
+
this.historyBuilder = new LLMMessageBuilder();
|
|
1663
|
+
if (this.startPrefix && this.endPrefix) {
|
|
1664
|
+
this.historyBuilder.withPrefixes(this.startPrefix, this.endPrefix, this.argPrefix);
|
|
1665
|
+
}
|
|
1666
|
+
for (const msg of newHistory) {
|
|
1667
|
+
if (msg.role === "user") {
|
|
1668
|
+
this.historyBuilder.addUser(msg.content);
|
|
1669
|
+
} else if (msg.role === "assistant") {
|
|
1670
|
+
this.historyBuilder.addAssistant(msg.content);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1238
1674
|
};
|
|
1239
1675
|
}
|
|
1240
1676
|
});
|
|
@@ -1447,334 +1883,241 @@ var init_hook_validators = __esm({
|
|
|
1447
1883
|
}
|
|
1448
1884
|
});
|
|
1449
1885
|
|
|
1450
|
-
// src/gadgets/
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1886
|
+
// src/gadgets/schema-introspector.ts
|
|
1887
|
+
function getDef(schema) {
|
|
1888
|
+
return schema._def;
|
|
1889
|
+
}
|
|
1890
|
+
function getTypeName(schema) {
|
|
1891
|
+
const def = getDef(schema);
|
|
1892
|
+
return def?.type ?? def?.typeName;
|
|
1893
|
+
}
|
|
1894
|
+
function getShape(schema) {
|
|
1895
|
+
const def = getDef(schema);
|
|
1896
|
+
if (typeof def?.shape === "function") {
|
|
1897
|
+
return def.shape();
|
|
1898
|
+
}
|
|
1899
|
+
return def?.shape;
|
|
1900
|
+
}
|
|
1901
|
+
var SchemaIntrospector;
|
|
1902
|
+
var init_schema_introspector = __esm({
|
|
1903
|
+
"src/gadgets/schema-introspector.ts"() {
|
|
1454
1904
|
"use strict";
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
constructor(options = {}) {
|
|
1461
|
-
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1462
|
-
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
1463
|
-
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
1905
|
+
SchemaIntrospector = class {
|
|
1906
|
+
schema;
|
|
1907
|
+
cache = /* @__PURE__ */ new Map();
|
|
1908
|
+
constructor(schema) {
|
|
1909
|
+
this.schema = schema;
|
|
1464
1910
|
}
|
|
1465
1911
|
/**
|
|
1466
|
-
*
|
|
1912
|
+
* Get the expected type at a JSON pointer path.
|
|
1467
1913
|
*
|
|
1468
|
-
* @param
|
|
1469
|
-
* @
|
|
1470
|
-
* @param gadget - The gadget instance (for generating instructions)
|
|
1471
|
-
* @returns Formatted error message with usage instructions
|
|
1914
|
+
* @param pointer - JSON pointer path without leading / (e.g., "config/timeout", "items/0")
|
|
1915
|
+
* @returns Type hint for coercion decision
|
|
1472
1916
|
*/
|
|
1473
|
-
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
const path2 = issue.path.join(".") || "root";
|
|
1478
|
-
parts.push(` - ${path2}: ${issue.message}`);
|
|
1917
|
+
getTypeAtPath(pointer) {
|
|
1918
|
+
const cached = this.cache.get(pointer);
|
|
1919
|
+
if (cached !== void 0) {
|
|
1920
|
+
return cached;
|
|
1479
1921
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return parts.join("\n");
|
|
1922
|
+
const result = this.resolveTypeAtPath(pointer);
|
|
1923
|
+
this.cache.set(pointer, result);
|
|
1924
|
+
return result;
|
|
1484
1925
|
}
|
|
1485
1926
|
/**
|
|
1486
|
-
*
|
|
1487
|
-
*
|
|
1488
|
-
* @param gadgetName - Name of the gadget that was called
|
|
1489
|
-
* @param parseError - The parse error message
|
|
1490
|
-
* @param gadget - The gadget instance if found (for generating instructions)
|
|
1491
|
-
* @returns Formatted error message with format reference
|
|
1927
|
+
* Internal method to resolve type at path without caching.
|
|
1492
1928
|
*/
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1929
|
+
resolveTypeAtPath(pointer) {
|
|
1930
|
+
if (!pointer) {
|
|
1931
|
+
return this.getBaseType(this.schema);
|
|
1932
|
+
}
|
|
1933
|
+
const segments = pointer.split("/");
|
|
1934
|
+
let current = this.schema;
|
|
1935
|
+
for (const segment of segments) {
|
|
1936
|
+
current = this.unwrapSchema(current);
|
|
1937
|
+
const typeName = getTypeName(current);
|
|
1938
|
+
if (typeName === "object" || typeName === "ZodObject") {
|
|
1939
|
+
const shape = getShape(current);
|
|
1940
|
+
if (!shape || !(segment in shape)) {
|
|
1941
|
+
return "unknown";
|
|
1942
|
+
}
|
|
1943
|
+
current = shape[segment];
|
|
1944
|
+
} else if (typeName === "array" || typeName === "ZodArray") {
|
|
1945
|
+
if (!/^\d+$/.test(segment)) {
|
|
1946
|
+
return "unknown";
|
|
1947
|
+
}
|
|
1948
|
+
const def = getDef(current);
|
|
1949
|
+
const elementType = def?.element ?? def?.type;
|
|
1950
|
+
if (!elementType) {
|
|
1951
|
+
return "unknown";
|
|
1952
|
+
}
|
|
1953
|
+
current = elementType;
|
|
1954
|
+
} else if (typeName === "tuple" || typeName === "ZodTuple") {
|
|
1955
|
+
if (!/^\d+$/.test(segment)) {
|
|
1956
|
+
return "unknown";
|
|
1957
|
+
}
|
|
1958
|
+
const index = parseInt(segment, 10);
|
|
1959
|
+
const def = getDef(current);
|
|
1960
|
+
const items = def?.items;
|
|
1961
|
+
if (!items || index >= items.length) {
|
|
1962
|
+
return "unknown";
|
|
1963
|
+
}
|
|
1964
|
+
current = items[index];
|
|
1965
|
+
} else if (typeName === "record" || typeName === "ZodRecord") {
|
|
1966
|
+
const def = getDef(current);
|
|
1967
|
+
const valueType = def?.valueType;
|
|
1968
|
+
if (!valueType) {
|
|
1969
|
+
return "unknown";
|
|
1970
|
+
}
|
|
1971
|
+
current = valueType;
|
|
1972
|
+
} else {
|
|
1973
|
+
return "unknown";
|
|
1974
|
+
}
|
|
1501
1975
|
}
|
|
1502
|
-
|
|
1503
|
-
parts.push("Block Format Reference:");
|
|
1504
|
-
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
1505
|
-
parts.push(` ${this.argPrefix}parameterName`);
|
|
1506
|
-
parts.push(" parameter value here");
|
|
1507
|
-
parts.push(` ${this.endPrefix}`);
|
|
1508
|
-
return parts.join("\n");
|
|
1976
|
+
return this.getBaseType(current);
|
|
1509
1977
|
}
|
|
1510
1978
|
/**
|
|
1511
|
-
*
|
|
1512
|
-
*
|
|
1513
|
-
* @param gadgetName - Name of the gadget that was not found
|
|
1514
|
-
* @param availableGadgets - List of available gadget names
|
|
1515
|
-
* @returns Formatted error message with available gadgets
|
|
1979
|
+
* Unwrap schema modifiers (optional, default, nullable, branded, etc.)
|
|
1980
|
+
* to get to the underlying type.
|
|
1516
1981
|
*/
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1982
|
+
unwrapSchema(schema) {
|
|
1983
|
+
let current = schema;
|
|
1984
|
+
let iterations = 0;
|
|
1985
|
+
const maxIterations = 20;
|
|
1986
|
+
while (iterations < maxIterations) {
|
|
1987
|
+
const typeName = getTypeName(current);
|
|
1988
|
+
const wrapperTypes = [
|
|
1989
|
+
"optional",
|
|
1990
|
+
"nullable",
|
|
1991
|
+
"default",
|
|
1992
|
+
"catch",
|
|
1993
|
+
"branded",
|
|
1994
|
+
"readonly",
|
|
1995
|
+
"pipeline",
|
|
1996
|
+
"ZodOptional",
|
|
1997
|
+
"ZodNullable",
|
|
1998
|
+
"ZodDefault",
|
|
1999
|
+
"ZodCatch",
|
|
2000
|
+
"ZodBranded",
|
|
2001
|
+
"ZodReadonly",
|
|
2002
|
+
"ZodPipeline"
|
|
2003
|
+
];
|
|
2004
|
+
if (typeName && wrapperTypes.includes(typeName)) {
|
|
2005
|
+
const def = getDef(current);
|
|
2006
|
+
const inner = def?.innerType ?? def?.in ?? def?.type;
|
|
2007
|
+
if (!inner || inner === current) break;
|
|
2008
|
+
current = inner;
|
|
2009
|
+
iterations++;
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
break;
|
|
1526
2013
|
}
|
|
1527
|
-
return
|
|
1528
|
-
}
|
|
1529
|
-
};
|
|
1530
|
-
}
|
|
1531
|
-
});
|
|
1532
|
-
|
|
1533
|
-
// src/gadgets/exceptions.ts
|
|
1534
|
-
var BreakLoopException, HumanInputException, TimeoutException;
|
|
1535
|
-
var init_exceptions = __esm({
|
|
1536
|
-
"src/gadgets/exceptions.ts"() {
|
|
1537
|
-
"use strict";
|
|
1538
|
-
BreakLoopException = class extends Error {
|
|
1539
|
-
constructor(message) {
|
|
1540
|
-
super(message ?? "Agent loop terminated by gadget");
|
|
1541
|
-
this.name = "BreakLoopException";
|
|
1542
|
-
}
|
|
1543
|
-
};
|
|
1544
|
-
HumanInputException = class extends Error {
|
|
1545
|
-
question;
|
|
1546
|
-
constructor(question) {
|
|
1547
|
-
super(`Human input required: ${question}`);
|
|
1548
|
-
this.name = "HumanInputException";
|
|
1549
|
-
this.question = question;
|
|
2014
|
+
return current;
|
|
1550
2015
|
}
|
|
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
|
-
|
|
1608
|
-
return
|
|
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
|
-
|
|
1656
|
-
this.logger.debug("Executing gadget with timeout", {
|
|
1657
|
-
gadgetName: call.gadgetName,
|
|
1658
|
-
timeoutMs
|
|
1659
|
-
});
|
|
1660
|
-
result = await Promise.race([
|
|
1661
|
-
Promise.resolve(gadget.execute(validatedParameters)),
|
|
1662
|
-
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
1663
|
-
]);
|
|
1664
|
-
} else {
|
|
1665
|
-
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
1666
|
-
}
|
|
1667
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1668
|
-
this.logger.info("Gadget executed successfully", {
|
|
1669
|
-
gadgetName: call.gadgetName,
|
|
1670
|
-
invocationId: call.invocationId,
|
|
1671
|
-
executionTimeMs
|
|
1672
|
-
});
|
|
1673
|
-
this.logger.debug("Gadget result", {
|
|
1674
|
-
gadgetName: call.gadgetName,
|
|
1675
|
-
invocationId: call.invocationId,
|
|
1676
|
-
parameters: validatedParameters,
|
|
1677
|
-
result,
|
|
1678
|
-
executionTimeMs
|
|
1679
|
-
});
|
|
1680
|
-
return {
|
|
1681
|
-
gadgetName: call.gadgetName,
|
|
1682
|
-
invocationId: call.invocationId,
|
|
1683
|
-
parameters: validatedParameters,
|
|
1684
|
-
result,
|
|
1685
|
-
executionTimeMs
|
|
1686
|
-
};
|
|
1687
|
-
} catch (error) {
|
|
1688
|
-
if (error instanceof BreakLoopException) {
|
|
1689
|
-
this.logger.info("Gadget requested loop termination", {
|
|
1690
|
-
gadgetName: call.gadgetName,
|
|
1691
|
-
message: error.message
|
|
1692
|
-
});
|
|
1693
|
-
return {
|
|
1694
|
-
gadgetName: call.gadgetName,
|
|
1695
|
-
invocationId: call.invocationId,
|
|
1696
|
-
parameters: validatedParameters,
|
|
1697
|
-
result: error.message,
|
|
1698
|
-
breaksLoop: true,
|
|
1699
|
-
executionTimeMs: Date.now() - startTime
|
|
1700
|
-
};
|
|
1701
|
-
}
|
|
1702
|
-
if (error instanceof TimeoutException) {
|
|
1703
|
-
this.logger.error("Gadget execution timed out", {
|
|
1704
|
-
gadgetName: call.gadgetName,
|
|
1705
|
-
timeoutMs: error.timeoutMs,
|
|
1706
|
-
executionTimeMs: Date.now() - startTime
|
|
1707
|
-
});
|
|
1708
|
-
return {
|
|
1709
|
-
gadgetName: call.gadgetName,
|
|
1710
|
-
invocationId: call.invocationId,
|
|
1711
|
-
parameters: validatedParameters,
|
|
1712
|
-
error: error.message,
|
|
1713
|
-
executionTimeMs: Date.now() - startTime
|
|
1714
|
-
};
|
|
1715
|
-
}
|
|
1716
|
-
if (error instanceof HumanInputException) {
|
|
1717
|
-
this.logger.info("Gadget requested human input", {
|
|
1718
|
-
gadgetName: call.gadgetName,
|
|
1719
|
-
question: error.question
|
|
1720
|
-
});
|
|
1721
|
-
if (this.onHumanInputRequired) {
|
|
1722
|
-
try {
|
|
1723
|
-
const answer = await this.onHumanInputRequired(error.question);
|
|
1724
|
-
this.logger.debug("Human input received", {
|
|
1725
|
-
gadgetName: call.gadgetName,
|
|
1726
|
-
answerLength: answer.length
|
|
1727
|
-
});
|
|
1728
|
-
return {
|
|
1729
|
-
gadgetName: call.gadgetName,
|
|
1730
|
-
invocationId: call.invocationId,
|
|
1731
|
-
parameters: validatedParameters,
|
|
1732
|
-
result: answer,
|
|
1733
|
-
executionTimeMs: Date.now() - startTime
|
|
1734
|
-
};
|
|
1735
|
-
} catch (inputError) {
|
|
1736
|
-
this.logger.error("Human input callback error", {
|
|
1737
|
-
gadgetName: call.gadgetName,
|
|
1738
|
-
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
1739
|
-
});
|
|
1740
|
-
return {
|
|
1741
|
-
gadgetName: call.gadgetName,
|
|
1742
|
-
invocationId: call.invocationId,
|
|
1743
|
-
parameters: validatedParameters,
|
|
1744
|
-
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
1745
|
-
executionTimeMs: Date.now() - startTime
|
|
1746
|
-
};
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
this.logger.warn("Human input required but no callback provided", {
|
|
1750
|
-
gadgetName: call.gadgetName
|
|
1751
|
-
});
|
|
1752
|
-
return {
|
|
1753
|
-
gadgetName: call.gadgetName,
|
|
1754
|
-
invocationId: call.invocationId,
|
|
1755
|
-
parameters: validatedParameters,
|
|
1756
|
-
error: "Human input required but not available (stdin is not interactive)",
|
|
1757
|
-
executionTimeMs: Date.now() - startTime
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
const executionTimeMs = Date.now() - startTime;
|
|
1761
|
-
this.logger.error("Gadget execution failed", {
|
|
1762
|
-
gadgetName: call.gadgetName,
|
|
1763
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1764
|
-
executionTimeMs
|
|
1765
|
-
});
|
|
1766
|
-
return {
|
|
1767
|
-
gadgetName: call.gadgetName,
|
|
1768
|
-
invocationId: call.invocationId,
|
|
1769
|
-
parameters: validatedParameters,
|
|
1770
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1771
|
-
executionTimeMs
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
// Execute multiple gadget calls in parallel
|
|
1776
|
-
async executeAll(calls) {
|
|
1777
|
-
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2016
|
+
/**
|
|
2017
|
+
* Get the primitive type hint from an unwrapped schema.
|
|
2018
|
+
*/
|
|
2019
|
+
getBaseType(schema) {
|
|
2020
|
+
const unwrapped = this.unwrapSchema(schema);
|
|
2021
|
+
const typeName = getTypeName(unwrapped);
|
|
2022
|
+
switch (typeName) {
|
|
2023
|
+
// Primitive types
|
|
2024
|
+
case "string":
|
|
2025
|
+
case "ZodString":
|
|
2026
|
+
return "string";
|
|
2027
|
+
case "number":
|
|
2028
|
+
case "ZodNumber":
|
|
2029
|
+
case "bigint":
|
|
2030
|
+
case "ZodBigInt":
|
|
2031
|
+
return "number";
|
|
2032
|
+
case "boolean":
|
|
2033
|
+
case "ZodBoolean":
|
|
2034
|
+
return "boolean";
|
|
2035
|
+
// Literal types - check the literal value type
|
|
2036
|
+
case "literal":
|
|
2037
|
+
case "ZodLiteral": {
|
|
2038
|
+
const def = getDef(unwrapped);
|
|
2039
|
+
const values = def?.values;
|
|
2040
|
+
const value = values?.[0] ?? def?.value;
|
|
2041
|
+
if (typeof value === "string") return "string";
|
|
2042
|
+
if (typeof value === "number" || typeof value === "bigint")
|
|
2043
|
+
return "number";
|
|
2044
|
+
if (typeof value === "boolean") return "boolean";
|
|
2045
|
+
return "unknown";
|
|
2046
|
+
}
|
|
2047
|
+
// Enum - always string keys
|
|
2048
|
+
case "enum":
|
|
2049
|
+
case "ZodEnum":
|
|
2050
|
+
case "nativeEnum":
|
|
2051
|
+
case "ZodNativeEnum":
|
|
2052
|
+
return "string";
|
|
2053
|
+
// Union - return 'unknown' to let auto-coercion decide
|
|
2054
|
+
// Since multiple types are valid, we can't definitively say what the LLM intended
|
|
2055
|
+
// Auto-coercion will handle common cases (numbers, booleans) appropriately
|
|
2056
|
+
case "union":
|
|
2057
|
+
case "ZodUnion":
|
|
2058
|
+
return "unknown";
|
|
2059
|
+
// Discriminated union - complex, return unknown
|
|
2060
|
+
case "discriminatedUnion":
|
|
2061
|
+
case "ZodDiscriminatedUnion":
|
|
2062
|
+
return "unknown";
|
|
2063
|
+
// Intersection - check both sides
|
|
2064
|
+
case "intersection":
|
|
2065
|
+
case "ZodIntersection": {
|
|
2066
|
+
const def = getDef(unwrapped);
|
|
2067
|
+
const left = def?.left;
|
|
2068
|
+
const right = def?.right;
|
|
2069
|
+
if (!left || !right) return "unknown";
|
|
2070
|
+
const leftType = this.getBaseType(left);
|
|
2071
|
+
const rightType = this.getBaseType(right);
|
|
2072
|
+
if (leftType === rightType) return leftType;
|
|
2073
|
+
if (leftType === "string" || rightType === "string") return "string";
|
|
2074
|
+
return "unknown";
|
|
2075
|
+
}
|
|
2076
|
+
// Effects/transforms - return unknown to let Zod handle it
|
|
2077
|
+
case "effects":
|
|
2078
|
+
case "ZodEffects":
|
|
2079
|
+
return "unknown";
|
|
2080
|
+
// Lazy - can't resolve without evaluating
|
|
2081
|
+
case "lazy":
|
|
2082
|
+
case "ZodLazy":
|
|
2083
|
+
return "unknown";
|
|
2084
|
+
// Complex types - return unknown
|
|
2085
|
+
case "object":
|
|
2086
|
+
case "ZodObject":
|
|
2087
|
+
case "array":
|
|
2088
|
+
case "ZodArray":
|
|
2089
|
+
case "tuple":
|
|
2090
|
+
case "ZodTuple":
|
|
2091
|
+
case "record":
|
|
2092
|
+
case "ZodRecord":
|
|
2093
|
+
case "map":
|
|
2094
|
+
case "ZodMap":
|
|
2095
|
+
case "set":
|
|
2096
|
+
case "ZodSet":
|
|
2097
|
+
case "function":
|
|
2098
|
+
case "ZodFunction":
|
|
2099
|
+
case "promise":
|
|
2100
|
+
case "ZodPromise":
|
|
2101
|
+
case "date":
|
|
2102
|
+
case "ZodDate":
|
|
2103
|
+
return "unknown";
|
|
2104
|
+
// Unknown/any/never/void/undefined/null
|
|
2105
|
+
case "unknown":
|
|
2106
|
+
case "ZodUnknown":
|
|
2107
|
+
case "any":
|
|
2108
|
+
case "ZodAny":
|
|
2109
|
+
case "never":
|
|
2110
|
+
case "ZodNever":
|
|
2111
|
+
case "void":
|
|
2112
|
+
case "ZodVoid":
|
|
2113
|
+
case "undefined":
|
|
2114
|
+
case "ZodUndefined":
|
|
2115
|
+
case "null":
|
|
2116
|
+
case "ZodNull":
|
|
2117
|
+
return "unknown";
|
|
2118
|
+
default:
|
|
2119
|
+
return "unknown";
|
|
2120
|
+
}
|
|
1778
2121
|
}
|
|
1779
2122
|
};
|
|
1780
2123
|
}
|
|
@@ -1785,6 +2128,7 @@ function parseBlockParams(content, options) {
|
|
|
1785
2128
|
const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
1786
2129
|
const result = {};
|
|
1787
2130
|
const seenPointers = /* @__PURE__ */ new Set();
|
|
2131
|
+
const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
|
|
1788
2132
|
const parts = content.split(argPrefix);
|
|
1789
2133
|
for (let i = 1; i < parts.length; i++) {
|
|
1790
2134
|
const part = parts[i];
|
|
@@ -1796,7 +2140,7 @@ function parseBlockParams(content, options) {
|
|
|
1796
2140
|
throw new Error(`Duplicate pointer: ${pointer2}`);
|
|
1797
2141
|
}
|
|
1798
2142
|
seenPointers.add(pointer2);
|
|
1799
|
-
setByPointer(result, pointer2, "");
|
|
2143
|
+
setByPointer(result, pointer2, "", introspector);
|
|
1800
2144
|
}
|
|
1801
2145
|
continue;
|
|
1802
2146
|
}
|
|
@@ -1812,15 +2156,30 @@ function parseBlockParams(content, options) {
|
|
|
1812
2156
|
throw new Error(`Duplicate pointer: ${pointer}`);
|
|
1813
2157
|
}
|
|
1814
2158
|
seenPointers.add(pointer);
|
|
1815
|
-
setByPointer(result, pointer, value);
|
|
2159
|
+
setByPointer(result, pointer, value, introspector);
|
|
1816
2160
|
}
|
|
1817
2161
|
return result;
|
|
1818
2162
|
}
|
|
1819
|
-
function coerceValue(value) {
|
|
2163
|
+
function coerceValue(value, expectedType) {
|
|
1820
2164
|
if (value.includes("\n")) {
|
|
1821
2165
|
return value;
|
|
1822
2166
|
}
|
|
1823
2167
|
const trimmed = value.trim();
|
|
2168
|
+
if (expectedType === "string") {
|
|
2169
|
+
return value;
|
|
2170
|
+
}
|
|
2171
|
+
if (expectedType === "boolean") {
|
|
2172
|
+
if (trimmed === "true") return true;
|
|
2173
|
+
if (trimmed === "false") return false;
|
|
2174
|
+
return value;
|
|
2175
|
+
}
|
|
2176
|
+
if (expectedType === "number") {
|
|
2177
|
+
const num = Number(trimmed);
|
|
2178
|
+
if (!isNaN(num) && isFinite(num) && trimmed !== "") {
|
|
2179
|
+
return num;
|
|
2180
|
+
}
|
|
2181
|
+
return value;
|
|
2182
|
+
}
|
|
1824
2183
|
if (trimmed === "true") return true;
|
|
1825
2184
|
if (trimmed === "false") return false;
|
|
1826
2185
|
if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
@@ -1831,7 +2190,7 @@ function coerceValue(value) {
|
|
|
1831
2190
|
}
|
|
1832
2191
|
return value;
|
|
1833
2192
|
}
|
|
1834
|
-
function setByPointer(obj, pointer, value) {
|
|
2193
|
+
function setByPointer(obj, pointer, value, introspector) {
|
|
1835
2194
|
const segments = pointer.split("/");
|
|
1836
2195
|
let current = obj;
|
|
1837
2196
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
@@ -1859,7 +2218,8 @@ function setByPointer(obj, pointer, value) {
|
|
|
1859
2218
|
}
|
|
1860
2219
|
}
|
|
1861
2220
|
const lastSegment = segments[segments.length - 1];
|
|
1862
|
-
const
|
|
2221
|
+
const expectedType = introspector?.getTypeAtPath(pointer);
|
|
2222
|
+
const coercedValue = coerceValue(value, expectedType);
|
|
1863
2223
|
if (Array.isArray(current)) {
|
|
1864
2224
|
const index = parseInt(lastSegment, 10);
|
|
1865
2225
|
if (isNaN(index) || index < 0) {
|
|
@@ -1877,6 +2237,122 @@ var init_block_params = __esm({
|
|
|
1877
2237
|
"src/gadgets/block-params.ts"() {
|
|
1878
2238
|
"use strict";
|
|
1879
2239
|
init_constants();
|
|
2240
|
+
init_schema_introspector();
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
|
|
2244
|
+
// src/gadgets/error-formatter.ts
|
|
2245
|
+
var GadgetErrorFormatter;
|
|
2246
|
+
var init_error_formatter = __esm({
|
|
2247
|
+
"src/gadgets/error-formatter.ts"() {
|
|
2248
|
+
"use strict";
|
|
2249
|
+
init_constants();
|
|
2250
|
+
GadgetErrorFormatter = class {
|
|
2251
|
+
argPrefix;
|
|
2252
|
+
startPrefix;
|
|
2253
|
+
endPrefix;
|
|
2254
|
+
constructor(options = {}) {
|
|
2255
|
+
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2256
|
+
this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
|
|
2257
|
+
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Format a Zod validation error with full gadget instructions.
|
|
2261
|
+
*
|
|
2262
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2263
|
+
* @param zodError - The Zod validation error
|
|
2264
|
+
* @param gadget - The gadget instance (for generating instructions)
|
|
2265
|
+
* @returns Formatted error message with usage instructions
|
|
2266
|
+
*/
|
|
2267
|
+
formatValidationError(gadgetName, zodError, gadget) {
|
|
2268
|
+
const parts = [];
|
|
2269
|
+
parts.push(`Error: Invalid parameters for '${gadgetName}':`);
|
|
2270
|
+
for (const issue of zodError.issues) {
|
|
2271
|
+
const path2 = issue.path.join(".") || "root";
|
|
2272
|
+
parts.push(` - ${path2}: ${issue.message}`);
|
|
2273
|
+
}
|
|
2274
|
+
parts.push("");
|
|
2275
|
+
parts.push("Gadget Usage:");
|
|
2276
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2277
|
+
return parts.join("\n");
|
|
2278
|
+
}
|
|
2279
|
+
/**
|
|
2280
|
+
* Format a parse error with block format reference.
|
|
2281
|
+
*
|
|
2282
|
+
* @param gadgetName - Name of the gadget that was called
|
|
2283
|
+
* @param parseError - The parse error message
|
|
2284
|
+
* @param gadget - The gadget instance if found (for generating instructions)
|
|
2285
|
+
* @returns Formatted error message with format reference
|
|
2286
|
+
*/
|
|
2287
|
+
formatParseError(gadgetName, parseError, gadget) {
|
|
2288
|
+
const parts = [];
|
|
2289
|
+
parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
|
|
2290
|
+
parts.push(` ${parseError}`);
|
|
2291
|
+
if (gadget) {
|
|
2292
|
+
parts.push("");
|
|
2293
|
+
parts.push("Gadget Usage:");
|
|
2294
|
+
parts.push(gadget.getInstruction(this.argPrefix));
|
|
2295
|
+
}
|
|
2296
|
+
parts.push("");
|
|
2297
|
+
parts.push("Block Format Reference:");
|
|
2298
|
+
parts.push(` ${this.startPrefix}${gadgetName}`);
|
|
2299
|
+
parts.push(` ${this.argPrefix}parameterName`);
|
|
2300
|
+
parts.push(" parameter value here");
|
|
2301
|
+
parts.push(` ${this.endPrefix}`);
|
|
2302
|
+
return parts.join("\n");
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Format a registry error (gadget not found) with available gadgets list.
|
|
2306
|
+
*
|
|
2307
|
+
* @param gadgetName - Name of the gadget that was not found
|
|
2308
|
+
* @param availableGadgets - List of available gadget names
|
|
2309
|
+
* @returns Formatted error message with available gadgets
|
|
2310
|
+
*/
|
|
2311
|
+
formatRegistryError(gadgetName, availableGadgets) {
|
|
2312
|
+
const parts = [];
|
|
2313
|
+
parts.push(`Error: Gadget '${gadgetName}' not found.`);
|
|
2314
|
+
if (availableGadgets.length > 0) {
|
|
2315
|
+
parts.push("");
|
|
2316
|
+
parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
|
|
2317
|
+
} else {
|
|
2318
|
+
parts.push("");
|
|
2319
|
+
parts.push("No gadgets are currently registered.");
|
|
2320
|
+
}
|
|
2321
|
+
return parts.join("\n");
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
});
|
|
2326
|
+
|
|
2327
|
+
// src/gadgets/exceptions.ts
|
|
2328
|
+
var BreakLoopException, HumanInputException, TimeoutException;
|
|
2329
|
+
var init_exceptions = __esm({
|
|
2330
|
+
"src/gadgets/exceptions.ts"() {
|
|
2331
|
+
"use strict";
|
|
2332
|
+
BreakLoopException = class extends Error {
|
|
2333
|
+
constructor(message) {
|
|
2334
|
+
super(message ?? "Agent loop terminated by gadget");
|
|
2335
|
+
this.name = "BreakLoopException";
|
|
2336
|
+
}
|
|
2337
|
+
};
|
|
2338
|
+
HumanInputException = class extends Error {
|
|
2339
|
+
question;
|
|
2340
|
+
constructor(question) {
|
|
2341
|
+
super(`Human input required: ${question}`);
|
|
2342
|
+
this.name = "HumanInputException";
|
|
2343
|
+
this.question = question;
|
|
2344
|
+
}
|
|
2345
|
+
};
|
|
2346
|
+
TimeoutException = class extends Error {
|
|
2347
|
+
timeoutMs;
|
|
2348
|
+
gadgetName;
|
|
2349
|
+
constructor(gadgetName, timeoutMs) {
|
|
2350
|
+
super(`Gadget '${gadgetName}' execution exceeded timeout of ${timeoutMs}ms`);
|
|
2351
|
+
this.name = "TimeoutException";
|
|
2352
|
+
this.gadgetName = gadgetName;
|
|
2353
|
+
this.timeoutMs = timeoutMs;
|
|
2354
|
+
}
|
|
2355
|
+
};
|
|
1880
2356
|
}
|
|
1881
2357
|
});
|
|
1882
2358
|
|
|
@@ -1994,64 +2470,341 @@ var init_parser = __esm({
|
|
|
1994
2470
|
break;
|
|
1995
2471
|
}
|
|
1996
2472
|
}
|
|
1997
|
-
const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
|
|
1998
|
-
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
1999
|
-
yield {
|
|
2000
|
-
type: "gadget_call",
|
|
2001
|
-
call: {
|
|
2002
|
-
gadgetName: actualGadgetName,
|
|
2003
|
-
invocationId,
|
|
2004
|
-
parametersRaw,
|
|
2005
|
-
parameters,
|
|
2006
|
-
parseError
|
|
2007
|
-
}
|
|
2473
|
+
const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
|
|
2474
|
+
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
2475
|
+
yield {
|
|
2476
|
+
type: "gadget_call",
|
|
2477
|
+
call: {
|
|
2478
|
+
gadgetName: actualGadgetName,
|
|
2479
|
+
invocationId,
|
|
2480
|
+
parametersRaw,
|
|
2481
|
+
parameters,
|
|
2482
|
+
parseError
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2485
|
+
startIndex = partEndIndex + endMarkerLength;
|
|
2486
|
+
this.lastReportedTextLength = startIndex;
|
|
2487
|
+
}
|
|
2488
|
+
if (startIndex > 0) {
|
|
2489
|
+
this.buffer = this.buffer.substring(startIndex);
|
|
2490
|
+
this.lastReportedTextLength = 0;
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
// Finalize parsing and return remaining text or incomplete gadgets
|
|
2494
|
+
*finalize() {
|
|
2495
|
+
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
|
|
2496
|
+
if (startIndex !== -1) {
|
|
2497
|
+
const textBefore = this.takeTextUntil(startIndex);
|
|
2498
|
+
if (textBefore !== void 0) {
|
|
2499
|
+
yield { type: "text", content: textBefore };
|
|
2500
|
+
}
|
|
2501
|
+
const metadataStartIndex = startIndex + this.startPrefix.length;
|
|
2502
|
+
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
2503
|
+
if (metadataEndIndex !== -1) {
|
|
2504
|
+
const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
2505
|
+
const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
|
|
2506
|
+
const contentStartIndex = metadataEndIndex + 1;
|
|
2507
|
+
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
2508
|
+
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
2509
|
+
yield {
|
|
2510
|
+
type: "gadget_call",
|
|
2511
|
+
call: {
|
|
2512
|
+
gadgetName: actualGadgetName,
|
|
2513
|
+
invocationId,
|
|
2514
|
+
parametersRaw,
|
|
2515
|
+
parameters,
|
|
2516
|
+
parseError
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
const remainingText = this.takeTextUntil(this.buffer.length);
|
|
2523
|
+
if (remainingText !== void 0) {
|
|
2524
|
+
yield { type: "text", content: remainingText };
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
2528
|
+
reset() {
|
|
2529
|
+
this.buffer = "";
|
|
2530
|
+
this.lastReportedTextLength = 0;
|
|
2531
|
+
}
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
});
|
|
2535
|
+
|
|
2536
|
+
// src/gadgets/executor.ts
|
|
2537
|
+
var GadgetExecutor;
|
|
2538
|
+
var init_executor = __esm({
|
|
2539
|
+
"src/gadgets/executor.ts"() {
|
|
2540
|
+
"use strict";
|
|
2541
|
+
init_constants();
|
|
2542
|
+
init_logger();
|
|
2543
|
+
init_block_params();
|
|
2544
|
+
init_error_formatter();
|
|
2545
|
+
init_exceptions();
|
|
2546
|
+
init_parser();
|
|
2547
|
+
GadgetExecutor = class {
|
|
2548
|
+
constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
|
|
2549
|
+
this.registry = registry;
|
|
2550
|
+
this.onHumanInputRequired = onHumanInputRequired;
|
|
2551
|
+
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
2552
|
+
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
2553
|
+
this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
|
|
2554
|
+
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
2555
|
+
}
|
|
2556
|
+
logger;
|
|
2557
|
+
errorFormatter;
|
|
2558
|
+
argPrefix;
|
|
2559
|
+
/**
|
|
2560
|
+
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
2561
|
+
*/
|
|
2562
|
+
createTimeoutPromise(gadgetName, timeoutMs) {
|
|
2563
|
+
return new Promise((_, reject) => {
|
|
2564
|
+
setTimeout(() => {
|
|
2565
|
+
reject(new TimeoutException(gadgetName, timeoutMs));
|
|
2566
|
+
}, timeoutMs);
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
// Execute a gadget call asynchronously
|
|
2570
|
+
async execute(call) {
|
|
2571
|
+
const startTime = Date.now();
|
|
2572
|
+
this.logger.debug("Executing gadget", {
|
|
2573
|
+
gadgetName: call.gadgetName,
|
|
2574
|
+
invocationId: call.invocationId,
|
|
2575
|
+
parameters: call.parameters
|
|
2576
|
+
});
|
|
2577
|
+
const rawParameters = call.parameters ?? {};
|
|
2578
|
+
let validatedParameters = rawParameters;
|
|
2579
|
+
try {
|
|
2580
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
2581
|
+
if (!gadget) {
|
|
2582
|
+
this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
|
|
2583
|
+
const availableGadgets = this.registry.getNames();
|
|
2584
|
+
return {
|
|
2585
|
+
gadgetName: call.gadgetName,
|
|
2586
|
+
invocationId: call.invocationId,
|
|
2587
|
+
parameters: call.parameters ?? {},
|
|
2588
|
+
error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
|
|
2589
|
+
executionTimeMs: Date.now() - startTime
|
|
2590
|
+
};
|
|
2591
|
+
}
|
|
2592
|
+
if (call.parseError || !call.parameters) {
|
|
2593
|
+
this.logger.error("Gadget parameter parse error", {
|
|
2594
|
+
gadgetName: call.gadgetName,
|
|
2595
|
+
parseError: call.parseError,
|
|
2596
|
+
rawParameters: call.parametersRaw
|
|
2597
|
+
});
|
|
2598
|
+
const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
|
|
2599
|
+
return {
|
|
2600
|
+
gadgetName: call.gadgetName,
|
|
2601
|
+
invocationId: call.invocationId,
|
|
2602
|
+
parameters: {},
|
|
2603
|
+
error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
|
|
2604
|
+
executionTimeMs: Date.now() - startTime
|
|
2605
|
+
};
|
|
2606
|
+
}
|
|
2607
|
+
let schemaAwareParameters = rawParameters;
|
|
2608
|
+
const hasBlockFormat = call.parametersRaw?.includes(this.argPrefix);
|
|
2609
|
+
if (gadget.parameterSchema && hasBlockFormat) {
|
|
2610
|
+
try {
|
|
2611
|
+
const cleanedRaw = stripMarkdownFences(call.parametersRaw);
|
|
2612
|
+
const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
|
|
2613
|
+
const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
|
|
2614
|
+
if (parametersWereModified) {
|
|
2615
|
+
this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
|
|
2616
|
+
gadgetName: call.gadgetName
|
|
2617
|
+
});
|
|
2618
|
+
schemaAwareParameters = rawParameters;
|
|
2619
|
+
} else {
|
|
2620
|
+
schemaAwareParameters = parseBlockParams(cleanedRaw, {
|
|
2621
|
+
argPrefix: this.argPrefix,
|
|
2622
|
+
schema: gadget.parameterSchema
|
|
2623
|
+
});
|
|
2624
|
+
this.logger.debug("Re-parsed parameters with schema", {
|
|
2625
|
+
gadgetName: call.gadgetName,
|
|
2626
|
+
original: rawParameters,
|
|
2627
|
+
schemaAware: schemaAwareParameters
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
} catch (error) {
|
|
2631
|
+
this.logger.warn("Schema-aware re-parsing failed, using original parameters", {
|
|
2632
|
+
gadgetName: call.gadgetName,
|
|
2633
|
+
error: error instanceof Error ? error.message : String(error)
|
|
2634
|
+
});
|
|
2635
|
+
schemaAwareParameters = rawParameters;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
if (gadget.parameterSchema) {
|
|
2639
|
+
const validationResult = gadget.parameterSchema.safeParse(schemaAwareParameters);
|
|
2640
|
+
if (!validationResult.success) {
|
|
2641
|
+
const validationError = this.errorFormatter.formatValidationError(
|
|
2642
|
+
call.gadgetName,
|
|
2643
|
+
validationResult.error,
|
|
2644
|
+
gadget
|
|
2645
|
+
);
|
|
2646
|
+
this.logger.error("Gadget parameter validation failed", {
|
|
2647
|
+
gadgetName: call.gadgetName,
|
|
2648
|
+
issueCount: validationResult.error.issues.length
|
|
2649
|
+
});
|
|
2650
|
+
return {
|
|
2651
|
+
gadgetName: call.gadgetName,
|
|
2652
|
+
invocationId: call.invocationId,
|
|
2653
|
+
parameters: schemaAwareParameters,
|
|
2654
|
+
error: validationError,
|
|
2655
|
+
executionTimeMs: Date.now() - startTime
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
validatedParameters = validationResult.data;
|
|
2659
|
+
} else {
|
|
2660
|
+
validatedParameters = schemaAwareParameters;
|
|
2661
|
+
}
|
|
2662
|
+
const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
|
|
2663
|
+
let result;
|
|
2664
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
2665
|
+
this.logger.debug("Executing gadget with timeout", {
|
|
2666
|
+
gadgetName: call.gadgetName,
|
|
2667
|
+
timeoutMs
|
|
2668
|
+
});
|
|
2669
|
+
result = await Promise.race([
|
|
2670
|
+
Promise.resolve(gadget.execute(validatedParameters)),
|
|
2671
|
+
this.createTimeoutPromise(call.gadgetName, timeoutMs)
|
|
2672
|
+
]);
|
|
2673
|
+
} else {
|
|
2674
|
+
result = await Promise.resolve(gadget.execute(validatedParameters));
|
|
2675
|
+
}
|
|
2676
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2677
|
+
this.logger.info("Gadget executed successfully", {
|
|
2678
|
+
gadgetName: call.gadgetName,
|
|
2679
|
+
invocationId: call.invocationId,
|
|
2680
|
+
executionTimeMs
|
|
2681
|
+
});
|
|
2682
|
+
this.logger.debug("Gadget result", {
|
|
2683
|
+
gadgetName: call.gadgetName,
|
|
2684
|
+
invocationId: call.invocationId,
|
|
2685
|
+
parameters: validatedParameters,
|
|
2686
|
+
result,
|
|
2687
|
+
executionTimeMs
|
|
2688
|
+
});
|
|
2689
|
+
return {
|
|
2690
|
+
gadgetName: call.gadgetName,
|
|
2691
|
+
invocationId: call.invocationId,
|
|
2692
|
+
parameters: validatedParameters,
|
|
2693
|
+
result,
|
|
2694
|
+
executionTimeMs
|
|
2008
2695
|
};
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
yield { type: "text", content: textBefore };
|
|
2696
|
+
} catch (error) {
|
|
2697
|
+
if (error instanceof BreakLoopException) {
|
|
2698
|
+
this.logger.info("Gadget requested loop termination", {
|
|
2699
|
+
gadgetName: call.gadgetName,
|
|
2700
|
+
message: error.message
|
|
2701
|
+
});
|
|
2702
|
+
return {
|
|
2703
|
+
gadgetName: call.gadgetName,
|
|
2704
|
+
invocationId: call.invocationId,
|
|
2705
|
+
parameters: validatedParameters,
|
|
2706
|
+
result: error.message,
|
|
2707
|
+
breaksLoop: true,
|
|
2708
|
+
executionTimeMs: Date.now() - startTime
|
|
2709
|
+
};
|
|
2024
2710
|
}
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2711
|
+
if (error instanceof TimeoutException) {
|
|
2712
|
+
this.logger.error("Gadget execution timed out", {
|
|
2713
|
+
gadgetName: call.gadgetName,
|
|
2714
|
+
timeoutMs: error.timeoutMs,
|
|
2715
|
+
executionTimeMs: Date.now() - startTime
|
|
2716
|
+
});
|
|
2717
|
+
return {
|
|
2718
|
+
gadgetName: call.gadgetName,
|
|
2719
|
+
invocationId: call.invocationId,
|
|
2720
|
+
parameters: validatedParameters,
|
|
2721
|
+
error: error.message,
|
|
2722
|
+
executionTimeMs: Date.now() - startTime
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2725
|
+
if (error instanceof HumanInputException) {
|
|
2726
|
+
this.logger.info("Gadget requested human input", {
|
|
2727
|
+
gadgetName: call.gadgetName,
|
|
2728
|
+
question: error.question
|
|
2729
|
+
});
|
|
2730
|
+
if (this.onHumanInputRequired) {
|
|
2731
|
+
try {
|
|
2732
|
+
const answer = await this.onHumanInputRequired(error.question);
|
|
2733
|
+
this.logger.debug("Human input received", {
|
|
2734
|
+
gadgetName: call.gadgetName,
|
|
2735
|
+
answerLength: answer.length
|
|
2736
|
+
});
|
|
2737
|
+
return {
|
|
2738
|
+
gadgetName: call.gadgetName,
|
|
2739
|
+
invocationId: call.invocationId,
|
|
2740
|
+
parameters: validatedParameters,
|
|
2741
|
+
result: answer,
|
|
2742
|
+
executionTimeMs: Date.now() - startTime
|
|
2743
|
+
};
|
|
2744
|
+
} catch (inputError) {
|
|
2745
|
+
this.logger.error("Human input callback error", {
|
|
2746
|
+
gadgetName: call.gadgetName,
|
|
2747
|
+
error: inputError instanceof Error ? inputError.message : String(inputError)
|
|
2748
|
+
});
|
|
2749
|
+
return {
|
|
2750
|
+
gadgetName: call.gadgetName,
|
|
2751
|
+
invocationId: call.invocationId,
|
|
2752
|
+
parameters: validatedParameters,
|
|
2753
|
+
error: inputError instanceof Error ? inputError.message : String(inputError),
|
|
2754
|
+
executionTimeMs: Date.now() - startTime
|
|
2755
|
+
};
|
|
2041
2756
|
}
|
|
2757
|
+
}
|
|
2758
|
+
this.logger.warn("Human input required but no callback provided", {
|
|
2759
|
+
gadgetName: call.gadgetName
|
|
2760
|
+
});
|
|
2761
|
+
return {
|
|
2762
|
+
gadgetName: call.gadgetName,
|
|
2763
|
+
invocationId: call.invocationId,
|
|
2764
|
+
parameters: validatedParameters,
|
|
2765
|
+
error: "Human input required but not available (stdin is not interactive)",
|
|
2766
|
+
executionTimeMs: Date.now() - startTime
|
|
2042
2767
|
};
|
|
2043
|
-
return;
|
|
2044
2768
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2769
|
+
const executionTimeMs = Date.now() - startTime;
|
|
2770
|
+
this.logger.error("Gadget execution failed", {
|
|
2771
|
+
gadgetName: call.gadgetName,
|
|
2772
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2773
|
+
executionTimeMs
|
|
2774
|
+
});
|
|
2775
|
+
return {
|
|
2776
|
+
gadgetName: call.gadgetName,
|
|
2777
|
+
invocationId: call.invocationId,
|
|
2778
|
+
parameters: validatedParameters,
|
|
2779
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2780
|
+
executionTimeMs
|
|
2781
|
+
};
|
|
2049
2782
|
}
|
|
2050
2783
|
}
|
|
2051
|
-
//
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2784
|
+
// Execute multiple gadget calls in parallel
|
|
2785
|
+
async executeAll(calls) {
|
|
2786
|
+
return Promise.all(calls.map((call) => this.execute(call)));
|
|
2787
|
+
}
|
|
2788
|
+
/**
|
|
2789
|
+
* Deep equality check for objects/arrays.
|
|
2790
|
+
* Used to detect if parameters were modified by an interceptor.
|
|
2791
|
+
*/
|
|
2792
|
+
deepEquals(a, b) {
|
|
2793
|
+
if (a === b) return true;
|
|
2794
|
+
if (a === null || b === null) return a === b;
|
|
2795
|
+
if (typeof a !== typeof b) return false;
|
|
2796
|
+
if (typeof a !== "object") return a === b;
|
|
2797
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
2798
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
2799
|
+
if (a.length !== b.length) return false;
|
|
2800
|
+
return a.every((val, i) => this.deepEquals(val, b[i]));
|
|
2801
|
+
}
|
|
2802
|
+
const aObj = a;
|
|
2803
|
+
const bObj = b;
|
|
2804
|
+
const aKeys = Object.keys(aObj);
|
|
2805
|
+
const bKeys = Object.keys(bObj);
|
|
2806
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
2807
|
+
return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
|
|
2055
2808
|
}
|
|
2056
2809
|
};
|
|
2057
2810
|
}
|
|
@@ -2094,7 +2847,8 @@ var init_stream_processor = __esm({
|
|
|
2094
2847
|
options.registry,
|
|
2095
2848
|
options.onHumanInputRequired,
|
|
2096
2849
|
this.logger.getSubLogger({ name: "executor" }),
|
|
2097
|
-
options.defaultGadgetTimeoutMs
|
|
2850
|
+
options.defaultGadgetTimeoutMs,
|
|
2851
|
+
{ argPrefix: options.gadgetArgPrefix }
|
|
2098
2852
|
);
|
|
2099
2853
|
}
|
|
2100
2854
|
/**
|
|
@@ -2463,6 +3217,7 @@ var init_agent = __esm({
|
|
|
2463
3217
|
init_model_shortcuts();
|
|
2464
3218
|
init_output_viewer();
|
|
2465
3219
|
init_logger();
|
|
3220
|
+
init_manager();
|
|
2466
3221
|
init_gadget_output_store();
|
|
2467
3222
|
init_agent_internal_key();
|
|
2468
3223
|
init_conversation_manager();
|
|
@@ -2493,6 +3248,8 @@ var init_agent = __esm({
|
|
|
2493
3248
|
outputStore;
|
|
2494
3249
|
outputLimitEnabled;
|
|
2495
3250
|
outputLimitCharLimit;
|
|
3251
|
+
// Context compaction
|
|
3252
|
+
compactionManager;
|
|
2496
3253
|
/**
|
|
2497
3254
|
* Creates a new Agent instance.
|
|
2498
3255
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -2552,6 +3309,14 @@ var init_agent = __esm({
|
|
|
2552
3309
|
if (options.userPrompt) {
|
|
2553
3310
|
this.conversation.addUserMessage(options.userPrompt);
|
|
2554
3311
|
}
|
|
3312
|
+
const compactionEnabled = options.compactionConfig?.enabled ?? true;
|
|
3313
|
+
if (compactionEnabled) {
|
|
3314
|
+
this.compactionManager = new CompactionManager(
|
|
3315
|
+
this.client,
|
|
3316
|
+
this.model,
|
|
3317
|
+
options.compactionConfig
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
2555
3320
|
}
|
|
2556
3321
|
/**
|
|
2557
3322
|
* Get the gadget registry for this agent.
|
|
@@ -2574,6 +3339,53 @@ var init_agent = __esm({
|
|
|
2574
3339
|
getRegistry() {
|
|
2575
3340
|
return this.registry;
|
|
2576
3341
|
}
|
|
3342
|
+
/**
|
|
3343
|
+
* Manually trigger context compaction.
|
|
3344
|
+
*
|
|
3345
|
+
* Forces compaction regardless of threshold. Useful for:
|
|
3346
|
+
* - Pre-emptive context management before expected long operations
|
|
3347
|
+
* - Testing compaction behavior
|
|
3348
|
+
*
|
|
3349
|
+
* @returns CompactionEvent if compaction was performed, null if not configured or no history
|
|
3350
|
+
*
|
|
3351
|
+
* @example
|
|
3352
|
+
* ```typescript
|
|
3353
|
+
* const agent = await LLMist.createAgent()
|
|
3354
|
+
* .withModel('sonnet')
|
|
3355
|
+
* .withCompaction()
|
|
3356
|
+
* .ask('...');
|
|
3357
|
+
*
|
|
3358
|
+
* // Manually compact before a long operation
|
|
3359
|
+
* const event = await agent.compact();
|
|
3360
|
+
* if (event) {
|
|
3361
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
3362
|
+
* }
|
|
3363
|
+
* ```
|
|
3364
|
+
*/
|
|
3365
|
+
async compact() {
|
|
3366
|
+
if (!this.compactionManager) {
|
|
3367
|
+
return null;
|
|
3368
|
+
}
|
|
3369
|
+
return this.compactionManager.compact(this.conversation, -1);
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Get compaction statistics.
|
|
3373
|
+
*
|
|
3374
|
+
* @returns CompactionStats if compaction is enabled, null otherwise
|
|
3375
|
+
*
|
|
3376
|
+
* @example
|
|
3377
|
+
* ```typescript
|
|
3378
|
+
* const stats = agent.getCompactionStats();
|
|
3379
|
+
* if (stats) {
|
|
3380
|
+
* console.log(`Total compactions: ${stats.totalCompactions}`);
|
|
3381
|
+
* console.log(`Tokens saved: ${stats.totalTokensSaved}`);
|
|
3382
|
+
* console.log(`Current usage: ${stats.currentUsage.percent.toFixed(1)}%`);
|
|
3383
|
+
* }
|
|
3384
|
+
* ```
|
|
3385
|
+
*/
|
|
3386
|
+
getCompactionStats() {
|
|
3387
|
+
return this.compactionManager?.getStats() ?? null;
|
|
3388
|
+
}
|
|
2577
3389
|
/**
|
|
2578
3390
|
* Run the agent loop.
|
|
2579
3391
|
* Clean, simple orchestration - all complexity is in StreamProcessor.
|
|
@@ -2594,6 +3406,30 @@ var init_agent = __esm({
|
|
|
2594
3406
|
while (currentIteration < this.maxIterations) {
|
|
2595
3407
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
2596
3408
|
try {
|
|
3409
|
+
if (this.compactionManager) {
|
|
3410
|
+
const compactionEvent = await this.compactionManager.checkAndCompact(
|
|
3411
|
+
this.conversation,
|
|
3412
|
+
currentIteration
|
|
3413
|
+
);
|
|
3414
|
+
if (compactionEvent) {
|
|
3415
|
+
this.logger.info("Context compacted", {
|
|
3416
|
+
strategy: compactionEvent.strategy,
|
|
3417
|
+
tokensBefore: compactionEvent.tokensBefore,
|
|
3418
|
+
tokensAfter: compactionEvent.tokensAfter
|
|
3419
|
+
});
|
|
3420
|
+
yield { type: "compaction", event: compactionEvent };
|
|
3421
|
+
await this.safeObserve(async () => {
|
|
3422
|
+
if (this.hooks.observers?.onCompaction) {
|
|
3423
|
+
await this.hooks.observers.onCompaction({
|
|
3424
|
+
iteration: currentIteration,
|
|
3425
|
+
event: compactionEvent,
|
|
3426
|
+
stats: this.compactionManager.getStats(),
|
|
3427
|
+
logger: this.logger
|
|
3428
|
+
});
|
|
3429
|
+
}
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
}
|
|
2597
3433
|
let llmOptions = {
|
|
2598
3434
|
model: this.model,
|
|
2599
3435
|
messages: this.conversation.getMessages(),
|
|
@@ -2613,6 +3449,7 @@ var init_agent = __esm({
|
|
|
2613
3449
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
2614
3450
|
const context = {
|
|
2615
3451
|
iteration: currentIteration,
|
|
3452
|
+
maxIterations: this.maxIterations,
|
|
2616
3453
|
options: llmOptions,
|
|
2617
3454
|
logger: this.logger
|
|
2618
3455
|
};
|
|
@@ -2677,12 +3514,17 @@ var init_agent = __esm({
|
|
|
2677
3514
|
});
|
|
2678
3515
|
let finalMessage = result.finalMessage;
|
|
2679
3516
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
3517
|
+
const gadgetCallCount = result.outputs.filter(
|
|
3518
|
+
(output) => output.type === "gadget_result"
|
|
3519
|
+
).length;
|
|
2680
3520
|
const context = {
|
|
2681
3521
|
iteration: currentIteration,
|
|
3522
|
+
maxIterations: this.maxIterations,
|
|
2682
3523
|
options: llmOptions,
|
|
2683
3524
|
finishReason: result.finishReason,
|
|
2684
3525
|
usage: result.usage,
|
|
2685
3526
|
finalMessage: result.finalMessage,
|
|
3527
|
+
gadgetCallCount,
|
|
2686
3528
|
logger: this.logger
|
|
2687
3529
|
};
|
|
2688
3530
|
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
@@ -4917,6 +5759,7 @@ var init_builder = __esm({
|
|
|
4917
5759
|
defaultGadgetTimeoutMs;
|
|
4918
5760
|
gadgetOutputLimit;
|
|
4919
5761
|
gadgetOutputLimitPercent;
|
|
5762
|
+
compactionConfig;
|
|
4920
5763
|
constructor(client) {
|
|
4921
5764
|
this.client = client;
|
|
4922
5765
|
}
|
|
@@ -5312,6 +6155,57 @@ var init_builder = __esm({
|
|
|
5312
6155
|
this.gadgetOutputLimitPercent = percent;
|
|
5313
6156
|
return this;
|
|
5314
6157
|
}
|
|
6158
|
+
/**
|
|
6159
|
+
* Configure context compaction.
|
|
6160
|
+
*
|
|
6161
|
+
* Context compaction automatically manages conversation history to prevent
|
|
6162
|
+
* context window overflow in long-running agent conversations.
|
|
6163
|
+
*
|
|
6164
|
+
* @param config - Compaction configuration options
|
|
6165
|
+
* @returns This builder for chaining
|
|
6166
|
+
*
|
|
6167
|
+
* @example
|
|
6168
|
+
* ```typescript
|
|
6169
|
+
* // Custom thresholds
|
|
6170
|
+
* .withCompaction({
|
|
6171
|
+
* triggerThresholdPercent: 70,
|
|
6172
|
+
* targetPercent: 40,
|
|
6173
|
+
* preserveRecentTurns: 10,
|
|
6174
|
+
* })
|
|
6175
|
+
*
|
|
6176
|
+
* // Different strategy
|
|
6177
|
+
* .withCompaction({
|
|
6178
|
+
* strategy: 'sliding-window',
|
|
6179
|
+
* })
|
|
6180
|
+
*
|
|
6181
|
+
* // With callback
|
|
6182
|
+
* .withCompaction({
|
|
6183
|
+
* onCompaction: (event) => {
|
|
6184
|
+
* console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
|
|
6185
|
+
* }
|
|
6186
|
+
* })
|
|
6187
|
+
* ```
|
|
6188
|
+
*/
|
|
6189
|
+
withCompaction(config) {
|
|
6190
|
+
this.compactionConfig = { ...config, enabled: config.enabled ?? true };
|
|
6191
|
+
return this;
|
|
6192
|
+
}
|
|
6193
|
+
/**
|
|
6194
|
+
* Disable context compaction.
|
|
6195
|
+
*
|
|
6196
|
+
* By default, compaction is enabled. Use this method to explicitly disable it.
|
|
6197
|
+
*
|
|
6198
|
+
* @returns This builder for chaining
|
|
6199
|
+
*
|
|
6200
|
+
* @example
|
|
6201
|
+
* ```typescript
|
|
6202
|
+
* .withoutCompaction() // Disable automatic compaction
|
|
6203
|
+
* ```
|
|
6204
|
+
*/
|
|
6205
|
+
withoutCompaction() {
|
|
6206
|
+
this.compactionConfig = { enabled: false };
|
|
6207
|
+
return this;
|
|
6208
|
+
}
|
|
5315
6209
|
/**
|
|
5316
6210
|
* Add a synthetic gadget call to the conversation history.
|
|
5317
6211
|
*
|
|
@@ -5427,7 +6321,8 @@ ${endPrefix}`
|
|
|
5427
6321
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5428
6322
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5429
6323
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5430
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6324
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6325
|
+
compactionConfig: this.compactionConfig
|
|
5431
6326
|
};
|
|
5432
6327
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5433
6328
|
}
|
|
@@ -5529,7 +6424,8 @@ ${endPrefix}`
|
|
|
5529
6424
|
shouldContinueAfterError: this.shouldContinueAfterError,
|
|
5530
6425
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5531
6426
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5532
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
|
|
6427
|
+
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6428
|
+
compactionConfig: this.compactionConfig
|
|
5533
6429
|
};
|
|
5534
6430
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5535
6431
|
}
|
|
@@ -5588,7 +6484,7 @@ var import_commander2 = require("commander");
|
|
|
5588
6484
|
// package.json
|
|
5589
6485
|
var package_default = {
|
|
5590
6486
|
name: "llmist",
|
|
5591
|
-
version: "1.
|
|
6487
|
+
version: "1.2.0",
|
|
5592
6488
|
description: "Universal TypeScript LLM client with streaming-first agent framework. Works with any model - no structured outputs or native tool calling required. Implements its own flexible grammar for function calling.",
|
|
5593
6489
|
type: "module",
|
|
5594
6490
|
main: "dist/index.cjs",
|
|
@@ -5735,26 +6631,16 @@ var askUser = createGadget({
|
|
|
5735
6631
|
});
|
|
5736
6632
|
var tellUser = createGadget({
|
|
5737
6633
|
name: "TellUser",
|
|
5738
|
-
description: "Tell the user something important.
|
|
6634
|
+
description: "Tell the user something important.",
|
|
5739
6635
|
schema: import_zod2.z.object({
|
|
5740
6636
|
message: import_zod2.z.string().optional().describe("The message to display to the user in Markdown"),
|
|
5741
|
-
done: import_zod2.z.boolean().default(false).describe("Set to true to end the conversation, false to continue"),
|
|
5742
6637
|
type: import_zod2.z.enum(["info", "success", "warning", "error"]).default("info").describe("Message type: info, success, warning, or error")
|
|
5743
6638
|
}),
|
|
5744
6639
|
examples: [
|
|
5745
6640
|
{
|
|
5746
|
-
comment: "
|
|
5747
|
-
params: {
|
|
5748
|
-
message: "I've completed the refactoring. All tests pass.",
|
|
5749
|
-
done: true,
|
|
5750
|
-
type: "success"
|
|
5751
|
-
}
|
|
5752
|
-
},
|
|
5753
|
-
{
|
|
5754
|
-
comment: "Warn the user about something without ending",
|
|
6641
|
+
comment: "Warn the user about something",
|
|
5755
6642
|
params: {
|
|
5756
6643
|
message: "Found 3 files with potential issues. Continuing analysis...",
|
|
5757
|
-
done: false,
|
|
5758
6644
|
type: "warning"
|
|
5759
6645
|
}
|
|
5760
6646
|
},
|
|
@@ -5762,12 +6648,11 @@ var tellUser = createGadget({
|
|
|
5762
6648
|
comment: "Share detailed analysis with bullet points (use heredoc for multiline)",
|
|
5763
6649
|
params: {
|
|
5764
6650
|
message: "Here's what I found in the codebase:\n\n1. **Main entry point**: `src/index.ts` exports all public APIs\n2. **Core logic**: Located in `src/core/` with 5 modules\n3. **Tests**: Good coverage in `src/__tests__/`\n\nI'll continue exploring the core modules.",
|
|
5765
|
-
done: false,
|
|
5766
6651
|
type: "info"
|
|
5767
6652
|
}
|
|
5768
6653
|
}
|
|
5769
6654
|
],
|
|
5770
|
-
execute: ({ message,
|
|
6655
|
+
execute: ({ message, type }) => {
|
|
5771
6656
|
if (!message || message.trim() === "") {
|
|
5772
6657
|
return "\u26A0\uFE0F TellUser was called without a message. Please provide content in the 'message' field.";
|
|
5773
6658
|
}
|
|
@@ -5777,14 +6662,24 @@ var tellUser = createGadget({
|
|
|
5777
6662
|
warning: "\u26A0\uFE0F ",
|
|
5778
6663
|
error: "\u274C "
|
|
5779
6664
|
};
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
6665
|
+
return prefixes[type] + message;
|
|
6666
|
+
}
|
|
6667
|
+
});
|
|
6668
|
+
var finish = createGadget({
|
|
6669
|
+
name: "Finish",
|
|
6670
|
+
description: "Signal that you have completed your task. Call this when your work is done.",
|
|
6671
|
+
schema: import_zod2.z.object({}),
|
|
6672
|
+
examples: [
|
|
6673
|
+
{
|
|
6674
|
+
comment: "Signal task completion",
|
|
6675
|
+
params: {}
|
|
5783
6676
|
}
|
|
5784
|
-
|
|
6677
|
+
],
|
|
6678
|
+
execute: () => {
|
|
6679
|
+
throw new BreakLoopException("Task completed");
|
|
5785
6680
|
}
|
|
5786
6681
|
});
|
|
5787
|
-
var builtinGadgets = [askUser, tellUser];
|
|
6682
|
+
var builtinGadgets = [askUser, tellUser, finish];
|
|
5788
6683
|
|
|
5789
6684
|
// src/cli/gadgets.ts
|
|
5790
6685
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -6340,6 +7235,17 @@ var StreamProgress = class {
|
|
|
6340
7235
|
} else {
|
|
6341
7236
|
parts.push(iterPart);
|
|
6342
7237
|
}
|
|
7238
|
+
const usagePercent = this.getContextUsagePercent();
|
|
7239
|
+
if (usagePercent !== null) {
|
|
7240
|
+
const formatted = `${Math.round(usagePercent)}%`;
|
|
7241
|
+
if (usagePercent >= 80) {
|
|
7242
|
+
parts.push(import_chalk2.default.red(formatted));
|
|
7243
|
+
} else if (usagePercent >= 50) {
|
|
7244
|
+
parts.push(import_chalk2.default.yellow(formatted));
|
|
7245
|
+
} else {
|
|
7246
|
+
parts.push(import_chalk2.default.green(formatted));
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
6343
7249
|
if (this.callInputTokens > 0) {
|
|
6344
7250
|
const prefix = this.callInputTokensEstimated ? "~" : "";
|
|
6345
7251
|
parts.push(import_chalk2.default.dim("\u2191") + import_chalk2.default.yellow(` ${prefix}${formatTokens(this.callInputTokens)}`));
|
|
@@ -6375,6 +7281,21 @@ var StreamProgress = class {
|
|
|
6375
7281
|
return 0;
|
|
6376
7282
|
}
|
|
6377
7283
|
}
|
|
7284
|
+
/**
|
|
7285
|
+
* Calculates context window usage percentage.
|
|
7286
|
+
* Returns null if model is unknown or context window unavailable.
|
|
7287
|
+
*/
|
|
7288
|
+
getContextUsagePercent() {
|
|
7289
|
+
if (!this.modelRegistry || !this.model || this.callInputTokens === 0) {
|
|
7290
|
+
return null;
|
|
7291
|
+
}
|
|
7292
|
+
const modelName = this.model.includes(":") ? this.model.split(":")[1] : this.model;
|
|
7293
|
+
const limits = this.modelRegistry.getModelLimits(modelName);
|
|
7294
|
+
if (!limits?.contextWindow) {
|
|
7295
|
+
return null;
|
|
7296
|
+
}
|
|
7297
|
+
return this.callInputTokens / limits.contextWindow * 100;
|
|
7298
|
+
}
|
|
6378
7299
|
renderCumulativeMode(spinner) {
|
|
6379
7300
|
const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
|
|
6380
7301
|
const parts = [];
|
|
@@ -7002,7 +7923,9 @@ function resolveTemplate(eta, template, context = {}, configPath) {
|
|
|
7002
7923
|
try {
|
|
7003
7924
|
const fullContext = {
|
|
7004
7925
|
...context,
|
|
7005
|
-
env: process.env
|
|
7926
|
+
env: process.env,
|
|
7927
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
7928
|
+
// "2025-12-01"
|
|
7006
7929
|
};
|
|
7007
7930
|
return eta.renderString(template, fullContext);
|
|
7008
7931
|
} catch (error) {
|