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.
@@ -348,10 +348,21 @@ function resolveRulesTemplate(rules, context) {
348
348
  }
349
349
  return [resolved];
350
350
  }
351
- var DEFAULT_PROMPTS;
351
+ function resolveHintTemplate(template, defaultValue, context) {
352
+ const resolved = template ?? defaultValue;
353
+ if (typeof resolved === "function") {
354
+ return resolved(context);
355
+ }
356
+ return resolved.replace(/\{iteration\}/g, String(context.iteration)).replace(/\{maxIterations\}/g, String(context.maxIterations)).replace(/\{remaining\}/g, String(context.remaining));
357
+ }
358
+ var DEFAULT_HINTS, DEFAULT_PROMPTS;
352
359
  var init_prompt_config = __esm({
353
360
  "src/core/prompt-config.ts"() {
354
361
  "use strict";
362
+ DEFAULT_HINTS = {
363
+ parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.",
364
+ iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly."
365
+ };
355
366
  DEFAULT_PROMPTS = {
356
367
  mainInstruction: [
357
368
  "\u26A0\uFE0F CRITICAL: RESPOND ONLY WITH GADGET INVOCATIONS",
@@ -1116,6 +1127,417 @@ var init_output_viewer = __esm({
1116
1127
  }
1117
1128
  });
1118
1129
 
1130
+ // src/agent/compaction/config.ts
1131
+ function resolveCompactionConfig(config = {}) {
1132
+ const trigger = config.triggerThresholdPercent ?? DEFAULT_COMPACTION_CONFIG.triggerThresholdPercent;
1133
+ const target = config.targetPercent ?? DEFAULT_COMPACTION_CONFIG.targetPercent;
1134
+ if (target >= trigger) {
1135
+ console.warn(
1136
+ `[llmist/compaction] targetPercent (${target}) should be less than triggerThresholdPercent (${trigger}) to be effective.`
1137
+ );
1138
+ }
1139
+ const strategy = config.strategy ?? DEFAULT_COMPACTION_CONFIG.strategy;
1140
+ const strategyName = typeof strategy === "object" && "name" in strategy ? strategy.name : strategy;
1141
+ return {
1142
+ enabled: config.enabled ?? DEFAULT_COMPACTION_CONFIG.enabled,
1143
+ strategy: strategyName,
1144
+ triggerThresholdPercent: trigger,
1145
+ targetPercent: target,
1146
+ preserveRecentTurns: config.preserveRecentTurns ?? DEFAULT_COMPACTION_CONFIG.preserveRecentTurns,
1147
+ summarizationModel: config.summarizationModel,
1148
+ summarizationPrompt: config.summarizationPrompt ?? DEFAULT_SUMMARIZATION_PROMPT,
1149
+ onCompaction: config.onCompaction
1150
+ };
1151
+ }
1152
+ var DEFAULT_COMPACTION_CONFIG, DEFAULT_SUMMARIZATION_PROMPT;
1153
+ var init_config = __esm({
1154
+ "src/agent/compaction/config.ts"() {
1155
+ "use strict";
1156
+ DEFAULT_COMPACTION_CONFIG = {
1157
+ enabled: true,
1158
+ strategy: "hybrid",
1159
+ triggerThresholdPercent: 80,
1160
+ targetPercent: 50,
1161
+ preserveRecentTurns: 5
1162
+ };
1163
+ DEFAULT_SUMMARIZATION_PROMPT = `Summarize this conversation history concisely, preserving:
1164
+ 1. Key decisions made and their rationale
1165
+ 2. Important facts and data discovered
1166
+ 3. Errors encountered and how they were resolved
1167
+ 4. Current task context and goals
1168
+
1169
+ Format as a brief narrative paragraph, not bullet points.
1170
+ Previous conversation:`;
1171
+ }
1172
+ });
1173
+
1174
+ // src/agent/compaction/strategy.ts
1175
+ function groupIntoTurns(messages) {
1176
+ const turns = [];
1177
+ let currentTurn = [];
1178
+ for (const msg of messages) {
1179
+ if (msg.role === "user" && currentTurn.length > 0) {
1180
+ turns.push({
1181
+ messages: currentTurn,
1182
+ tokenEstimate: estimateTurnTokens(currentTurn)
1183
+ });
1184
+ currentTurn = [msg];
1185
+ } else {
1186
+ currentTurn.push(msg);
1187
+ }
1188
+ }
1189
+ if (currentTurn.length > 0) {
1190
+ turns.push({
1191
+ messages: currentTurn,
1192
+ tokenEstimate: estimateTurnTokens(currentTurn)
1193
+ });
1194
+ }
1195
+ return turns;
1196
+ }
1197
+ function estimateTurnTokens(messages) {
1198
+ return Math.ceil(messages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4);
1199
+ }
1200
+ function flattenTurns(turns) {
1201
+ return turns.flatMap((turn) => turn.messages);
1202
+ }
1203
+ var init_strategy = __esm({
1204
+ "src/agent/compaction/strategy.ts"() {
1205
+ "use strict";
1206
+ }
1207
+ });
1208
+
1209
+ // src/agent/compaction/strategies/sliding-window.ts
1210
+ var TRUNCATION_MARKER_TEMPLATE, SlidingWindowStrategy;
1211
+ var init_sliding_window = __esm({
1212
+ "src/agent/compaction/strategies/sliding-window.ts"() {
1213
+ "use strict";
1214
+ init_strategy();
1215
+ TRUNCATION_MARKER_TEMPLATE = "[Previous conversation truncated. Removed {count} turn(s) to fit context window.]";
1216
+ SlidingWindowStrategy = class {
1217
+ name = "sliding-window";
1218
+ async compact(messages, config, context) {
1219
+ const turns = groupIntoTurns(messages);
1220
+ const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
1221
+ if (turns.length <= preserveCount) {
1222
+ return {
1223
+ messages,
1224
+ strategyName: this.name,
1225
+ metadata: {
1226
+ originalCount: messages.length,
1227
+ compactedCount: messages.length,
1228
+ tokensBefore: context.currentTokens,
1229
+ tokensAfter: context.currentTokens
1230
+ }
1231
+ };
1232
+ }
1233
+ const turnsToKeep = turns.slice(-preserveCount);
1234
+ const turnsRemoved = turns.length - preserveCount;
1235
+ const truncationMarker = {
1236
+ role: "user",
1237
+ content: TRUNCATION_MARKER_TEMPLATE.replace("{count}", turnsRemoved.toString())
1238
+ };
1239
+ const compactedMessages = [truncationMarker, ...flattenTurns(turnsToKeep)];
1240
+ const tokensAfter = Math.ceil(
1241
+ compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
1242
+ );
1243
+ return {
1244
+ messages: compactedMessages,
1245
+ strategyName: this.name,
1246
+ metadata: {
1247
+ originalCount: messages.length,
1248
+ compactedCount: compactedMessages.length,
1249
+ tokensBefore: context.currentTokens,
1250
+ tokensAfter
1251
+ }
1252
+ };
1253
+ }
1254
+ };
1255
+ }
1256
+ });
1257
+
1258
+ // src/agent/compaction/strategies/summarization.ts
1259
+ var SummarizationStrategy;
1260
+ var init_summarization = __esm({
1261
+ "src/agent/compaction/strategies/summarization.ts"() {
1262
+ "use strict";
1263
+ init_strategy();
1264
+ SummarizationStrategy = class {
1265
+ name = "summarization";
1266
+ async compact(messages, config, context) {
1267
+ const turns = groupIntoTurns(messages);
1268
+ const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
1269
+ if (turns.length <= preserveCount) {
1270
+ return {
1271
+ messages,
1272
+ strategyName: this.name,
1273
+ metadata: {
1274
+ originalCount: messages.length,
1275
+ compactedCount: messages.length,
1276
+ tokensBefore: context.currentTokens,
1277
+ tokensAfter: context.currentTokens
1278
+ }
1279
+ };
1280
+ }
1281
+ const turnsToSummarize = turns.slice(0, -preserveCount);
1282
+ const turnsToKeep = turns.slice(-preserveCount);
1283
+ const conversationToSummarize = this.formatTurnsForSummary(flattenTurns(turnsToSummarize));
1284
+ const summary = await this.generateSummary(conversationToSummarize, config, context);
1285
+ const summaryMessage = {
1286
+ role: "user",
1287
+ content: `[Previous conversation summary]
1288
+ ${summary}
1289
+ [End of summary - conversation continues below]`
1290
+ };
1291
+ const compactedMessages = [summaryMessage, ...flattenTurns(turnsToKeep)];
1292
+ const tokensAfter = Math.ceil(
1293
+ compactedMessages.reduce((sum, msg) => sum + (msg.content?.length ?? 0), 0) / 4
1294
+ );
1295
+ return {
1296
+ messages: compactedMessages,
1297
+ summary,
1298
+ strategyName: this.name,
1299
+ metadata: {
1300
+ originalCount: messages.length,
1301
+ compactedCount: compactedMessages.length,
1302
+ tokensBefore: context.currentTokens,
1303
+ tokensAfter
1304
+ }
1305
+ };
1306
+ }
1307
+ /**
1308
+ * Formats messages into a readable conversation format for summarization.
1309
+ */
1310
+ formatTurnsForSummary(messages) {
1311
+ return messages.map((msg) => {
1312
+ const role = msg.role.charAt(0).toUpperCase() + msg.role.slice(1);
1313
+ return `${role}: ${msg.content}`;
1314
+ }).join("\n\n");
1315
+ }
1316
+ /**
1317
+ * Generates a summary using the configured LLM.
1318
+ */
1319
+ async generateSummary(conversation, config, context) {
1320
+ const model = config.summarizationModel ?? context.model;
1321
+ const prompt = `${config.summarizationPrompt}
1322
+
1323
+ ${conversation}`;
1324
+ const response = await context.client.complete(prompt, {
1325
+ model,
1326
+ temperature: 0.3
1327
+ // Low temperature for factual summarization
1328
+ });
1329
+ return response.trim();
1330
+ }
1331
+ };
1332
+ }
1333
+ });
1334
+
1335
+ // src/agent/compaction/strategies/hybrid.ts
1336
+ var MIN_TURNS_FOR_SUMMARIZATION, HybridStrategy;
1337
+ var init_hybrid = __esm({
1338
+ "src/agent/compaction/strategies/hybrid.ts"() {
1339
+ "use strict";
1340
+ init_strategy();
1341
+ init_sliding_window();
1342
+ init_summarization();
1343
+ MIN_TURNS_FOR_SUMMARIZATION = 3;
1344
+ HybridStrategy = class {
1345
+ name = "hybrid";
1346
+ slidingWindow = new SlidingWindowStrategy();
1347
+ summarization = new SummarizationStrategy();
1348
+ async compact(messages, config, context) {
1349
+ const turns = groupIntoTurns(messages);
1350
+ const preserveCount = Math.min(config.preserveRecentTurns, turns.length);
1351
+ if (turns.length <= preserveCount) {
1352
+ return {
1353
+ messages,
1354
+ strategyName: this.name,
1355
+ metadata: {
1356
+ originalCount: messages.length,
1357
+ compactedCount: messages.length,
1358
+ tokensBefore: context.currentTokens,
1359
+ tokensAfter: context.currentTokens
1360
+ }
1361
+ };
1362
+ }
1363
+ const turnsToSummarize = turns.length - preserveCount;
1364
+ if (turnsToSummarize < MIN_TURNS_FOR_SUMMARIZATION) {
1365
+ return this.slidingWindow.compact(messages, config, context);
1366
+ }
1367
+ return this.summarization.compact(messages, config, context);
1368
+ }
1369
+ };
1370
+ }
1371
+ });
1372
+
1373
+ // src/agent/compaction/strategies/index.ts
1374
+ var init_strategies = __esm({
1375
+ "src/agent/compaction/strategies/index.ts"() {
1376
+ "use strict";
1377
+ init_sliding_window();
1378
+ init_summarization();
1379
+ init_hybrid();
1380
+ }
1381
+ });
1382
+
1383
+ // src/agent/compaction/manager.ts
1384
+ function createStrategy(name) {
1385
+ switch (name) {
1386
+ case "sliding-window":
1387
+ return new SlidingWindowStrategy();
1388
+ case "summarization":
1389
+ return new SummarizationStrategy();
1390
+ case "hybrid":
1391
+ return new HybridStrategy();
1392
+ default:
1393
+ throw new Error(`Unknown compaction strategy: ${name}`);
1394
+ }
1395
+ }
1396
+ var CompactionManager;
1397
+ var init_manager = __esm({
1398
+ "src/agent/compaction/manager.ts"() {
1399
+ "use strict";
1400
+ init_config();
1401
+ init_strategies();
1402
+ CompactionManager = class {
1403
+ client;
1404
+ model;
1405
+ config;
1406
+ strategy;
1407
+ modelLimits;
1408
+ // Statistics
1409
+ totalCompactions = 0;
1410
+ totalTokensSaved = 0;
1411
+ lastTokenCount = 0;
1412
+ constructor(client, model, config = {}) {
1413
+ this.client = client;
1414
+ this.model = model;
1415
+ this.config = resolveCompactionConfig(config);
1416
+ if (typeof config.strategy === "object" && "compact" in config.strategy) {
1417
+ this.strategy = config.strategy;
1418
+ } else {
1419
+ this.strategy = createStrategy(this.config.strategy);
1420
+ }
1421
+ }
1422
+ /**
1423
+ * Check if compaction is needed and perform it if so.
1424
+ *
1425
+ * @param conversation - The conversation manager to compact
1426
+ * @param iteration - Current agent iteration (for event metadata)
1427
+ * @returns CompactionEvent if compaction was performed, null otherwise
1428
+ */
1429
+ async checkAndCompact(conversation, iteration) {
1430
+ if (!this.config.enabled) {
1431
+ return null;
1432
+ }
1433
+ if (!this.modelLimits) {
1434
+ this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
1435
+ if (!this.modelLimits) {
1436
+ return null;
1437
+ }
1438
+ }
1439
+ if (!this.client.countTokens) {
1440
+ return null;
1441
+ }
1442
+ const messages = conversation.getMessages();
1443
+ const currentTokens = await this.client.countTokens(this.model, messages);
1444
+ this.lastTokenCount = currentTokens;
1445
+ const usagePercent = currentTokens / this.modelLimits.contextWindow * 100;
1446
+ if (usagePercent < this.config.triggerThresholdPercent) {
1447
+ return null;
1448
+ }
1449
+ const historyMessages = conversation.getHistoryMessages();
1450
+ const baseMessages = conversation.getBaseMessages();
1451
+ const historyTokens = await this.client.countTokens(this.model, historyMessages);
1452
+ const baseTokens = await this.client.countTokens(this.model, baseMessages);
1453
+ return this.compact(conversation, iteration, {
1454
+ historyMessages,
1455
+ baseMessages,
1456
+ historyTokens,
1457
+ baseTokens,
1458
+ currentTokens: historyTokens + baseTokens
1459
+ });
1460
+ }
1461
+ /**
1462
+ * Force compaction regardless of threshold.
1463
+ *
1464
+ * @param conversation - The conversation manager to compact
1465
+ * @param iteration - Current agent iteration (for event metadata). Use -1 for manual compaction.
1466
+ * @param precomputed - Optional pre-computed token counts (passed from checkAndCompact for efficiency)
1467
+ * @returns CompactionEvent with compaction details
1468
+ */
1469
+ async compact(conversation, iteration, precomputed) {
1470
+ if (!this.modelLimits) {
1471
+ this.modelLimits = this.client.modelRegistry.getModelLimits(this.model);
1472
+ if (!this.modelLimits) {
1473
+ return null;
1474
+ }
1475
+ }
1476
+ const historyMessages = precomputed?.historyMessages ?? conversation.getHistoryMessages();
1477
+ const baseMessages = precomputed?.baseMessages ?? conversation.getBaseMessages();
1478
+ const historyTokens = precomputed?.historyTokens ?? await this.client.countTokens(this.model, historyMessages);
1479
+ const baseTokens = precomputed?.baseTokens ?? await this.client.countTokens(this.model, baseMessages);
1480
+ const currentTokens = precomputed?.currentTokens ?? historyTokens + baseTokens;
1481
+ const targetTotalTokens = Math.floor(
1482
+ this.modelLimits.contextWindow * this.config.targetPercent / 100
1483
+ );
1484
+ const targetHistoryTokens = Math.max(0, targetTotalTokens - baseTokens);
1485
+ const result = await this.strategy.compact(historyMessages, this.config, {
1486
+ currentTokens: historyTokens,
1487
+ targetTokens: targetHistoryTokens,
1488
+ modelLimits: this.modelLimits,
1489
+ client: this.client,
1490
+ model: this.config.summarizationModel ?? this.model
1491
+ });
1492
+ conversation.replaceHistory(result.messages);
1493
+ const afterTokens = await this.client.countTokens(this.model, conversation.getMessages());
1494
+ const tokensSaved = currentTokens - afterTokens;
1495
+ this.totalCompactions++;
1496
+ this.totalTokensSaved += tokensSaved;
1497
+ this.lastTokenCount = afterTokens;
1498
+ const event = {
1499
+ strategy: result.strategyName,
1500
+ tokensBefore: currentTokens,
1501
+ tokensAfter: afterTokens,
1502
+ messagesBefore: historyMessages.length + baseMessages.length,
1503
+ messagesAfter: result.messages.length + baseMessages.length,
1504
+ summary: result.summary,
1505
+ iteration
1506
+ };
1507
+ if (this.config.onCompaction) {
1508
+ try {
1509
+ this.config.onCompaction(event);
1510
+ } catch (err) {
1511
+ console.warn("[llmist/compaction] onCompaction callback error:", err);
1512
+ }
1513
+ }
1514
+ return event;
1515
+ }
1516
+ /**
1517
+ * Get compaction statistics.
1518
+ */
1519
+ getStats() {
1520
+ const contextWindow = this.modelLimits?.contextWindow ?? 0;
1521
+ return {
1522
+ totalCompactions: this.totalCompactions,
1523
+ totalTokensSaved: this.totalTokensSaved,
1524
+ currentUsage: {
1525
+ tokens: this.lastTokenCount,
1526
+ percent: contextWindow > 0 ? this.lastTokenCount / contextWindow * 100 : 0
1527
+ },
1528
+ contextWindow
1529
+ };
1530
+ }
1531
+ /**
1532
+ * Check if compaction is enabled.
1533
+ */
1534
+ isEnabled() {
1535
+ return this.config.enabled;
1536
+ }
1537
+ };
1538
+ }
1539
+ });
1540
+
1119
1541
  // src/agent/gadget-output-store.ts
1120
1542
  import { randomBytes } from "node:crypto";
1121
1543
  var GadgetOutputStore;
@@ -1206,10 +1628,16 @@ var init_conversation_manager = __esm({
1206
1628
  baseMessages;
1207
1629
  initialMessages;
1208
1630
  historyBuilder;
1631
+ startPrefix;
1632
+ endPrefix;
1633
+ argPrefix;
1209
1634
  constructor(baseMessages, initialMessages, options = {}) {
1210
1635
  this.baseMessages = baseMessages;
1211
1636
  this.initialMessages = initialMessages;
1212
1637
  this.historyBuilder = new LLMMessageBuilder();
1638
+ this.startPrefix = options.startPrefix;
1639
+ this.endPrefix = options.endPrefix;
1640
+ this.argPrefix = options.argPrefix;
1213
1641
  if (options.startPrefix && options.endPrefix) {
1214
1642
  this.historyBuilder.withPrefixes(options.startPrefix, options.endPrefix, options.argPrefix);
1215
1643
  }
@@ -1226,6 +1654,25 @@ var init_conversation_manager = __esm({
1226
1654
  getMessages() {
1227
1655
  return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
1228
1656
  }
1657
+ getHistoryMessages() {
1658
+ return this.historyBuilder.build();
1659
+ }
1660
+ getBaseMessages() {
1661
+ return [...this.baseMessages, ...this.initialMessages];
1662
+ }
1663
+ replaceHistory(newHistory) {
1664
+ this.historyBuilder = new LLMMessageBuilder();
1665
+ if (this.startPrefix && this.endPrefix) {
1666
+ this.historyBuilder.withPrefixes(this.startPrefix, this.endPrefix, this.argPrefix);
1667
+ }
1668
+ for (const msg of newHistory) {
1669
+ if (msg.role === "user") {
1670
+ this.historyBuilder.addUser(msg.content);
1671
+ } else if (msg.role === "assistant") {
1672
+ this.historyBuilder.addAssistant(msg.content);
1673
+ }
1674
+ }
1675
+ }
1229
1676
  };
1230
1677
  }
1231
1678
  });
@@ -1346,324 +1793,264 @@ var init_exceptions = __esm({
1346
1793
  }
1347
1794
  });
1348
1795
 
1349
- // src/gadgets/error-formatter.ts
1350
- var GadgetErrorFormatter;
1351
- var init_error_formatter = __esm({
1352
- "src/gadgets/error-formatter.ts"() {
1796
+ // src/gadgets/schema-introspector.ts
1797
+ function getDef(schema) {
1798
+ return schema._def;
1799
+ }
1800
+ function getTypeName(schema) {
1801
+ const def = getDef(schema);
1802
+ return def?.type ?? def?.typeName;
1803
+ }
1804
+ function getShape(schema) {
1805
+ const def = getDef(schema);
1806
+ if (typeof def?.shape === "function") {
1807
+ return def.shape();
1808
+ }
1809
+ return def?.shape;
1810
+ }
1811
+ var SchemaIntrospector;
1812
+ var init_schema_introspector = __esm({
1813
+ "src/gadgets/schema-introspector.ts"() {
1353
1814
  "use strict";
1354
- init_constants();
1355
- GadgetErrorFormatter = class {
1356
- argPrefix;
1357
- startPrefix;
1358
- endPrefix;
1359
- constructor(options = {}) {
1360
- this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
1361
- this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
1362
- this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
1815
+ SchemaIntrospector = class {
1816
+ schema;
1817
+ cache = /* @__PURE__ */ new Map();
1818
+ constructor(schema) {
1819
+ this.schema = schema;
1363
1820
  }
1364
1821
  /**
1365
- * Format a Zod validation error with full gadget instructions.
1822
+ * Get the expected type at a JSON pointer path.
1366
1823
  *
1367
- * @param gadgetName - Name of the gadget that was called
1368
- * @param zodError - The Zod validation error
1369
- * @param gadget - The gadget instance (for generating instructions)
1370
- * @returns Formatted error message with usage instructions
1824
+ * @param pointer - JSON pointer path without leading / (e.g., "config/timeout", "items/0")
1825
+ * @returns Type hint for coercion decision
1371
1826
  */
1372
- formatValidationError(gadgetName, zodError, gadget) {
1373
- const parts = [];
1374
- parts.push(`Error: Invalid parameters for '${gadgetName}':`);
1375
- for (const issue of zodError.issues) {
1376
- const path = issue.path.join(".") || "root";
1377
- parts.push(` - ${path}: ${issue.message}`);
1827
+ getTypeAtPath(pointer) {
1828
+ const cached = this.cache.get(pointer);
1829
+ if (cached !== void 0) {
1830
+ return cached;
1378
1831
  }
1379
- parts.push("");
1380
- parts.push("Gadget Usage:");
1381
- parts.push(gadget.getInstruction(this.argPrefix));
1382
- return parts.join("\n");
1832
+ const result = this.resolveTypeAtPath(pointer);
1833
+ this.cache.set(pointer, result);
1834
+ return result;
1383
1835
  }
1384
1836
  /**
1385
- * Format a parse error with block format reference.
1386
- *
1387
- * @param gadgetName - Name of the gadget that was called
1388
- * @param parseError - The parse error message
1389
- * @param gadget - The gadget instance if found (for generating instructions)
1390
- * @returns Formatted error message with format reference
1837
+ * Internal method to resolve type at path without caching.
1391
1838
  */
1392
- formatParseError(gadgetName, parseError, gadget) {
1393
- const parts = [];
1394
- parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
1395
- parts.push(` ${parseError}`);
1396
- if (gadget) {
1397
- parts.push("");
1398
- parts.push("Gadget Usage:");
1399
- parts.push(gadget.getInstruction(this.argPrefix));
1839
+ resolveTypeAtPath(pointer) {
1840
+ if (!pointer) {
1841
+ return this.getBaseType(this.schema);
1842
+ }
1843
+ const segments = pointer.split("/");
1844
+ let current = this.schema;
1845
+ for (const segment of segments) {
1846
+ current = this.unwrapSchema(current);
1847
+ const typeName = getTypeName(current);
1848
+ if (typeName === "object" || typeName === "ZodObject") {
1849
+ const shape = getShape(current);
1850
+ if (!shape || !(segment in shape)) {
1851
+ return "unknown";
1852
+ }
1853
+ current = shape[segment];
1854
+ } else if (typeName === "array" || typeName === "ZodArray") {
1855
+ if (!/^\d+$/.test(segment)) {
1856
+ return "unknown";
1857
+ }
1858
+ const def = getDef(current);
1859
+ const elementType = def?.element ?? def?.type;
1860
+ if (!elementType) {
1861
+ return "unknown";
1862
+ }
1863
+ current = elementType;
1864
+ } else if (typeName === "tuple" || typeName === "ZodTuple") {
1865
+ if (!/^\d+$/.test(segment)) {
1866
+ return "unknown";
1867
+ }
1868
+ const index = parseInt(segment, 10);
1869
+ const def = getDef(current);
1870
+ const items = def?.items;
1871
+ if (!items || index >= items.length) {
1872
+ return "unknown";
1873
+ }
1874
+ current = items[index];
1875
+ } else if (typeName === "record" || typeName === "ZodRecord") {
1876
+ const def = getDef(current);
1877
+ const valueType = def?.valueType;
1878
+ if (!valueType) {
1879
+ return "unknown";
1880
+ }
1881
+ current = valueType;
1882
+ } else {
1883
+ return "unknown";
1884
+ }
1400
1885
  }
1401
- parts.push("");
1402
- parts.push("Block Format Reference:");
1403
- parts.push(` ${this.startPrefix}${gadgetName}`);
1404
- parts.push(` ${this.argPrefix}parameterName`);
1405
- parts.push(" parameter value here");
1406
- parts.push(` ${this.endPrefix}`);
1407
- return parts.join("\n");
1886
+ return this.getBaseType(current);
1408
1887
  }
1409
1888
  /**
1410
- * Format a registry error (gadget not found) with available gadgets list.
1411
- *
1412
- * @param gadgetName - Name of the gadget that was not found
1413
- * @param availableGadgets - List of available gadget names
1414
- * @returns Formatted error message with available gadgets
1889
+ * Unwrap schema modifiers (optional, default, nullable, branded, etc.)
1890
+ * to get to the underlying type.
1415
1891
  */
1416
- formatRegistryError(gadgetName, availableGadgets) {
1417
- const parts = [];
1418
- parts.push(`Error: Gadget '${gadgetName}' not found.`);
1419
- if (availableGadgets.length > 0) {
1420
- parts.push("");
1421
- parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
1422
- } else {
1423
- parts.push("");
1424
- parts.push("No gadgets are currently registered.");
1892
+ unwrapSchema(schema) {
1893
+ let current = schema;
1894
+ let iterations = 0;
1895
+ const maxIterations = 20;
1896
+ while (iterations < maxIterations) {
1897
+ const typeName = getTypeName(current);
1898
+ const wrapperTypes = [
1899
+ "optional",
1900
+ "nullable",
1901
+ "default",
1902
+ "catch",
1903
+ "branded",
1904
+ "readonly",
1905
+ "pipeline",
1906
+ "ZodOptional",
1907
+ "ZodNullable",
1908
+ "ZodDefault",
1909
+ "ZodCatch",
1910
+ "ZodBranded",
1911
+ "ZodReadonly",
1912
+ "ZodPipeline"
1913
+ ];
1914
+ if (typeName && wrapperTypes.includes(typeName)) {
1915
+ const def = getDef(current);
1916
+ const inner = def?.innerType ?? def?.in ?? def?.type;
1917
+ if (!inner || inner === current) break;
1918
+ current = inner;
1919
+ iterations++;
1920
+ continue;
1921
+ }
1922
+ break;
1923
+ }
1924
+ return current;
1925
+ }
1926
+ /**
1927
+ * Get the primitive type hint from an unwrapped schema.
1928
+ */
1929
+ getBaseType(schema) {
1930
+ const unwrapped = this.unwrapSchema(schema);
1931
+ const typeName = getTypeName(unwrapped);
1932
+ switch (typeName) {
1933
+ // Primitive types
1934
+ case "string":
1935
+ case "ZodString":
1936
+ return "string";
1937
+ case "number":
1938
+ case "ZodNumber":
1939
+ case "bigint":
1940
+ case "ZodBigInt":
1941
+ return "number";
1942
+ case "boolean":
1943
+ case "ZodBoolean":
1944
+ return "boolean";
1945
+ // Literal types - check the literal value type
1946
+ case "literal":
1947
+ case "ZodLiteral": {
1948
+ const def = getDef(unwrapped);
1949
+ const values = def?.values;
1950
+ const value = values?.[0] ?? def?.value;
1951
+ if (typeof value === "string") return "string";
1952
+ if (typeof value === "number" || typeof value === "bigint")
1953
+ return "number";
1954
+ if (typeof value === "boolean") return "boolean";
1955
+ return "unknown";
1956
+ }
1957
+ // Enum - always string keys
1958
+ case "enum":
1959
+ case "ZodEnum":
1960
+ case "nativeEnum":
1961
+ case "ZodNativeEnum":
1962
+ return "string";
1963
+ // Union - return 'unknown' to let auto-coercion decide
1964
+ // Since multiple types are valid, we can't definitively say what the LLM intended
1965
+ // Auto-coercion will handle common cases (numbers, booleans) appropriately
1966
+ case "union":
1967
+ case "ZodUnion":
1968
+ return "unknown";
1969
+ // Discriminated union - complex, return unknown
1970
+ case "discriminatedUnion":
1971
+ case "ZodDiscriminatedUnion":
1972
+ return "unknown";
1973
+ // Intersection - check both sides
1974
+ case "intersection":
1975
+ case "ZodIntersection": {
1976
+ const def = getDef(unwrapped);
1977
+ const left = def?.left;
1978
+ const right = def?.right;
1979
+ if (!left || !right) return "unknown";
1980
+ const leftType = this.getBaseType(left);
1981
+ const rightType = this.getBaseType(right);
1982
+ if (leftType === rightType) return leftType;
1983
+ if (leftType === "string" || rightType === "string") return "string";
1984
+ return "unknown";
1985
+ }
1986
+ // Effects/transforms - return unknown to let Zod handle it
1987
+ case "effects":
1988
+ case "ZodEffects":
1989
+ return "unknown";
1990
+ // Lazy - can't resolve without evaluating
1991
+ case "lazy":
1992
+ case "ZodLazy":
1993
+ return "unknown";
1994
+ // Complex types - return unknown
1995
+ case "object":
1996
+ case "ZodObject":
1997
+ case "array":
1998
+ case "ZodArray":
1999
+ case "tuple":
2000
+ case "ZodTuple":
2001
+ case "record":
2002
+ case "ZodRecord":
2003
+ case "map":
2004
+ case "ZodMap":
2005
+ case "set":
2006
+ case "ZodSet":
2007
+ case "function":
2008
+ case "ZodFunction":
2009
+ case "promise":
2010
+ case "ZodPromise":
2011
+ case "date":
2012
+ case "ZodDate":
2013
+ return "unknown";
2014
+ // Unknown/any/never/void/undefined/null
2015
+ case "unknown":
2016
+ case "ZodUnknown":
2017
+ case "any":
2018
+ case "ZodAny":
2019
+ case "never":
2020
+ case "ZodNever":
2021
+ case "void":
2022
+ case "ZodVoid":
2023
+ case "undefined":
2024
+ case "ZodUndefined":
2025
+ case "null":
2026
+ case "ZodNull":
2027
+ return "unknown";
2028
+ default:
2029
+ return "unknown";
1425
2030
  }
1426
- return parts.join("\n");
1427
2031
  }
1428
2032
  };
1429
2033
  }
1430
2034
  });
1431
2035
 
1432
- // src/gadgets/executor.ts
1433
- var GadgetExecutor;
1434
- var init_executor = __esm({
1435
- "src/gadgets/executor.ts"() {
1436
- "use strict";
1437
- init_logger();
1438
- init_error_formatter();
1439
- init_exceptions();
1440
- GadgetExecutor = class {
1441
- constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
1442
- this.registry = registry;
1443
- this.onHumanInputRequired = onHumanInputRequired;
1444
- this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
1445
- this.logger = logger ?? createLogger({ name: "llmist:executor" });
1446
- this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
1447
- }
1448
- logger;
1449
- errorFormatter;
1450
- /**
1451
- * Creates a promise that rejects with a TimeoutException after the specified timeout.
1452
- */
1453
- createTimeoutPromise(gadgetName, timeoutMs) {
1454
- return new Promise((_, reject) => {
1455
- setTimeout(() => {
1456
- reject(new TimeoutException(gadgetName, timeoutMs));
1457
- }, timeoutMs);
1458
- });
1459
- }
1460
- // Execute a gadget call asynchronously
1461
- async execute(call) {
1462
- const startTime = Date.now();
1463
- this.logger.debug("Executing gadget", {
1464
- gadgetName: call.gadgetName,
1465
- invocationId: call.invocationId,
1466
- parameters: call.parameters
1467
- });
1468
- const rawParameters = call.parameters ?? {};
1469
- let validatedParameters = rawParameters;
1470
- try {
1471
- const gadget = this.registry.get(call.gadgetName);
1472
- if (!gadget) {
1473
- this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
1474
- const availableGadgets = this.registry.getNames();
1475
- return {
1476
- gadgetName: call.gadgetName,
1477
- invocationId: call.invocationId,
1478
- parameters: call.parameters ?? {},
1479
- error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
1480
- executionTimeMs: Date.now() - startTime
1481
- };
1482
- }
1483
- if (call.parseError || !call.parameters) {
1484
- this.logger.error("Gadget parameter parse error", {
1485
- gadgetName: call.gadgetName,
1486
- parseError: call.parseError,
1487
- rawParameters: call.parametersRaw
1488
- });
1489
- const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
1490
- return {
1491
- gadgetName: call.gadgetName,
1492
- invocationId: call.invocationId,
1493
- parameters: {},
1494
- error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
1495
- executionTimeMs: Date.now() - startTime
1496
- };
1497
- }
1498
- if (gadget.parameterSchema) {
1499
- const validationResult = gadget.parameterSchema.safeParse(rawParameters);
1500
- if (!validationResult.success) {
1501
- const validationError = this.errorFormatter.formatValidationError(
1502
- call.gadgetName,
1503
- validationResult.error,
1504
- gadget
1505
- );
1506
- this.logger.error("Gadget parameter validation failed", {
1507
- gadgetName: call.gadgetName,
1508
- issueCount: validationResult.error.issues.length
1509
- });
1510
- return {
1511
- gadgetName: call.gadgetName,
1512
- invocationId: call.invocationId,
1513
- parameters: rawParameters,
1514
- error: validationError,
1515
- executionTimeMs: Date.now() - startTime
1516
- };
1517
- }
1518
- validatedParameters = validationResult.data;
1519
- }
1520
- const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
1521
- let result;
1522
- if (timeoutMs && timeoutMs > 0) {
1523
- this.logger.debug("Executing gadget with timeout", {
1524
- gadgetName: call.gadgetName,
1525
- timeoutMs
1526
- });
1527
- result = await Promise.race([
1528
- Promise.resolve(gadget.execute(validatedParameters)),
1529
- this.createTimeoutPromise(call.gadgetName, timeoutMs)
1530
- ]);
1531
- } else {
1532
- result = await Promise.resolve(gadget.execute(validatedParameters));
1533
- }
1534
- const executionTimeMs = Date.now() - startTime;
1535
- this.logger.info("Gadget executed successfully", {
1536
- gadgetName: call.gadgetName,
1537
- invocationId: call.invocationId,
1538
- executionTimeMs
1539
- });
1540
- this.logger.debug("Gadget result", {
1541
- gadgetName: call.gadgetName,
1542
- invocationId: call.invocationId,
1543
- parameters: validatedParameters,
1544
- result,
1545
- executionTimeMs
1546
- });
1547
- return {
1548
- gadgetName: call.gadgetName,
1549
- invocationId: call.invocationId,
1550
- parameters: validatedParameters,
1551
- result,
1552
- executionTimeMs
1553
- };
1554
- } catch (error) {
1555
- if (error instanceof BreakLoopException) {
1556
- this.logger.info("Gadget requested loop termination", {
1557
- gadgetName: call.gadgetName,
1558
- message: error.message
1559
- });
1560
- return {
1561
- gadgetName: call.gadgetName,
1562
- invocationId: call.invocationId,
1563
- parameters: validatedParameters,
1564
- result: error.message,
1565
- breaksLoop: true,
1566
- executionTimeMs: Date.now() - startTime
1567
- };
1568
- }
1569
- if (error instanceof TimeoutException) {
1570
- this.logger.error("Gadget execution timed out", {
1571
- gadgetName: call.gadgetName,
1572
- timeoutMs: error.timeoutMs,
1573
- executionTimeMs: Date.now() - startTime
1574
- });
1575
- return {
1576
- gadgetName: call.gadgetName,
1577
- invocationId: call.invocationId,
1578
- parameters: validatedParameters,
1579
- error: error.message,
1580
- executionTimeMs: Date.now() - startTime
1581
- };
1582
- }
1583
- if (error instanceof HumanInputException) {
1584
- this.logger.info("Gadget requested human input", {
1585
- gadgetName: call.gadgetName,
1586
- question: error.question
1587
- });
1588
- if (this.onHumanInputRequired) {
1589
- try {
1590
- const answer = await this.onHumanInputRequired(error.question);
1591
- this.logger.debug("Human input received", {
1592
- gadgetName: call.gadgetName,
1593
- answerLength: answer.length
1594
- });
1595
- return {
1596
- gadgetName: call.gadgetName,
1597
- invocationId: call.invocationId,
1598
- parameters: validatedParameters,
1599
- result: answer,
1600
- executionTimeMs: Date.now() - startTime
1601
- };
1602
- } catch (inputError) {
1603
- this.logger.error("Human input callback error", {
1604
- gadgetName: call.gadgetName,
1605
- error: inputError instanceof Error ? inputError.message : String(inputError)
1606
- });
1607
- return {
1608
- gadgetName: call.gadgetName,
1609
- invocationId: call.invocationId,
1610
- parameters: validatedParameters,
1611
- error: inputError instanceof Error ? inputError.message : String(inputError),
1612
- executionTimeMs: Date.now() - startTime
1613
- };
1614
- }
1615
- }
1616
- this.logger.warn("Human input required but no callback provided", {
1617
- gadgetName: call.gadgetName
1618
- });
1619
- return {
1620
- gadgetName: call.gadgetName,
1621
- invocationId: call.invocationId,
1622
- parameters: validatedParameters,
1623
- error: "Human input required but not available (stdin is not interactive)",
1624
- executionTimeMs: Date.now() - startTime
1625
- };
1626
- }
1627
- const executionTimeMs = Date.now() - startTime;
1628
- this.logger.error("Gadget execution failed", {
1629
- gadgetName: call.gadgetName,
1630
- error: error instanceof Error ? error.message : String(error),
1631
- executionTimeMs
1632
- });
1633
- return {
1634
- gadgetName: call.gadgetName,
1635
- invocationId: call.invocationId,
1636
- parameters: validatedParameters,
1637
- error: error instanceof Error ? error.message : String(error),
1638
- executionTimeMs
1639
- };
1640
- }
1641
- }
1642
- // Execute multiple gadget calls in parallel
1643
- async executeAll(calls) {
1644
- return Promise.all(calls.map((call) => this.execute(call)));
1645
- }
1646
- };
1647
- }
1648
- });
1649
-
1650
- // src/gadgets/block-params.ts
1651
- function parseBlockParams(content, options) {
1652
- const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
1653
- const result = {};
1654
- const seenPointers = /* @__PURE__ */ new Set();
1655
- const parts = content.split(argPrefix);
1656
- for (let i = 1; i < parts.length; i++) {
1657
- const part = parts[i];
1658
- const newlineIndex = part.indexOf("\n");
1659
- if (newlineIndex === -1) {
1660
- const pointer2 = part.trim();
1661
- if (pointer2) {
1662
- if (seenPointers.has(pointer2)) {
1663
- throw new Error(`Duplicate pointer: ${pointer2}`);
1664
- }
1665
- seenPointers.add(pointer2);
1666
- setByPointer(result, pointer2, "");
2036
+ // src/gadgets/block-params.ts
2037
+ function parseBlockParams(content, options) {
2038
+ const argPrefix = options?.argPrefix ?? GADGET_ARG_PREFIX;
2039
+ const result = {};
2040
+ const seenPointers = /* @__PURE__ */ new Set();
2041
+ const introspector = options?.schema ? new SchemaIntrospector(options.schema) : void 0;
2042
+ const parts = content.split(argPrefix);
2043
+ for (let i = 1; i < parts.length; i++) {
2044
+ const part = parts[i];
2045
+ const newlineIndex = part.indexOf("\n");
2046
+ if (newlineIndex === -1) {
2047
+ const pointer2 = part.trim();
2048
+ if (pointer2) {
2049
+ if (seenPointers.has(pointer2)) {
2050
+ throw new Error(`Duplicate pointer: ${pointer2}`);
2051
+ }
2052
+ seenPointers.add(pointer2);
2053
+ setByPointer(result, pointer2, "", introspector);
1667
2054
  }
1668
2055
  continue;
1669
2056
  }
@@ -1679,15 +2066,30 @@ function parseBlockParams(content, options) {
1679
2066
  throw new Error(`Duplicate pointer: ${pointer}`);
1680
2067
  }
1681
2068
  seenPointers.add(pointer);
1682
- setByPointer(result, pointer, value);
2069
+ setByPointer(result, pointer, value, introspector);
1683
2070
  }
1684
2071
  return result;
1685
2072
  }
1686
- function coerceValue(value) {
2073
+ function coerceValue(value, expectedType) {
1687
2074
  if (value.includes("\n")) {
1688
2075
  return value;
1689
2076
  }
1690
2077
  const trimmed = value.trim();
2078
+ if (expectedType === "string") {
2079
+ return value;
2080
+ }
2081
+ if (expectedType === "boolean") {
2082
+ if (trimmed === "true") return true;
2083
+ if (trimmed === "false") return false;
2084
+ return value;
2085
+ }
2086
+ if (expectedType === "number") {
2087
+ const num = Number(trimmed);
2088
+ if (!isNaN(num) && isFinite(num) && trimmed !== "") {
2089
+ return num;
2090
+ }
2091
+ return value;
2092
+ }
1691
2093
  if (trimmed === "true") return true;
1692
2094
  if (trimmed === "false") return false;
1693
2095
  if (trimmed !== "" && /^-?\d+(\.\d+)?$/.test(trimmed)) {
@@ -1698,7 +2100,7 @@ function coerceValue(value) {
1698
2100
  }
1699
2101
  return value;
1700
2102
  }
1701
- function setByPointer(obj, pointer, value) {
2103
+ function setByPointer(obj, pointer, value, introspector) {
1702
2104
  const segments = pointer.split("/");
1703
2105
  let current = obj;
1704
2106
  for (let i = 0; i < segments.length - 1; i++) {
@@ -1726,7 +2128,8 @@ function setByPointer(obj, pointer, value) {
1726
2128
  }
1727
2129
  }
1728
2130
  const lastSegment = segments[segments.length - 1];
1729
- const coercedValue = coerceValue(value);
2131
+ const expectedType = introspector?.getTypeAtPath(pointer);
2132
+ const coercedValue = coerceValue(value, expectedType);
1730
2133
  if (Array.isArray(current)) {
1731
2134
  const index = parseInt(lastSegment, 10);
1732
2135
  if (isNaN(index) || index < 0) {
@@ -1744,6 +2147,7 @@ var init_block_params = __esm({
1744
2147
  "src/gadgets/block-params.ts"() {
1745
2148
  "use strict";
1746
2149
  init_constants();
2150
+ init_schema_introspector();
1747
2151
  }
1748
2152
  });
1749
2153
 
@@ -1861,64 +2265,424 @@ var init_parser = __esm({
1861
2265
  break;
1862
2266
  }
1863
2267
  }
1864
- const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
1865
- const { parameters, parseError } = this.parseParameters(parametersRaw);
1866
- yield {
1867
- type: "gadget_call",
1868
- call: {
1869
- gadgetName: actualGadgetName,
1870
- invocationId,
1871
- parametersRaw,
1872
- parameters,
1873
- parseError
2268
+ const parametersRaw = this.buffer.substring(contentStartIndex, partEndIndex).trim();
2269
+ const { parameters, parseError } = this.parseParameters(parametersRaw);
2270
+ yield {
2271
+ type: "gadget_call",
2272
+ call: {
2273
+ gadgetName: actualGadgetName,
2274
+ invocationId,
2275
+ parametersRaw,
2276
+ parameters,
2277
+ parseError
2278
+ }
2279
+ };
2280
+ startIndex = partEndIndex + endMarkerLength;
2281
+ this.lastReportedTextLength = startIndex;
2282
+ }
2283
+ if (startIndex > 0) {
2284
+ this.buffer = this.buffer.substring(startIndex);
2285
+ this.lastReportedTextLength = 0;
2286
+ }
2287
+ }
2288
+ // Finalize parsing and return remaining text or incomplete gadgets
2289
+ *finalize() {
2290
+ const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
2291
+ if (startIndex !== -1) {
2292
+ const textBefore = this.takeTextUntil(startIndex);
2293
+ if (textBefore !== void 0) {
2294
+ yield { type: "text", content: textBefore };
2295
+ }
2296
+ const metadataStartIndex = startIndex + this.startPrefix.length;
2297
+ const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
2298
+ if (metadataEndIndex !== -1) {
2299
+ const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
2300
+ const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
2301
+ const contentStartIndex = metadataEndIndex + 1;
2302
+ const parametersRaw = this.buffer.substring(contentStartIndex).trim();
2303
+ const { parameters, parseError } = this.parseParameters(parametersRaw);
2304
+ yield {
2305
+ type: "gadget_call",
2306
+ call: {
2307
+ gadgetName: actualGadgetName,
2308
+ invocationId,
2309
+ parametersRaw,
2310
+ parameters,
2311
+ parseError
2312
+ }
2313
+ };
2314
+ return;
2315
+ }
2316
+ }
2317
+ const remainingText = this.takeTextUntil(this.buffer.length);
2318
+ if (remainingText !== void 0) {
2319
+ yield { type: "text", content: remainingText };
2320
+ }
2321
+ }
2322
+ // Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
2323
+ reset() {
2324
+ this.buffer = "";
2325
+ this.lastReportedTextLength = 0;
2326
+ }
2327
+ };
2328
+ }
2329
+ });
2330
+
2331
+ // src/gadgets/error-formatter.ts
2332
+ var GadgetErrorFormatter;
2333
+ var init_error_formatter = __esm({
2334
+ "src/gadgets/error-formatter.ts"() {
2335
+ "use strict";
2336
+ init_constants();
2337
+ GadgetErrorFormatter = class {
2338
+ argPrefix;
2339
+ startPrefix;
2340
+ endPrefix;
2341
+ constructor(options = {}) {
2342
+ this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
2343
+ this.startPrefix = options.startPrefix ?? GADGET_START_PREFIX;
2344
+ this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
2345
+ }
2346
+ /**
2347
+ * Format a Zod validation error with full gadget instructions.
2348
+ *
2349
+ * @param gadgetName - Name of the gadget that was called
2350
+ * @param zodError - The Zod validation error
2351
+ * @param gadget - The gadget instance (for generating instructions)
2352
+ * @returns Formatted error message with usage instructions
2353
+ */
2354
+ formatValidationError(gadgetName, zodError, gadget) {
2355
+ const parts = [];
2356
+ parts.push(`Error: Invalid parameters for '${gadgetName}':`);
2357
+ for (const issue of zodError.issues) {
2358
+ const path = issue.path.join(".") || "root";
2359
+ parts.push(` - ${path}: ${issue.message}`);
2360
+ }
2361
+ parts.push("");
2362
+ parts.push("Gadget Usage:");
2363
+ parts.push(gadget.getInstruction(this.argPrefix));
2364
+ return parts.join("\n");
2365
+ }
2366
+ /**
2367
+ * Format a parse error with block format reference.
2368
+ *
2369
+ * @param gadgetName - Name of the gadget that was called
2370
+ * @param parseError - The parse error message
2371
+ * @param gadget - The gadget instance if found (for generating instructions)
2372
+ * @returns Formatted error message with format reference
2373
+ */
2374
+ formatParseError(gadgetName, parseError, gadget) {
2375
+ const parts = [];
2376
+ parts.push(`Error: Failed to parse parameters for '${gadgetName}':`);
2377
+ parts.push(` ${parseError}`);
2378
+ if (gadget) {
2379
+ parts.push("");
2380
+ parts.push("Gadget Usage:");
2381
+ parts.push(gadget.getInstruction(this.argPrefix));
2382
+ }
2383
+ parts.push("");
2384
+ parts.push("Block Format Reference:");
2385
+ parts.push(` ${this.startPrefix}${gadgetName}`);
2386
+ parts.push(` ${this.argPrefix}parameterName`);
2387
+ parts.push(" parameter value here");
2388
+ parts.push(` ${this.endPrefix}`);
2389
+ return parts.join("\n");
2390
+ }
2391
+ /**
2392
+ * Format a registry error (gadget not found) with available gadgets list.
2393
+ *
2394
+ * @param gadgetName - Name of the gadget that was not found
2395
+ * @param availableGadgets - List of available gadget names
2396
+ * @returns Formatted error message with available gadgets
2397
+ */
2398
+ formatRegistryError(gadgetName, availableGadgets) {
2399
+ const parts = [];
2400
+ parts.push(`Error: Gadget '${gadgetName}' not found.`);
2401
+ if (availableGadgets.length > 0) {
2402
+ parts.push("");
2403
+ parts.push(`Available gadgets: ${availableGadgets.join(", ")}`);
2404
+ } else {
2405
+ parts.push("");
2406
+ parts.push("No gadgets are currently registered.");
2407
+ }
2408
+ return parts.join("\n");
2409
+ }
2410
+ };
2411
+ }
2412
+ });
2413
+
2414
+ // src/gadgets/executor.ts
2415
+ var GadgetExecutor;
2416
+ var init_executor = __esm({
2417
+ "src/gadgets/executor.ts"() {
2418
+ "use strict";
2419
+ init_constants();
2420
+ init_logger();
2421
+ init_block_params();
2422
+ init_error_formatter();
2423
+ init_exceptions();
2424
+ init_parser();
2425
+ GadgetExecutor = class {
2426
+ constructor(registry, onHumanInputRequired, logger, defaultGadgetTimeoutMs, errorFormatterOptions) {
2427
+ this.registry = registry;
2428
+ this.onHumanInputRequired = onHumanInputRequired;
2429
+ this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
2430
+ this.logger = logger ?? createLogger({ name: "llmist:executor" });
2431
+ this.errorFormatter = new GadgetErrorFormatter(errorFormatterOptions);
2432
+ this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
2433
+ }
2434
+ logger;
2435
+ errorFormatter;
2436
+ argPrefix;
2437
+ /**
2438
+ * Creates a promise that rejects with a TimeoutException after the specified timeout.
2439
+ */
2440
+ createTimeoutPromise(gadgetName, timeoutMs) {
2441
+ return new Promise((_, reject) => {
2442
+ setTimeout(() => {
2443
+ reject(new TimeoutException(gadgetName, timeoutMs));
2444
+ }, timeoutMs);
2445
+ });
2446
+ }
2447
+ // Execute a gadget call asynchronously
2448
+ async execute(call) {
2449
+ const startTime = Date.now();
2450
+ this.logger.debug("Executing gadget", {
2451
+ gadgetName: call.gadgetName,
2452
+ invocationId: call.invocationId,
2453
+ parameters: call.parameters
2454
+ });
2455
+ const rawParameters = call.parameters ?? {};
2456
+ let validatedParameters = rawParameters;
2457
+ try {
2458
+ const gadget = this.registry.get(call.gadgetName);
2459
+ if (!gadget) {
2460
+ this.logger.error("Gadget not found", { gadgetName: call.gadgetName });
2461
+ const availableGadgets = this.registry.getNames();
2462
+ return {
2463
+ gadgetName: call.gadgetName,
2464
+ invocationId: call.invocationId,
2465
+ parameters: call.parameters ?? {},
2466
+ error: this.errorFormatter.formatRegistryError(call.gadgetName, availableGadgets),
2467
+ executionTimeMs: Date.now() - startTime
2468
+ };
2469
+ }
2470
+ if (call.parseError || !call.parameters) {
2471
+ this.logger.error("Gadget parameter parse error", {
2472
+ gadgetName: call.gadgetName,
2473
+ parseError: call.parseError,
2474
+ rawParameters: call.parametersRaw
2475
+ });
2476
+ const parseErrorMessage = call.parseError ?? "Failed to parse parameters";
2477
+ return {
2478
+ gadgetName: call.gadgetName,
2479
+ invocationId: call.invocationId,
2480
+ parameters: {},
2481
+ error: this.errorFormatter.formatParseError(call.gadgetName, parseErrorMessage, gadget),
2482
+ executionTimeMs: Date.now() - startTime
2483
+ };
2484
+ }
2485
+ let schemaAwareParameters = rawParameters;
2486
+ const hasBlockFormat = call.parametersRaw?.includes(this.argPrefix);
2487
+ if (gadget.parameterSchema && hasBlockFormat) {
2488
+ try {
2489
+ const cleanedRaw = stripMarkdownFences(call.parametersRaw);
2490
+ const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
2491
+ const parametersWereModified = !this.deepEquals(rawParameters, initialParse);
2492
+ if (parametersWereModified) {
2493
+ this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
2494
+ gadgetName: call.gadgetName
2495
+ });
2496
+ schemaAwareParameters = rawParameters;
2497
+ } else {
2498
+ schemaAwareParameters = parseBlockParams(cleanedRaw, {
2499
+ argPrefix: this.argPrefix,
2500
+ schema: gadget.parameterSchema
2501
+ });
2502
+ this.logger.debug("Re-parsed parameters with schema", {
2503
+ gadgetName: call.gadgetName,
2504
+ original: rawParameters,
2505
+ schemaAware: schemaAwareParameters
2506
+ });
2507
+ }
2508
+ } catch (error) {
2509
+ this.logger.warn("Schema-aware re-parsing failed, using original parameters", {
2510
+ gadgetName: call.gadgetName,
2511
+ error: error instanceof Error ? error.message : String(error)
2512
+ });
2513
+ schemaAwareParameters = rawParameters;
2514
+ }
2515
+ }
2516
+ if (gadget.parameterSchema) {
2517
+ const validationResult = gadget.parameterSchema.safeParse(schemaAwareParameters);
2518
+ if (!validationResult.success) {
2519
+ const validationError = this.errorFormatter.formatValidationError(
2520
+ call.gadgetName,
2521
+ validationResult.error,
2522
+ gadget
2523
+ );
2524
+ this.logger.error("Gadget parameter validation failed", {
2525
+ gadgetName: call.gadgetName,
2526
+ issueCount: validationResult.error.issues.length
2527
+ });
2528
+ return {
2529
+ gadgetName: call.gadgetName,
2530
+ invocationId: call.invocationId,
2531
+ parameters: schemaAwareParameters,
2532
+ error: validationError,
2533
+ executionTimeMs: Date.now() - startTime
2534
+ };
1874
2535
  }
2536
+ validatedParameters = validationResult.data;
2537
+ } else {
2538
+ validatedParameters = schemaAwareParameters;
2539
+ }
2540
+ const timeoutMs = gadget.timeoutMs ?? this.defaultGadgetTimeoutMs;
2541
+ let result;
2542
+ if (timeoutMs && timeoutMs > 0) {
2543
+ this.logger.debug("Executing gadget with timeout", {
2544
+ gadgetName: call.gadgetName,
2545
+ timeoutMs
2546
+ });
2547
+ result = await Promise.race([
2548
+ Promise.resolve(gadget.execute(validatedParameters)),
2549
+ this.createTimeoutPromise(call.gadgetName, timeoutMs)
2550
+ ]);
2551
+ } else {
2552
+ result = await Promise.resolve(gadget.execute(validatedParameters));
2553
+ }
2554
+ const executionTimeMs = Date.now() - startTime;
2555
+ this.logger.info("Gadget executed successfully", {
2556
+ gadgetName: call.gadgetName,
2557
+ invocationId: call.invocationId,
2558
+ executionTimeMs
2559
+ });
2560
+ this.logger.debug("Gadget result", {
2561
+ gadgetName: call.gadgetName,
2562
+ invocationId: call.invocationId,
2563
+ parameters: validatedParameters,
2564
+ result,
2565
+ executionTimeMs
2566
+ });
2567
+ return {
2568
+ gadgetName: call.gadgetName,
2569
+ invocationId: call.invocationId,
2570
+ parameters: validatedParameters,
2571
+ result,
2572
+ executionTimeMs
1875
2573
  };
1876
- startIndex = partEndIndex + endMarkerLength;
1877
- this.lastReportedTextLength = startIndex;
1878
- }
1879
- if (startIndex > 0) {
1880
- this.buffer = this.buffer.substring(startIndex);
1881
- this.lastReportedTextLength = 0;
1882
- }
1883
- }
1884
- // Finalize parsing and return remaining text or incomplete gadgets
1885
- *finalize() {
1886
- const startIndex = this.buffer.indexOf(this.startPrefix, this.lastReportedTextLength);
1887
- if (startIndex !== -1) {
1888
- const textBefore = this.takeTextUntil(startIndex);
1889
- if (textBefore !== void 0) {
1890
- yield { type: "text", content: textBefore };
2574
+ } catch (error) {
2575
+ if (error instanceof BreakLoopException) {
2576
+ this.logger.info("Gadget requested loop termination", {
2577
+ gadgetName: call.gadgetName,
2578
+ message: error.message
2579
+ });
2580
+ return {
2581
+ gadgetName: call.gadgetName,
2582
+ invocationId: call.invocationId,
2583
+ parameters: validatedParameters,
2584
+ result: error.message,
2585
+ breaksLoop: true,
2586
+ executionTimeMs: Date.now() - startTime
2587
+ };
1891
2588
  }
1892
- const metadataStartIndex = startIndex + this.startPrefix.length;
1893
- const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
1894
- if (metadataEndIndex !== -1) {
1895
- const gadgetName = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
1896
- const { actualName: actualGadgetName, invocationId } = this.parseGadgetName(gadgetName);
1897
- const contentStartIndex = metadataEndIndex + 1;
1898
- const parametersRaw = this.buffer.substring(contentStartIndex).trim();
1899
- const { parameters, parseError } = this.parseParameters(parametersRaw);
1900
- yield {
1901
- type: "gadget_call",
1902
- call: {
1903
- gadgetName: actualGadgetName,
1904
- invocationId,
1905
- parametersRaw,
1906
- parameters,
1907
- parseError
2589
+ if (error instanceof TimeoutException) {
2590
+ this.logger.error("Gadget execution timed out", {
2591
+ gadgetName: call.gadgetName,
2592
+ timeoutMs: error.timeoutMs,
2593
+ executionTimeMs: Date.now() - startTime
2594
+ });
2595
+ return {
2596
+ gadgetName: call.gadgetName,
2597
+ invocationId: call.invocationId,
2598
+ parameters: validatedParameters,
2599
+ error: error.message,
2600
+ executionTimeMs: Date.now() - startTime
2601
+ };
2602
+ }
2603
+ if (error instanceof HumanInputException) {
2604
+ this.logger.info("Gadget requested human input", {
2605
+ gadgetName: call.gadgetName,
2606
+ question: error.question
2607
+ });
2608
+ if (this.onHumanInputRequired) {
2609
+ try {
2610
+ const answer = await this.onHumanInputRequired(error.question);
2611
+ this.logger.debug("Human input received", {
2612
+ gadgetName: call.gadgetName,
2613
+ answerLength: answer.length
2614
+ });
2615
+ return {
2616
+ gadgetName: call.gadgetName,
2617
+ invocationId: call.invocationId,
2618
+ parameters: validatedParameters,
2619
+ result: answer,
2620
+ executionTimeMs: Date.now() - startTime
2621
+ };
2622
+ } catch (inputError) {
2623
+ this.logger.error("Human input callback error", {
2624
+ gadgetName: call.gadgetName,
2625
+ error: inputError instanceof Error ? inputError.message : String(inputError)
2626
+ });
2627
+ return {
2628
+ gadgetName: call.gadgetName,
2629
+ invocationId: call.invocationId,
2630
+ parameters: validatedParameters,
2631
+ error: inputError instanceof Error ? inputError.message : String(inputError),
2632
+ executionTimeMs: Date.now() - startTime
2633
+ };
1908
2634
  }
2635
+ }
2636
+ this.logger.warn("Human input required but no callback provided", {
2637
+ gadgetName: call.gadgetName
2638
+ });
2639
+ return {
2640
+ gadgetName: call.gadgetName,
2641
+ invocationId: call.invocationId,
2642
+ parameters: validatedParameters,
2643
+ error: "Human input required but not available (stdin is not interactive)",
2644
+ executionTimeMs: Date.now() - startTime
1909
2645
  };
1910
- return;
1911
2646
  }
1912
- }
1913
- const remainingText = this.takeTextUntil(this.buffer.length);
1914
- if (remainingText !== void 0) {
1915
- yield { type: "text", content: remainingText };
2647
+ const executionTimeMs = Date.now() - startTime;
2648
+ this.logger.error("Gadget execution failed", {
2649
+ gadgetName: call.gadgetName,
2650
+ error: error instanceof Error ? error.message : String(error),
2651
+ executionTimeMs
2652
+ });
2653
+ return {
2654
+ gadgetName: call.gadgetName,
2655
+ invocationId: call.invocationId,
2656
+ parameters: validatedParameters,
2657
+ error: error instanceof Error ? error.message : String(error),
2658
+ executionTimeMs
2659
+ };
1916
2660
  }
1917
2661
  }
1918
- // Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
1919
- reset() {
1920
- this.buffer = "";
1921
- this.lastReportedTextLength = 0;
2662
+ // Execute multiple gadget calls in parallel
2663
+ async executeAll(calls) {
2664
+ return Promise.all(calls.map((call) => this.execute(call)));
2665
+ }
2666
+ /**
2667
+ * Deep equality check for objects/arrays.
2668
+ * Used to detect if parameters were modified by an interceptor.
2669
+ */
2670
+ deepEquals(a, b) {
2671
+ if (a === b) return true;
2672
+ if (a === null || b === null) return a === b;
2673
+ if (typeof a !== typeof b) return false;
2674
+ if (typeof a !== "object") return a === b;
2675
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2676
+ if (Array.isArray(a) && Array.isArray(b)) {
2677
+ if (a.length !== b.length) return false;
2678
+ return a.every((val, i) => this.deepEquals(val, b[i]));
2679
+ }
2680
+ const aObj = a;
2681
+ const bObj = b;
2682
+ const aKeys = Object.keys(aObj);
2683
+ const bKeys = Object.keys(bObj);
2684
+ if (aKeys.length !== bKeys.length) return false;
2685
+ return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
1922
2686
  }
1923
2687
  };
1924
2688
  }
@@ -2115,7 +2879,8 @@ var init_stream_processor = __esm({
2115
2879
  options.registry,
2116
2880
  options.onHumanInputRequired,
2117
2881
  this.logger.getSubLogger({ name: "executor" }),
2118
- options.defaultGadgetTimeoutMs
2882
+ options.defaultGadgetTimeoutMs,
2883
+ { argPrefix: options.gadgetArgPrefix }
2119
2884
  );
2120
2885
  }
2121
2886
  /**
@@ -4227,6 +4992,7 @@ var init_agent = __esm({
4227
4992
  init_model_shortcuts();
4228
4993
  init_output_viewer();
4229
4994
  init_logger();
4995
+ init_manager();
4230
4996
  init_gadget_output_store();
4231
4997
  init_agent_internal_key();
4232
4998
  init_conversation_manager();
@@ -4257,6 +5023,8 @@ var init_agent = __esm({
4257
5023
  outputStore;
4258
5024
  outputLimitEnabled;
4259
5025
  outputLimitCharLimit;
5026
+ // Context compaction
5027
+ compactionManager;
4260
5028
  /**
4261
5029
  * Creates a new Agent instance.
4262
5030
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -4316,6 +5084,14 @@ var init_agent = __esm({
4316
5084
  if (options.userPrompt) {
4317
5085
  this.conversation.addUserMessage(options.userPrompt);
4318
5086
  }
5087
+ const compactionEnabled = options.compactionConfig?.enabled ?? true;
5088
+ if (compactionEnabled) {
5089
+ this.compactionManager = new CompactionManager(
5090
+ this.client,
5091
+ this.model,
5092
+ options.compactionConfig
5093
+ );
5094
+ }
4319
5095
  }
4320
5096
  /**
4321
5097
  * Get the gadget registry for this agent.
@@ -4338,6 +5114,53 @@ var init_agent = __esm({
4338
5114
  getRegistry() {
4339
5115
  return this.registry;
4340
5116
  }
5117
+ /**
5118
+ * Manually trigger context compaction.
5119
+ *
5120
+ * Forces compaction regardless of threshold. Useful for:
5121
+ * - Pre-emptive context management before expected long operations
5122
+ * - Testing compaction behavior
5123
+ *
5124
+ * @returns CompactionEvent if compaction was performed, null if not configured or no history
5125
+ *
5126
+ * @example
5127
+ * ```typescript
5128
+ * const agent = await LLMist.createAgent()
5129
+ * .withModel('sonnet')
5130
+ * .withCompaction()
5131
+ * .ask('...');
5132
+ *
5133
+ * // Manually compact before a long operation
5134
+ * const event = await agent.compact();
5135
+ * if (event) {
5136
+ * console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
5137
+ * }
5138
+ * ```
5139
+ */
5140
+ async compact() {
5141
+ if (!this.compactionManager) {
5142
+ return null;
5143
+ }
5144
+ return this.compactionManager.compact(this.conversation, -1);
5145
+ }
5146
+ /**
5147
+ * Get compaction statistics.
5148
+ *
5149
+ * @returns CompactionStats if compaction is enabled, null otherwise
5150
+ *
5151
+ * @example
5152
+ * ```typescript
5153
+ * const stats = agent.getCompactionStats();
5154
+ * if (stats) {
5155
+ * console.log(`Total compactions: ${stats.totalCompactions}`);
5156
+ * console.log(`Tokens saved: ${stats.totalTokensSaved}`);
5157
+ * console.log(`Current usage: ${stats.currentUsage.percent.toFixed(1)}%`);
5158
+ * }
5159
+ * ```
5160
+ */
5161
+ getCompactionStats() {
5162
+ return this.compactionManager?.getStats() ?? null;
5163
+ }
4341
5164
  /**
4342
5165
  * Run the agent loop.
4343
5166
  * Clean, simple orchestration - all complexity is in StreamProcessor.
@@ -4358,6 +5181,30 @@ var init_agent = __esm({
4358
5181
  while (currentIteration < this.maxIterations) {
4359
5182
  this.logger.debug("Starting iteration", { iteration: currentIteration });
4360
5183
  try {
5184
+ if (this.compactionManager) {
5185
+ const compactionEvent = await this.compactionManager.checkAndCompact(
5186
+ this.conversation,
5187
+ currentIteration
5188
+ );
5189
+ if (compactionEvent) {
5190
+ this.logger.info("Context compacted", {
5191
+ strategy: compactionEvent.strategy,
5192
+ tokensBefore: compactionEvent.tokensBefore,
5193
+ tokensAfter: compactionEvent.tokensAfter
5194
+ });
5195
+ yield { type: "compaction", event: compactionEvent };
5196
+ await this.safeObserve(async () => {
5197
+ if (this.hooks.observers?.onCompaction) {
5198
+ await this.hooks.observers.onCompaction({
5199
+ iteration: currentIteration,
5200
+ event: compactionEvent,
5201
+ stats: this.compactionManager.getStats(),
5202
+ logger: this.logger
5203
+ });
5204
+ }
5205
+ });
5206
+ }
5207
+ }
4361
5208
  let llmOptions = {
4362
5209
  model: this.model,
4363
5210
  messages: this.conversation.getMessages(),
@@ -4377,6 +5224,7 @@ var init_agent = __esm({
4377
5224
  if (this.hooks.controllers?.beforeLLMCall) {
4378
5225
  const context = {
4379
5226
  iteration: currentIteration,
5227
+ maxIterations: this.maxIterations,
4380
5228
  options: llmOptions,
4381
5229
  logger: this.logger
4382
5230
  };
@@ -4441,12 +5289,17 @@ var init_agent = __esm({
4441
5289
  });
4442
5290
  let finalMessage = result.finalMessage;
4443
5291
  if (this.hooks.controllers?.afterLLMCall) {
5292
+ const gadgetCallCount = result.outputs.filter(
5293
+ (output) => output.type === "gadget_result"
5294
+ ).length;
4444
5295
  const context = {
4445
5296
  iteration: currentIteration,
5297
+ maxIterations: this.maxIterations,
4446
5298
  options: llmOptions,
4447
5299
  finishReason: result.finishReason,
4448
5300
  usage: result.usage,
4449
5301
  finalMessage: result.finalMessage,
5302
+ gadgetCallCount,
4450
5303
  logger: this.logger
4451
5304
  };
4452
5305
  const action = await this.hooks.controllers.afterLLMCall(context);
@@ -4708,6 +5561,7 @@ var init_builder = __esm({
4708
5561
  defaultGadgetTimeoutMs;
4709
5562
  gadgetOutputLimit;
4710
5563
  gadgetOutputLimitPercent;
5564
+ compactionConfig;
4711
5565
  constructor(client) {
4712
5566
  this.client = client;
4713
5567
  }
@@ -5103,6 +5957,57 @@ var init_builder = __esm({
5103
5957
  this.gadgetOutputLimitPercent = percent;
5104
5958
  return this;
5105
5959
  }
5960
+ /**
5961
+ * Configure context compaction.
5962
+ *
5963
+ * Context compaction automatically manages conversation history to prevent
5964
+ * context window overflow in long-running agent conversations.
5965
+ *
5966
+ * @param config - Compaction configuration options
5967
+ * @returns This builder for chaining
5968
+ *
5969
+ * @example
5970
+ * ```typescript
5971
+ * // Custom thresholds
5972
+ * .withCompaction({
5973
+ * triggerThresholdPercent: 70,
5974
+ * targetPercent: 40,
5975
+ * preserveRecentTurns: 10,
5976
+ * })
5977
+ *
5978
+ * // Different strategy
5979
+ * .withCompaction({
5980
+ * strategy: 'sliding-window',
5981
+ * })
5982
+ *
5983
+ * // With callback
5984
+ * .withCompaction({
5985
+ * onCompaction: (event) => {
5986
+ * console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
5987
+ * }
5988
+ * })
5989
+ * ```
5990
+ */
5991
+ withCompaction(config) {
5992
+ this.compactionConfig = { ...config, enabled: config.enabled ?? true };
5993
+ return this;
5994
+ }
5995
+ /**
5996
+ * Disable context compaction.
5997
+ *
5998
+ * By default, compaction is enabled. Use this method to explicitly disable it.
5999
+ *
6000
+ * @returns This builder for chaining
6001
+ *
6002
+ * @example
6003
+ * ```typescript
6004
+ * .withoutCompaction() // Disable automatic compaction
6005
+ * ```
6006
+ */
6007
+ withoutCompaction() {
6008
+ this.compactionConfig = { enabled: false };
6009
+ return this;
6010
+ }
5106
6011
  /**
5107
6012
  * Add a synthetic gadget call to the conversation history.
5108
6013
  *
@@ -5218,7 +6123,8 @@ ${endPrefix}`
5218
6123
  shouldContinueAfterError: this.shouldContinueAfterError,
5219
6124
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
5220
6125
  gadgetOutputLimit: this.gadgetOutputLimit,
5221
- gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
6126
+ gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6127
+ compactionConfig: this.compactionConfig
5222
6128
  };
5223
6129
  return new Agent(AGENT_INTERNAL_KEY, options);
5224
6130
  }
@@ -5320,7 +6226,8 @@ ${endPrefix}`
5320
6226
  shouldContinueAfterError: this.shouldContinueAfterError,
5321
6227
  defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
5322
6228
  gadgetOutputLimit: this.gadgetOutputLimit,
5323
- gadgetOutputLimitPercent: this.gadgetOutputLimitPercent
6229
+ gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
6230
+ compactionConfig: this.compactionConfig
5324
6231
  };
5325
6232
  return new Agent(AGENT_INTERNAL_KEY, options);
5326
6233
  }
@@ -5585,9 +6492,11 @@ export {
5585
6492
  init_schema_validator,
5586
6493
  GadgetRegistry,
5587
6494
  init_registry,
6495
+ DEFAULT_HINTS,
5588
6496
  DEFAULT_PROMPTS,
5589
6497
  resolvePromptTemplate,
5590
6498
  resolveRulesTemplate,
6499
+ resolveHintTemplate,
5591
6500
  init_prompt_config,
5592
6501
  LLMMessageBuilder,
5593
6502
  init_messages,
@@ -5602,6 +6511,16 @@ export {
5602
6511
  init_create_gadget,
5603
6512
  createGadgetOutputViewer,
5604
6513
  init_output_viewer,
6514
+ DEFAULT_COMPACTION_CONFIG,
6515
+ DEFAULT_SUMMARIZATION_PROMPT,
6516
+ init_config,
6517
+ init_strategy,
6518
+ SlidingWindowStrategy,
6519
+ SummarizationStrategy,
6520
+ HybridStrategy,
6521
+ init_strategies,
6522
+ CompactionManager,
6523
+ init_manager,
5605
6524
  GadgetOutputStore,
5606
6525
  init_gadget_output_store,
5607
6526
  ConversationManager,
@@ -5613,10 +6532,10 @@ export {
5613
6532
  BreakLoopException,
5614
6533
  HumanInputException,
5615
6534
  init_exceptions,
5616
- GadgetExecutor,
5617
- init_executor,
5618
6535
  StreamParser,
5619
6536
  init_parser,
6537
+ GadgetExecutor,
6538
+ init_executor,
5620
6539
  StreamProcessor,
5621
6540
  init_stream_processor,
5622
6541
  FALLBACK_CHARS_PER_TOKEN,
@@ -5644,4 +6563,4 @@ export {
5644
6563
  AgentBuilder,
5645
6564
  init_builder
5646
6565
  };
5647
- //# sourceMappingURL=chunk-VXPZQZF5.js.map
6566
+ //# sourceMappingURL=chunk-RZTAKIDE.js.map