llmist 16.0.3 → 16.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -632,8 +632,6 @@ interface CompleteGadgetParams {
632
632
  storedMedia?: StoredMedia[];
633
633
  }
634
634
 
635
- /** Event listener function type */
636
- type EventListener = (event: ExecutionEvent) => void;
637
635
  /**
638
636
  * The Execution Tree - single source of truth for all execution state.
639
637
  *
@@ -668,12 +666,9 @@ type EventListener = (event: ExecutionEvent) => void;
668
666
  declare class ExecutionTree {
669
667
  private nodes;
670
668
  private rootIds;
671
- private eventListeners;
672
- private eventIdCounter;
673
669
  private invocationIdToNodeId;
674
- private eventQueue;
675
- private eventWaiters;
676
- private isCompleted;
670
+ private emitter;
671
+ private aggregator;
677
672
  /**
678
673
  * Base depth for all nodes in this tree.
679
674
  * Used when this tree is a subagent's view into a parent tree.
@@ -831,11 +826,11 @@ declare class ExecutionTree {
831
826
  * });
832
827
  * ```
833
828
  */
834
- on(type: ExecutionEventType, listener: EventListener): () => void;
829
+ on(type: ExecutionEventType, listener: (event: ExecutionEvent) => void): () => void;
835
830
  /**
836
831
  * Subscribe to all events.
837
832
  */
838
- onAll(listener: EventListener): () => void;
833
+ onAll(listener: (event: ExecutionEvent) => void): () => void;
839
834
  /**
840
835
  * Get async iterable of all events.
841
836
  * Events are yielded as they occur.
@@ -843,6 +838,7 @@ declare class ExecutionTree {
843
838
  events(): AsyncGenerator<ExecutionEvent>;
844
839
  /**
845
840
  * Mark the tree as complete (no more events will be emitted).
841
+ * Wakes up any consumers waiting in the events() async generator.
846
842
  */
847
843
  complete(): void;
848
844
  /**
@@ -1127,7 +1123,6 @@ declare function Gadget<TSchema extends ZodType>(config: GadgetConfig<TSchema>):
1127
1123
  throwIfAborted(ctx?: ExecutionContext): void;
1128
1124
  onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
1129
1125
  createLinkedAbortController(ctx?: ExecutionContext): AbortController;
1130
- get instruction(): string;
1131
1126
  getInstruction(optionsOrArgPrefix?: string | {
1132
1127
  argPrefix?: string;
1133
1128
  startPrefix?: string;
@@ -1138,6 +1133,253 @@ declare function Gadget<TSchema extends ZodType>(config: GadgetConfig<TSchema>):
1138
1133
  };
1139
1134
  };
1140
1135
 
1136
+ /**
1137
+ * Proactive rate limiting for LLM API calls.
1138
+ *
1139
+ * Tracks request and token usage in sliding windows to prevent rate limit errors
1140
+ * before they occur. Works in conjunction with reactive backoff (retry.ts) for
1141
+ * comprehensive rate limit handling.
1142
+ */
1143
+ /**
1144
+ * Configuration for proactive rate limiting.
1145
+ *
1146
+ * Set these values based on your API tier to prevent rate limit errors.
1147
+ * When limits are approached, requests will be automatically delayed.
1148
+ *
1149
+ * @example
1150
+ * ```typescript
1151
+ * // Gemini free tier limits
1152
+ * const agent = LLMist.createAgent()
1153
+ * .withRateLimits({
1154
+ * requestsPerMinute: 15,
1155
+ * tokensPerMinute: 1_000_000,
1156
+ * safetyMargin: 0.8,
1157
+ * });
1158
+ *
1159
+ * // OpenAI Tier 1 limits
1160
+ * const agent = LLMist.createAgent()
1161
+ * .withRateLimits({
1162
+ * requestsPerMinute: 500,
1163
+ * tokensPerMinute: 200_000,
1164
+ * });
1165
+ * ```
1166
+ */
1167
+ interface RateLimitConfig {
1168
+ /**
1169
+ * Maximum requests per minute.
1170
+ * Set based on your API tier. If not set, RPM limiting is disabled.
1171
+ */
1172
+ requestsPerMinute?: number;
1173
+ /**
1174
+ * Maximum tokens per minute (input + output combined).
1175
+ * Set based on your API tier. If not set, TPM limiting is disabled.
1176
+ */
1177
+ tokensPerMinute?: number;
1178
+ /**
1179
+ * Maximum tokens per day (optional).
1180
+ * Useful for Gemini free tier which has daily limits.
1181
+ * If not set, daily limiting is disabled.
1182
+ */
1183
+ tokensPerDay?: number;
1184
+ /**
1185
+ * Safety margin - start throttling at this percentage of limit.
1186
+ * A value of 0.9 means throttling starts at 90% of the limit.
1187
+ * Lower values provide more safety but may reduce throughput.
1188
+ * @default 0.9
1189
+ */
1190
+ safetyMargin?: number;
1191
+ /**
1192
+ * Whether proactive rate limiting is enabled.
1193
+ * @default true (when any limit is configured)
1194
+ */
1195
+ enabled?: boolean;
1196
+ }
1197
+ /**
1198
+ * Resolved rate limit configuration with all defaults applied.
1199
+ */
1200
+ interface ResolvedRateLimitConfig {
1201
+ requestsPerMinute?: number;
1202
+ tokensPerMinute?: number;
1203
+ tokensPerDay?: number;
1204
+ safetyMargin: number;
1205
+ enabled: boolean;
1206
+ }
1207
+ /**
1208
+ * Default rate limit configuration values.
1209
+ */
1210
+ declare const DEFAULT_RATE_LIMIT_CONFIG: Pick<ResolvedRateLimitConfig, "safetyMargin" | "enabled">;
1211
+ /**
1212
+ * Resolves a partial rate limit configuration by applying defaults.
1213
+ *
1214
+ * @param config - Partial configuration (optional)
1215
+ * @returns Fully resolved configuration
1216
+ */
1217
+ declare function resolveRateLimitConfig(config?: RateLimitConfig): ResolvedRateLimitConfig;
1218
+ /**
1219
+ * Information about a triggered rate limit.
1220
+ */
1221
+ interface TriggeredLimitInfo {
1222
+ /** Current usage value */
1223
+ current: number;
1224
+ /** Configured limit value */
1225
+ limit: number;
1226
+ /** Effective limit after safety margin (limit × safetyMargin) */
1227
+ effectiveLimit: number;
1228
+ }
1229
+ /**
1230
+ * Usage statistics from the rate limit tracker.
1231
+ */
1232
+ interface RateLimitStats {
1233
+ /** Current requests per minute */
1234
+ rpm: number;
1235
+ /** Current tokens per minute */
1236
+ tpm: number;
1237
+ /** Tokens used today (UTC) */
1238
+ dailyTokens: number;
1239
+ /** Whether any limit is currently being approached */
1240
+ isApproachingLimit: boolean;
1241
+ /** Delay required before next request (0 if none) */
1242
+ requiredDelayMs: number;
1243
+ /** Which limit(s) triggered throttling, if any (present when requiredDelayMs > 0) */
1244
+ triggeredBy?: {
1245
+ rpm?: TriggeredLimitInfo;
1246
+ tpm?: TriggeredLimitInfo;
1247
+ daily?: TriggeredLimitInfo;
1248
+ };
1249
+ }
1250
+ /**
1251
+ * Tracks API usage and calculates required delays for proactive rate limiting.
1252
+ *
1253
+ * Uses sliding windows to track requests and token usage, automatically
1254
+ * calculating delays needed to stay within configured limits.
1255
+ *
1256
+ * @example
1257
+ * ```typescript
1258
+ * const tracker = new RateLimitTracker({
1259
+ * requestsPerMinute: 60,
1260
+ * tokensPerMinute: 100000,
1261
+ * });
1262
+ *
1263
+ * // Before each request
1264
+ * const delay = tracker.getRequiredDelayMs();
1265
+ * if (delay > 0) {
1266
+ * await sleep(delay);
1267
+ * }
1268
+ *
1269
+ * // After each request
1270
+ * tracker.recordUsage(inputTokens, outputTokens);
1271
+ * ```
1272
+ */
1273
+ declare class RateLimitTracker {
1274
+ private config;
1275
+ /** Timestamps of requests in the current minute window */
1276
+ private requestTimestamps;
1277
+ /** Token usage entries in the current minute window */
1278
+ private tokenUsage;
1279
+ /** Daily token count */
1280
+ private dailyTokens;
1281
+ /** Date string (YYYY-MM-DD UTC) for daily reset tracking */
1282
+ private dailyResetDate;
1283
+ /** Count of pending reservations (for backward compatibility) */
1284
+ private pendingReservations;
1285
+ constructor(config?: RateLimitConfig);
1286
+ /**
1287
+ * Record a completed request with its token usage.
1288
+ *
1289
+ * If reserveRequest() was called before the LLM call (recommended for concurrent
1290
+ * scenarios), the request timestamp was already recorded. Otherwise, this method
1291
+ * will add it for backward compatibility.
1292
+ *
1293
+ * @param inputTokens - Number of input tokens used
1294
+ * @param outputTokens - Number of output tokens generated
1295
+ */
1296
+ recordUsage(inputTokens: number, outputTokens: number): void;
1297
+ /**
1298
+ * Calculate the delay needed before the next request.
1299
+ *
1300
+ * Returns 0 if no delay is needed, otherwise returns the number of
1301
+ * milliseconds to wait to stay within rate limits.
1302
+ *
1303
+ * @returns Delay in milliseconds (0 if none needed)
1304
+ */
1305
+ getRequiredDelayMs(): number;
1306
+ /**
1307
+ * Check if we're approaching any configured limits.
1308
+ *
1309
+ * @returns true if any limit is at or above the safety margin threshold
1310
+ */
1311
+ isApproachingLimit(): boolean;
1312
+ /**
1313
+ * Get current usage statistics.
1314
+ *
1315
+ * @returns Current usage stats for monitoring/logging
1316
+ */
1317
+ getUsageStats(): RateLimitStats;
1318
+ /**
1319
+ * Reset all tracking state.
1320
+ * Useful for testing or when switching API keys/tiers.
1321
+ */
1322
+ reset(): void;
1323
+ /**
1324
+ * Update configuration dynamically.
1325
+ * Useful when API tier changes or for testing.
1326
+ *
1327
+ * @param config - New configuration to apply
1328
+ */
1329
+ updateConfig(config: RateLimitConfig): void;
1330
+ /**
1331
+ * Reserve a request slot before making an LLM call.
1332
+ *
1333
+ * This is critical for concurrent subagents sharing a rate limiter.
1334
+ * Without reservation, multiple subagents checking getRequiredDelayMs()
1335
+ * simultaneously would all see zero usage and proceed, causing rate limit errors.
1336
+ *
1337
+ * Call this AFTER waiting for getRequiredDelayMs() but BEFORE making the LLM call.
1338
+ * The reservation ensures subsequent concurrent checks see the pending request.
1339
+ *
1340
+ * @example
1341
+ * ```typescript
1342
+ * // Proactive rate limiting with reservation
1343
+ * const delay = tracker.getRequiredDelayMs();
1344
+ * if (delay > 0) await sleep(delay);
1345
+ *
1346
+ * tracker.reserveRequest(); // Claim slot BEFORE making call
1347
+ * try {
1348
+ * const result = await llm.call();
1349
+ * tracker.recordUsage(result.inputTokens, result.outputTokens);
1350
+ * } catch (error) {
1351
+ * // Request already reserved; recordUsage updates token count
1352
+ * throw error;
1353
+ * }
1354
+ * ```
1355
+ */
1356
+ reserveRequest(): void;
1357
+ /**
1358
+ * Calculate delay needed based on RPM limit.
1359
+ */
1360
+ private calculateRpmDelay;
1361
+ /**
1362
+ * Calculate delay needed based on TPM limit.
1363
+ */
1364
+ private calculateTpmDelay;
1365
+ /**
1366
+ * Remove entries older than 1 minute from the sliding window.
1367
+ */
1368
+ private pruneOldEntries;
1369
+ /**
1370
+ * Check if the day has changed (UTC) and reset daily counters.
1371
+ */
1372
+ private checkDailyReset;
1373
+ /**
1374
+ * Get current date in YYYY-MM-DD format (UTC).
1375
+ */
1376
+ private getCurrentDateUTC;
1377
+ /**
1378
+ * Calculate milliseconds until midnight UTC.
1379
+ */
1380
+ private getTimeUntilMidnightUTC;
1381
+ }
1382
+
1141
1383
  /**
1142
1384
  * Model Catalog Types
1143
1385
  *
@@ -1457,255 +1699,8 @@ interface ResolvedCompactionConfig {
1457
1699
  }
1458
1700
 
1459
1701
  /**
1460
- * Proactive rate limiting for LLM API calls.
1461
- *
1462
- * Tracks request and token usage in sliding windows to prevent rate limit errors
1463
- * before they occur. Works in conjunction with reactive backoff (retry.ts) for
1464
- * comprehensive rate limit handling.
1465
- */
1466
- /**
1467
- * Configuration for proactive rate limiting.
1468
- *
1469
- * Set these values based on your API tier to prevent rate limit errors.
1470
- * When limits are approached, requests will be automatically delayed.
1471
- *
1472
- * @example
1473
- * ```typescript
1474
- * // Gemini free tier limits
1475
- * const agent = LLMist.createAgent()
1476
- * .withRateLimits({
1477
- * requestsPerMinute: 15,
1478
- * tokensPerMinute: 1_000_000,
1479
- * safetyMargin: 0.8,
1480
- * });
1481
- *
1482
- * // OpenAI Tier 1 limits
1483
- * const agent = LLMist.createAgent()
1484
- * .withRateLimits({
1485
- * requestsPerMinute: 500,
1486
- * tokensPerMinute: 200_000,
1487
- * });
1488
- * ```
1489
- */
1490
- interface RateLimitConfig {
1491
- /**
1492
- * Maximum requests per minute.
1493
- * Set based on your API tier. If not set, RPM limiting is disabled.
1494
- */
1495
- requestsPerMinute?: number;
1496
- /**
1497
- * Maximum tokens per minute (input + output combined).
1498
- * Set based on your API tier. If not set, TPM limiting is disabled.
1499
- */
1500
- tokensPerMinute?: number;
1501
- /**
1502
- * Maximum tokens per day (optional).
1503
- * Useful for Gemini free tier which has daily limits.
1504
- * If not set, daily limiting is disabled.
1505
- */
1506
- tokensPerDay?: number;
1507
- /**
1508
- * Safety margin - start throttling at this percentage of limit.
1509
- * A value of 0.9 means throttling starts at 90% of the limit.
1510
- * Lower values provide more safety but may reduce throughput.
1511
- * @default 0.9
1512
- */
1513
- safetyMargin?: number;
1514
- /**
1515
- * Whether proactive rate limiting is enabled.
1516
- * @default true (when any limit is configured)
1517
- */
1518
- enabled?: boolean;
1519
- }
1520
- /**
1521
- * Resolved rate limit configuration with all defaults applied.
1522
- */
1523
- interface ResolvedRateLimitConfig {
1524
- requestsPerMinute?: number;
1525
- tokensPerMinute?: number;
1526
- tokensPerDay?: number;
1527
- safetyMargin: number;
1528
- enabled: boolean;
1529
- }
1530
- /**
1531
- * Default rate limit configuration values.
1532
- */
1533
- declare const DEFAULT_RATE_LIMIT_CONFIG: Pick<ResolvedRateLimitConfig, "safetyMargin" | "enabled">;
1534
- /**
1535
- * Resolves a partial rate limit configuration by applying defaults.
1536
- *
1537
- * @param config - Partial configuration (optional)
1538
- * @returns Fully resolved configuration
1539
- */
1540
- declare function resolveRateLimitConfig(config?: RateLimitConfig): ResolvedRateLimitConfig;
1541
- /**
1542
- * Information about a triggered rate limit.
1543
- */
1544
- interface TriggeredLimitInfo {
1545
- /** Current usage value */
1546
- current: number;
1547
- /** Configured limit value */
1548
- limit: number;
1549
- /** Effective limit after safety margin (limit × safetyMargin) */
1550
- effectiveLimit: number;
1551
- }
1552
- /**
1553
- * Usage statistics from the rate limit tracker.
1554
- */
1555
- interface RateLimitStats {
1556
- /** Current requests per minute */
1557
- rpm: number;
1558
- /** Current tokens per minute */
1559
- tpm: number;
1560
- /** Tokens used today (UTC) */
1561
- dailyTokens: number;
1562
- /** Whether any limit is currently being approached */
1563
- isApproachingLimit: boolean;
1564
- /** Delay required before next request (0 if none) */
1565
- requiredDelayMs: number;
1566
- /** Which limit(s) triggered throttling, if any (present when requiredDelayMs > 0) */
1567
- triggeredBy?: {
1568
- rpm?: TriggeredLimitInfo;
1569
- tpm?: TriggeredLimitInfo;
1570
- daily?: TriggeredLimitInfo;
1571
- };
1572
- }
1573
- /**
1574
- * Tracks API usage and calculates required delays for proactive rate limiting.
1575
- *
1576
- * Uses sliding windows to track requests and token usage, automatically
1577
- * calculating delays needed to stay within configured limits.
1578
- *
1579
- * @example
1580
- * ```typescript
1581
- * const tracker = new RateLimitTracker({
1582
- * requestsPerMinute: 60,
1583
- * tokensPerMinute: 100000,
1584
- * });
1585
- *
1586
- * // Before each request
1587
- * const delay = tracker.getRequiredDelayMs();
1588
- * if (delay > 0) {
1589
- * await sleep(delay);
1590
- * }
1591
- *
1592
- * // After each request
1593
- * tracker.recordUsage(inputTokens, outputTokens);
1594
- * ```
1595
- */
1596
- declare class RateLimitTracker {
1597
- private config;
1598
- /** Timestamps of requests in the current minute window */
1599
- private requestTimestamps;
1600
- /** Token usage entries in the current minute window */
1601
- private tokenUsage;
1602
- /** Daily token count */
1603
- private dailyTokens;
1604
- /** Date string (YYYY-MM-DD UTC) for daily reset tracking */
1605
- private dailyResetDate;
1606
- /** Count of pending reservations (for backward compatibility) */
1607
- private pendingReservations;
1608
- constructor(config?: RateLimitConfig);
1609
- /**
1610
- * Record a completed request with its token usage.
1611
- *
1612
- * If reserveRequest() was called before the LLM call (recommended for concurrent
1613
- * scenarios), the request timestamp was already recorded. Otherwise, this method
1614
- * will add it for backward compatibility.
1615
- *
1616
- * @param inputTokens - Number of input tokens used
1617
- * @param outputTokens - Number of output tokens generated
1618
- */
1619
- recordUsage(inputTokens: number, outputTokens: number): void;
1620
- /**
1621
- * Calculate the delay needed before the next request.
1622
- *
1623
- * Returns 0 if no delay is needed, otherwise returns the number of
1624
- * milliseconds to wait to stay within rate limits.
1625
- *
1626
- * @returns Delay in milliseconds (0 if none needed)
1627
- */
1628
- getRequiredDelayMs(): number;
1629
- /**
1630
- * Check if we're approaching any configured limits.
1631
- *
1632
- * @returns true if any limit is at or above the safety margin threshold
1633
- */
1634
- isApproachingLimit(): boolean;
1635
- /**
1636
- * Get current usage statistics.
1637
- *
1638
- * @returns Current usage stats for monitoring/logging
1639
- */
1640
- getUsageStats(): RateLimitStats;
1641
- /**
1642
- * Reset all tracking state.
1643
- * Useful for testing or when switching API keys/tiers.
1644
- */
1645
- reset(): void;
1646
- /**
1647
- * Update configuration dynamically.
1648
- * Useful when API tier changes or for testing.
1649
- *
1650
- * @param config - New configuration to apply
1651
- */
1652
- updateConfig(config: RateLimitConfig): void;
1653
- /**
1654
- * Reserve a request slot before making an LLM call.
1655
- *
1656
- * This is critical for concurrent subagents sharing a rate limiter.
1657
- * Without reservation, multiple subagents checking getRequiredDelayMs()
1658
- * simultaneously would all see zero usage and proceed, causing rate limit errors.
1659
- *
1660
- * Call this AFTER waiting for getRequiredDelayMs() but BEFORE making the LLM call.
1661
- * The reservation ensures subsequent concurrent checks see the pending request.
1662
- *
1663
- * @example
1664
- * ```typescript
1665
- * // Proactive rate limiting with reservation
1666
- * const delay = tracker.getRequiredDelayMs();
1667
- * if (delay > 0) await sleep(delay);
1668
- *
1669
- * tracker.reserveRequest(); // Claim slot BEFORE making call
1670
- * try {
1671
- * const result = await llm.call();
1672
- * tracker.recordUsage(result.inputTokens, result.outputTokens);
1673
- * } catch (error) {
1674
- * // Request already reserved; recordUsage updates token count
1675
- * throw error;
1676
- * }
1677
- * ```
1678
- */
1679
- reserveRequest(): void;
1680
- /**
1681
- * Calculate delay needed based on RPM limit.
1682
- */
1683
- private calculateRpmDelay;
1684
- /**
1685
- * Calculate delay needed based on TPM limit.
1686
- */
1687
- private calculateTpmDelay;
1688
- /**
1689
- * Remove entries older than 1 minute from the sliding window.
1690
- */
1691
- private pruneOldEntries;
1692
- /**
1693
- * Check if the day has changed (UTC) and reset daily counters.
1694
- */
1695
- private checkDailyReset;
1696
- /**
1697
- * Get current date in YYYY-MM-DD format (UTC).
1698
- */
1699
- private getCurrentDateUTC;
1700
- /**
1701
- * Calculate milliseconds until midnight UTC.
1702
- */
1703
- private getTimeUntilMidnightUTC;
1704
- }
1705
-
1706
- /**
1707
- * Metadata present when an event originates from a subagent.
1708
- * Undefined for top-level agent events.
1702
+ * Metadata present when an event originates from a subagent.
1703
+ * Undefined for top-level agent events.
1709
1704
  *
1710
1705
  * When using subagent gadgets (like BrowseWeb), hook observers receive events
1711
1706
  * from both the main agent AND subagents. Check this context to distinguish.
@@ -1823,13 +1818,6 @@ interface ObserveGadgetCompleteContext {
1823
1818
  gadgetName: string;
1824
1819
  invocationId: string;
1825
1820
  parameters: Readonly<Record<string, unknown>>;
1826
- /**
1827
- * Original result before interceptors.
1828
- * @deprecated No longer populated. Use `finalResult` instead.
1829
- * With the unified event architecture, observers receive the final (post-interceptor)
1830
- * result from the ExecutionTree.
1831
- */
1832
- originalResult?: string;
1833
1821
  /** Final result after interceptors (the value stored in ExecutionTree) */
1834
1822
  finalResult?: string;
1835
1823
  error?: string;
@@ -2058,12 +2046,13 @@ interface Interceptors {
2058
2046
  */
2059
2047
  interceptGadgetParameters?: (parameters: Readonly<Record<string, unknown>>, context: GadgetParameterInterceptorContext) => Record<string, unknown>;
2060
2048
  /**
2061
- * Intercept and transform gadget results after execution.
2049
+ * Intercept and transform gadget results and error messages after execution.
2062
2050
  * This affects what gets sent back to the LLM and stored in history.
2051
+ * Called for both successful results (result.result) and errors (result.error).
2063
2052
  *
2064
- * @param result - The gadget result text
2053
+ * @param result - The gadget result or error text
2065
2054
  * @param context - Context information including parameters and execution time
2066
- * @returns Transformed result text (cannot be suppressed)
2055
+ * @returns Transformed text (cannot be suppressed)
2067
2056
  */
2068
2057
  interceptGadgetResult?: (result: string, context: GadgetResultInterceptorContext) => string;
2069
2058
  }
@@ -2941,377 +2930,168 @@ declare function parseRetryAfterHeader(value: string): number | null;
2941
2930
  declare function extractRetryAfterMs(error: Error): number | null;
2942
2931
 
2943
2932
  /**
2944
- * Example of gadget usage to help LLMs understand proper invocation.
2945
- *
2946
- * Examples are rendered alongside the schema in `getInstruction()` to provide
2947
- * concrete usage patterns for the LLM.
2933
+ * Subagent configuration types.
2948
2934
  *
2949
- * @template TParams - Inferred parameter type from Zod schema (defaults to Record<string, unknown>)
2935
+ * Simple config shapes passed to gadgets for subagent configuration.
2936
+ * No external dependencies — self-contained data shapes.
2950
2937
  *
2951
- * @example
2952
- * ```typescript
2953
- * const calculator = createGadget({
2954
- * schema: z.object({ a: z.number(), b: z.number() }),
2955
- * examples: [
2956
- * { params: { a: 5, b: 3 }, output: "8", comment: "Addition example" }
2957
- * ],
2958
- * // ...
2959
- * });
2960
- * ```
2938
+ * @module
2961
2939
  */
2962
- interface GadgetExample<TParams = Record<string, unknown>> {
2963
- /** Example parameter values (typed to match schema) */
2964
- params: TParams;
2965
- /** Optional expected output/result string */
2966
- output?: string;
2967
- /** Optional description explaining what this example demonstrates */
2968
- comment?: string;
2969
- }
2970
2940
  /**
2971
- * Supported media types for gadget output.
2972
- * Extensible via union - add new types as needed.
2941
+ * Parent agent configuration passed to gadgets.
2942
+ * Contains settings that subagents can inherit.
2973
2943
  */
2974
- type MediaKind = "image" | "audio" | "video" | "file";
2944
+ interface AgentContextConfig {
2945
+ /** Model identifier used by the parent agent */
2946
+ model: string;
2947
+ /** Temperature setting used by the parent agent */
2948
+ temperature?: number;
2949
+ }
2975
2950
  /**
2976
- * Type-specific metadata for media outputs.
2977
- * Extensible via index signature for future media types.
2951
+ * Configuration for a single subagent.
2952
+ * Can be defined globally in `[subagents.Name]` or per-profile in `[profile.subagents.Name]`.
2953
+ *
2954
+ * @example
2955
+ * ```toml
2956
+ * [subagents.BrowseWeb]
2957
+ * model = "inherit" # Use parent agent's model
2958
+ * maxIterations = 20
2959
+ * headless = true
2960
+ * ```
2978
2961
  */
2979
- interface MediaMetadata {
2980
- /** Width in pixels (images, video) */
2981
- width?: number;
2982
- /** Height in pixels (images, video) */
2983
- height?: number;
2984
- /** Duration in milliseconds (audio, video) */
2985
- durationMs?: number;
2986
- /** Allow additional metadata for future extensions */
2962
+ interface SubagentConfig {
2963
+ /**
2964
+ * Model to use for this subagent.
2965
+ * - "inherit": Use parent agent's model (default behavior)
2966
+ * - Any model ID: Use specific model (e.g., "sonnet", "haiku", "gpt-4o")
2967
+ */
2968
+ model?: string;
2969
+ /** Maximum iterations for the subagent loop */
2970
+ maxIterations?: number;
2971
+ /** Budget limit in USD for the subagent */
2972
+ budget?: number;
2973
+ /**
2974
+ * Timeout for the subagent gadget execution in milliseconds.
2975
+ * Overrides the gadget's hardcoded timeoutMs when set.
2976
+ * Set to 0 to disable timeout for this gadget.
2977
+ */
2978
+ timeoutMs?: number;
2979
+ /**
2980
+ * Maximum number of concurrent executions allowed for this gadget.
2981
+ * When the limit is reached, additional calls are queued and processed
2982
+ * as earlier executions complete (FIFO order).
2983
+ * Set to 0 or omit to allow unlimited concurrent executions (default).
2984
+ */
2985
+ maxConcurrent?: number;
2986
+ /** Additional subagent-specific options */
2987
2987
  [key: string]: unknown;
2988
2988
  }
2989
2989
  /**
2990
- * Media output from a gadget execution.
2991
- * Supports images, audio, video, and arbitrary files.
2990
+ * Map of subagent names to their configurations.
2991
+ */
2992
+ type SubagentConfigMap = Record<string, SubagentConfig>;
2993
+ /**
2994
+ * Gadget execution mode controlling how multiple gadgets are executed.
2995
+ *
2996
+ * - `'parallel'` (default): Gadgets without dependencies execute concurrently (fire-and-forget).
2997
+ * This maximizes throughput but gadgets may complete in any order.
2998
+ *
2999
+ * - `'sequential'`: Gadgets execute one at a time, each awaiting completion before the next starts.
3000
+ * Useful for:
3001
+ * - Gadgets with implicit ordering dependencies (e.g., file operations)
3002
+ * - Debugging and tracing execution flow
3003
+ * - Resource-constrained environments
3004
+ * - Ensuring deterministic execution order
3005
+ *
3006
+ * Note: Explicit `dependsOn` relationships are always respected regardless of mode.
3007
+ * Sequential mode effectively enforces a global `maxConcurrent: 1` for all gadgets.
2992
3008
  *
2993
3009
  * @example
2994
3010
  * ```typescript
2995
- * // Image output
2996
- * const imageOutput: GadgetMediaOutput = {
2997
- * kind: "image",
2998
- * data: base64EncodedPng,
2999
- * mimeType: "image/png",
3000
- * description: "Screenshot of webpage",
3001
- * metadata: { width: 1920, height: 1080 }
3002
- * };
3011
+ * const agent = LLMist.createAgent()
3012
+ * .withModel("sonnet")
3013
+ * .withGadgets(FileReader, FileWriter)
3014
+ * .withGadgetExecutionMode('sequential') // Execute one at a time
3015
+ * .ask("Process files in order");
3003
3016
  * ```
3004
3017
  */
3005
- interface GadgetMediaOutput {
3006
- /** Type of media (discriminator for type-specific handling) */
3007
- kind: MediaKind;
3008
- /** Base64-encoded media data */
3009
- data: string;
3010
- /** Full MIME type (e.g., "image/png", "audio/mp3", "video/mp4") */
3011
- mimeType: string;
3012
- /** Human-readable description of the media */
3013
- description?: string;
3014
- /** Type-specific metadata */
3015
- metadata?: MediaMetadata;
3016
- /** Optional filename to use when saving (if not provided, auto-generated) */
3017
- fileName?: string;
3018
- }
3018
+ type GadgetExecutionMode = "parallel" | "sequential";
3019
+
3019
3020
  /**
3020
- * Stored media item with metadata and file path.
3021
- *
3022
- * Created by MediaStore when a gadget returns media outputs.
3023
- * Contains the abstract ID, file path, and metadata for display.
3021
+ * Image generation namespace with automatic cost reporting.
3024
3022
  */
3025
- interface StoredMedia {
3026
- /** Unique ID for this media item (e.g., "media_a1b2c3") */
3027
- id: string;
3028
- /** Type of media */
3029
- kind: MediaKind;
3030
- /** Actual file path on disk (internal use) */
3031
- path: string;
3032
- /** MIME type */
3033
- mimeType: string;
3034
- /** File size in bytes */
3035
- sizeBytes: number;
3036
- /** Human-readable description */
3037
- description?: string;
3038
- /** Type-specific metadata */
3039
- metadata?: MediaMetadata;
3040
- /** Name of the gadget that created this media */
3041
- gadgetName: string;
3042
- /** When the media was stored */
3043
- createdAt: Date;
3023
+ interface CostReportingImageNamespace {
3024
+ /**
3025
+ * Generate images from a text prompt.
3026
+ * Costs are automatically reported to the execution context.
3027
+ */
3028
+ generate(options: ImageGenerationOptions): Promise<ImageGenerationResult>;
3044
3029
  }
3045
- interface GadgetExecutionResult {
3046
- gadgetName: string;
3047
- invocationId: string;
3048
- parameters: Record<string, unknown>;
3049
- result?: string;
3050
- error?: string;
3051
- executionTimeMs: number;
3052
- breaksLoop?: boolean;
3053
- /** Cost of gadget execution in USD. Defaults to 0 if not provided by gadget. */
3054
- cost?: number;
3055
- /** Media outputs from the gadget (images, audio, video, files) */
3056
- media?: GadgetMediaOutput[];
3057
- /** Abstract IDs for media outputs (e.g., ["media_a1b2c3"]) */
3058
- mediaIds?: string[];
3059
- /** Stored media with paths (for CLI display) */
3060
- storedMedia?: StoredMedia[];
3030
+ /**
3031
+ * Speech generation namespace with automatic cost reporting.
3032
+ */
3033
+ interface CostReportingSpeechNamespace {
3034
+ /**
3035
+ * Generate speech audio from text.
3036
+ * Costs are automatically reported to the execution context.
3037
+ */
3038
+ generate(options: SpeechGenerationOptions): Promise<SpeechGenerationResult>;
3061
3039
  }
3062
3040
  /**
3063
- * Result returned by gadget execute() method.
3064
- * Can be a simple string or an object with result and optional cost.
3041
+ * LLMist client interface for use within gadgets.
3042
+ *
3043
+ * Provides LLM completion methods that automatically report costs
3044
+ * via the execution context. All LLM calls made through this client
3045
+ * will have their costs tracked and included in the gadget's total cost.
3065
3046
  *
3066
3047
  * @example
3067
3048
  * ```typescript
3068
- * // Simple string return (free gadget)
3069
- * execute: () => "result"
3070
- *
3071
- * // Object return with cost
3072
- * execute: () => ({ result: "data", cost: 0.001 })
3049
+ * execute: async ({ text }, ctx) => {
3050
+ * // LLM costs are automatically reported
3051
+ * const summary = await ctx.llmist.complete('Summarize: ' + text, {
3052
+ * model: 'haiku',
3053
+ * });
3054
+ * return summary;
3055
+ * }
3073
3056
  * ```
3074
3057
  */
3075
- interface GadgetExecuteResult {
3076
- /** The execution result as a string */
3077
- result: string;
3078
- /** Optional cost in USD (e.g., 0.001 for $0.001) */
3079
- cost?: number;
3058
+ interface CostReportingLLMist {
3059
+ /**
3060
+ * Quick completion - returns final text response.
3061
+ * Costs are automatically reported to the execution context.
3062
+ */
3063
+ complete(prompt: string, options?: TextGenerationOptions): Promise<string>;
3064
+ /**
3065
+ * Quick streaming - returns async generator of text chunks.
3066
+ * Costs are automatically reported when the stream completes.
3067
+ */
3068
+ streamText(prompt: string, options?: TextGenerationOptions): AsyncGenerator<string>;
3069
+ /**
3070
+ * Low-level stream access for full control.
3071
+ * Costs are automatically reported based on usage metadata in chunks.
3072
+ */
3073
+ stream(options: LLMGenerationOptions): LLMStream;
3074
+ /**
3075
+ * Access to model registry for cost estimation.
3076
+ */
3077
+ readonly modelRegistry: ModelRegistry;
3078
+ /**
3079
+ * Image generation with automatic cost reporting.
3080
+ * Costs are reported based on model and generation parameters.
3081
+ */
3082
+ readonly image: CostReportingImageNamespace;
3083
+ /**
3084
+ * Speech generation with automatic cost reporting.
3085
+ * Costs are reported based on input length and model pricing.
3086
+ */
3087
+ readonly speech: CostReportingSpeechNamespace;
3080
3088
  }
3081
3089
  /**
3082
- * Extended result type with media support.
3083
- * Use this when gadget returns images, audio, video, or files.
3084
- *
3085
- * @example
3086
- * ```typescript
3087
- * // Return with image
3088
- * execute: () => ({
3089
- * result: "Screenshot captured",
3090
- * media: [{
3091
- * kind: "image",
3092
- * data: base64EncodedPng,
3093
- * mimeType: "image/png",
3094
- * description: "Screenshot"
3095
- * }],
3096
- * cost: 0.001
3097
- * })
3098
- * ```
3099
- */
3100
- interface GadgetExecuteResultWithMedia {
3101
- /** The execution result as a string */
3102
- result: string;
3103
- /** Media outputs (images, audio, video, files) */
3104
- media?: GadgetMediaOutput[];
3105
- /** Optional cost in USD (e.g., 0.001 for $0.001) */
3106
- cost?: number;
3107
- }
3108
- /**
3109
- * Union type for backwards-compatible execute() return type.
3110
- * Gadgets can return:
3111
- * - string (legacy, cost = 0)
3112
- * - GadgetExecuteResult (result + optional cost)
3113
- * - GadgetExecuteResultWithMedia (result + optional media + optional cost)
3114
- */
3115
- type GadgetExecuteReturn = string | GadgetExecuteResult | GadgetExecuteResultWithMedia;
3116
- interface ParsedGadgetCall {
3117
- gadgetName: string;
3118
- invocationId: string;
3119
- parametersRaw: string;
3120
- parameters?: Record<string, unknown>;
3121
- parseError?: string;
3122
- /** List of invocation IDs this gadget depends on. Empty array if no dependencies. */
3123
- dependencies: string[];
3124
- }
3125
-
3126
- /** Event emitted when a gadget is skipped due to a failed dependency */
3127
- interface GadgetSkippedEvent {
3128
- type: "gadget_skipped";
3129
- gadgetName: string;
3130
- invocationId: string;
3131
- parameters: Record<string, unknown>;
3132
- /** The invocation ID of the dependency that failed */
3133
- failedDependency: string;
3134
- /** The error message from the failed dependency */
3135
- failedDependencyError: string;
3136
- }
3137
- /**
3138
- * Event emitted when stream processing completes, containing metadata.
3139
- * This allows the async generator to "return" metadata while still yielding events.
3140
- */
3141
- interface StreamCompletionEvent {
3142
- type: "stream_complete";
3143
- /** The reason the LLM stopped generating (e.g., "stop", "tool_use") */
3144
- finishReason: string | null;
3145
- /** Token usage statistics from the LLM call */
3146
- usage?: TokenUsage;
3147
- /** Raw response text from the LLM */
3148
- rawResponse: string;
3149
- /** Final message after all interceptors applied */
3150
- finalMessage: string;
3151
- /** Whether any gadgets were executed during this iteration */
3152
- didExecuteGadgets: boolean;
3153
- /** Whether to break the agent loop (e.g., TaskComplete was called) */
3154
- shouldBreakLoop: boolean;
3155
- /** Accumulated thinking/reasoning content from reasoning models */
3156
- thinkingContent?: string;
3157
- }
3158
- type StreamEvent = {
3159
- type: "text";
3160
- content: string;
3161
- } | {
3162
- type: "thinking";
3163
- content: string;
3164
- thinkingType: "thinking" | "redacted";
3165
- } | {
3166
- type: "gadget_call";
3167
- call: ParsedGadgetCall;
3168
- } | {
3169
- type: "gadget_result";
3170
- result: GadgetExecutionResult;
3171
- } | GadgetSkippedEvent | {
3172
- type: "human_input_required";
3173
- question: string;
3174
- gadgetName: string;
3175
- invocationId: string;
3176
- } | {
3177
- type: "compaction";
3178
- event: CompactionEvent;
3179
- } | {
3180
- type: "llm_response_end";
3181
- finishReason: string | null;
3182
- usage?: TokenUsage;
3183
- } | StreamCompletionEvent;
3184
-
3185
- type TextOnlyHandler = TextOnlyStrategy | TextOnlyGadgetConfig | TextOnlyCustomHandler;
3186
- /**
3187
- * Simple strategies for common cases
3188
- * - 'terminate': End the loop (default behavior)
3189
- * - 'acknowledge': Continue to next iteration
3190
- * - 'wait_for_input': Request human input
3191
- */
3192
- type TextOnlyStrategy = "terminate" | "acknowledge" | "wait_for_input";
3193
- /**
3194
- * Configuration for triggering a gadget when receiving text-only response
3195
- */
3196
- interface TextOnlyGadgetConfig {
3197
- type: "gadget";
3198
- name: string;
3199
- /**
3200
- * Optional function to map text to gadget parameters.
3201
- * If not provided, text will be passed as { text: string }
3202
- */
3203
- parameterMapping?: (text: string) => Record<string, unknown>;
3204
- }
3205
- /**
3206
- * Custom handler for complex text-only response scenarios
3207
- */
3208
- interface TextOnlyCustomHandler {
3209
- type: "custom";
3210
- handler: (context: TextOnlyContext) => Promise<TextOnlyAction> | TextOnlyAction;
3211
- }
3212
- /**
3213
- * Context provided to custom text-only handlers
3214
- */
3215
- interface TextOnlyContext {
3216
- /** The complete text response from the LLM */
3217
- text: string;
3218
- /** Current iteration number */
3219
- iteration: number;
3220
- /** Full conversation history */
3221
- conversation: LLMMessage[];
3222
- /** Logger instance */
3223
- logger: Logger<ILogObj>;
3224
- }
3225
- /**
3226
- * Actions that can be returned by text-only handlers
3227
- */
3228
- type TextOnlyAction = {
3229
- action: "continue";
3230
- } | {
3231
- action: "terminate";
3232
- } | {
3233
- action: "wait_for_input";
3234
- question?: string;
3235
- } | {
3236
- action: "trigger_gadget";
3237
- name: string;
3238
- parameters: Record<string, unknown>;
3239
- };
3240
- /**
3241
- * LLMist client interface for use within gadgets.
3242
- *
3243
- * Provides LLM completion methods that automatically report costs
3244
- * via the execution context. All LLM calls made through this client
3245
- * will have their costs tracked and included in the gadget's total cost.
3246
- *
3247
- * @example
3248
- * ```typescript
3249
- * execute: async ({ text }, ctx) => {
3250
- * // LLM costs are automatically reported
3251
- * const summary = await ctx.llmist.complete('Summarize: ' + text, {
3252
- * model: 'haiku',
3253
- * });
3254
- * return summary;
3255
- * }
3256
- * ```
3257
- */
3258
- /**
3259
- * Image generation namespace with automatic cost reporting.
3260
- */
3261
- interface CostReportingImageNamespace {
3262
- /**
3263
- * Generate images from a text prompt.
3264
- * Costs are automatically reported to the execution context.
3265
- */
3266
- generate(options: ImageGenerationOptions): Promise<ImageGenerationResult>;
3267
- }
3268
- /**
3269
- * Speech generation namespace with automatic cost reporting.
3270
- */
3271
- interface CostReportingSpeechNamespace {
3272
- /**
3273
- * Generate speech audio from text.
3274
- * Costs are automatically reported to the execution context.
3275
- */
3276
- generate(options: SpeechGenerationOptions): Promise<SpeechGenerationResult>;
3277
- }
3278
- interface CostReportingLLMist {
3279
- /**
3280
- * Quick completion - returns final text response.
3281
- * Costs are automatically reported to the execution context.
3282
- */
3283
- complete(prompt: string, options?: TextGenerationOptions): Promise<string>;
3284
- /**
3285
- * Quick streaming - returns async generator of text chunks.
3286
- * Costs are automatically reported when the stream completes.
3287
- */
3288
- streamText(prompt: string, options?: TextGenerationOptions): AsyncGenerator<string>;
3289
- /**
3290
- * Low-level stream access for full control.
3291
- * Costs are automatically reported based on usage metadata in chunks.
3292
- */
3293
- stream(options: LLMGenerationOptions): LLMStream;
3294
- /**
3295
- * Access to model registry for cost estimation.
3296
- */
3297
- readonly modelRegistry: ModelRegistry;
3298
- /**
3299
- * Image generation with automatic cost reporting.
3300
- * Costs are reported based on model and generation parameters.
3301
- */
3302
- readonly image: CostReportingImageNamespace;
3303
- /**
3304
- * Speech generation with automatic cost reporting.
3305
- * Costs are reported based on input length and model pricing.
3306
- */
3307
- readonly speech: CostReportingSpeechNamespace;
3308
- }
3309
- /**
3310
- * Execution context provided to gadgets during execution.
3311
- *
3312
- * Contains utilities for cost reporting and LLM access.
3313
- * This parameter is optional for backwards compatibility -
3314
- * existing gadgets without the context parameter continue to work.
3090
+ * Execution context provided to gadgets during execution.
3091
+ *
3092
+ * Contains utilities for cost reporting and LLM access.
3093
+ * This parameter is optional for backwards compatibility -
3094
+ * existing gadgets without the context parameter continue to work.
3315
3095
  *
3316
3096
  * @example
3317
3097
  * ```typescript
@@ -3758,1885 +3538,1308 @@ interface HostExports {
3758
3538
  /** Zod schema builder */
3759
3539
  z: typeof zod.z;
3760
3540
  }
3541
+
3761
3542
  /**
3762
- * Parent agent configuration passed to gadgets.
3763
- * Contains settings that subagents can inherit.
3543
+ * Media output types for gadgets returning images, audio, video, or files.
3544
+ *
3545
+ * This module contains pure data shapes with zero cross-module dependencies.
3546
+ * Imported by execution result types, stream event types, and execution context types.
3547
+ *
3548
+ * @module
3764
3549
  */
3765
- interface AgentContextConfig {
3766
- /** Model identifier used by the parent agent */
3767
- model: string;
3768
- /** Temperature setting used by the parent agent */
3769
- temperature?: number;
3550
+ /**
3551
+ * Supported media types for gadget output.
3552
+ * Extensible via union - add new types as needed.
3553
+ */
3554
+ type MediaKind = "image" | "audio" | "video" | "file";
3555
+ /**
3556
+ * Type-specific metadata for media outputs.
3557
+ * Extensible via index signature for future media types.
3558
+ */
3559
+ interface MediaMetadata {
3560
+ /** Width in pixels (images, video) */
3561
+ width?: number;
3562
+ /** Height in pixels (images, video) */
3563
+ height?: number;
3564
+ /** Duration in milliseconds (audio, video) */
3565
+ durationMs?: number;
3566
+ /** Allow additional metadata for future extensions */
3567
+ [key: string]: unknown;
3770
3568
  }
3771
3569
  /**
3772
- * Configuration for a single subagent.
3773
- * Can be defined globally in `[subagents.Name]` or per-profile in `[profile.subagents.Name]`.
3570
+ * Media output from a gadget execution.
3571
+ * Supports images, audio, video, and arbitrary files.
3774
3572
  *
3775
3573
  * @example
3776
- * ```toml
3777
- * [subagents.BrowseWeb]
3778
- * model = "inherit" # Use parent agent's model
3779
- * maxIterations = 20
3780
- * headless = true
3574
+ * ```typescript
3575
+ * // Image output
3576
+ * const imageOutput: GadgetMediaOutput = {
3577
+ * kind: "image",
3578
+ * data: base64EncodedPng,
3579
+ * mimeType: "image/png",
3580
+ * description: "Screenshot of webpage",
3581
+ * metadata: { width: 1920, height: 1080 }
3582
+ * };
3781
3583
  * ```
3782
3584
  */
3783
- interface SubagentConfig {
3784
- /**
3785
- * Model to use for this subagent.
3786
- * - "inherit": Use parent agent's model (default behavior)
3787
- * - Any model ID: Use specific model (e.g., "sonnet", "haiku", "gpt-4o")
3788
- */
3789
- model?: string;
3790
- /** Maximum iterations for the subagent loop */
3791
- maxIterations?: number;
3792
- /** Budget limit in USD for the subagent */
3793
- budget?: number;
3794
- /**
3795
- * Timeout for the subagent gadget execution in milliseconds.
3796
- * Overrides the gadget's hardcoded timeoutMs when set.
3797
- * Set to 0 to disable timeout for this gadget.
3798
- */
3799
- timeoutMs?: number;
3800
- /**
3801
- * Maximum number of concurrent executions allowed for this gadget.
3802
- * When the limit is reached, additional calls are queued and processed
3803
- * as earlier executions complete (FIFO order).
3804
- * Set to 0 or omit to allow unlimited concurrent executions (default).
3805
- */
3806
- maxConcurrent?: number;
3807
- /** Additional subagent-specific options */
3808
- [key: string]: unknown;
3809
- }
3810
- /**
3811
- * Map of subagent names to their configurations.
3812
- */
3813
- type SubagentConfigMap = Record<string, SubagentConfig>;
3814
- /**
3815
- * Gadget execution mode controlling how multiple gadgets are executed.
3816
- *
3817
- * - `'parallel'` (default): Gadgets without dependencies execute concurrently (fire-and-forget).
3818
- * This maximizes throughput but gadgets may complete in any order.
3819
- *
3820
- * - `'sequential'`: Gadgets execute one at a time, each awaiting completion before the next starts.
3821
- * Useful for:
3822
- * - Gadgets with implicit ordering dependencies (e.g., file operations)
3823
- * - Debugging and tracing execution flow
3824
- * - Resource-constrained environments
3825
- * - Ensuring deterministic execution order
3826
- *
3827
- * Note: Explicit `dependsOn` relationships are always respected regardless of mode.
3828
- * Sequential mode effectively enforces a global `maxConcurrent: 1` for all gadgets.
3829
- *
3830
- * @example
3831
- * ```typescript
3832
- * const agent = LLMist.createAgent()
3833
- * .withModel("sonnet")
3834
- * .withGadgets(FileReader, FileWriter)
3835
- * .withGadgetExecutionMode('sequential') // Execute one at a time
3836
- * .ask("Process files in order");
3837
- * ```
3838
- */
3839
- type GadgetExecutionMode = "parallel" | "sequential";
3840
-
3841
- /**
3842
- * Abstract base class for gadgets. Most users should use the `Gadget()` factory
3843
- * or `createGadget()` function instead, as they provide better type safety
3844
- * and simpler APIs.
3845
- *
3846
- * Extend this class directly only when you need advanced control over gadget behavior.
3847
- */
3848
- declare abstract class AbstractGadget {
3849
- /**
3850
- * The name of the gadget. Used for identification when LLM calls it.
3851
- * If not provided, defaults to the class name.
3852
- */
3853
- name?: string;
3854
- /**
3855
- * Human-readable description of what the gadget does.
3856
- */
3857
- abstract description: string;
3858
- /**
3859
- * Optional Zod schema describing the expected input payload. When provided,
3860
- * it will be validated before execution and transformed into a JSON Schema
3861
- * representation that is surfaced to the LLM as part of the instructions.
3862
- */
3863
- parameterSchema?: ZodTypeAny;
3864
- /**
3865
- * Optional timeout in milliseconds for gadget execution.
3866
- * If execution exceeds this timeout, a TimeoutException will be thrown.
3867
- * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
3868
- * Set to 0 or undefined to disable timeout for this gadget.
3869
- */
3870
- timeoutMs?: number;
3871
- /**
3872
- * Optional usage examples to help LLMs understand proper invocation.
3873
- * Examples are rendered in getInstruction() alongside the schema.
3874
- *
3875
- * Note: Uses broader `unknown` type to allow typed examples from subclasses
3876
- * while maintaining runtime compatibility.
3877
- */
3878
- examples?: GadgetExample<unknown>[];
3879
- /**
3880
- * Maximum number of concurrent executions allowed for this gadget.
3881
- * Use this to prevent race conditions in gadgets that modify shared state.
3882
- *
3883
- * - `1` = Sequential execution (only one instance runs at a time)
3884
- * - `0` or `undefined` = Unlimited concurrency (default)
3885
- * - `N > 1` = At most N concurrent executions
3886
- *
3887
- * This property sets a safety floor: external configuration (SubagentConfig)
3888
- * can only make concurrency MORE restrictive, never less. For example, if
3889
- * a gadget declares `maxConcurrent: 1`, external config cannot override it
3890
- * to allow parallel execution.
3891
- *
3892
- * @example
3893
- * ```typescript
3894
- * // File writer that must run sequentially to avoid race conditions
3895
- * class WriteFile extends Gadget({
3896
- * description: 'Writes content to a file',
3897
- * schema: z.object({ path: z.string(), content: z.string() }),
3898
- * maxConcurrent: 1, // Sequential - prevents race conditions
3899
- * }) {
3900
- * execute(params: this['params']) { ... }
3901
- * }
3902
- * ```
3903
- */
3904
- maxConcurrent?: number;
3905
- /**
3906
- * If true, this gadget must execute alone — no other gadgets in the same
3907
- * LLM response can run in parallel. When an exclusive gadget arrives and
3908
- * other gadgets are already in-flight, it is deferred until they complete.
3909
- *
3910
- * Use for gadgets that terminate the agent loop (e.g., Finish), where
3911
- * sibling tool results must be visible to the LLM before the loop ends.
3912
- *
3913
- * This is a safety floor: external config cannot weaken it.
3914
- */
3915
- exclusive?: boolean;
3916
- /**
3917
- * Execute the gadget with the given parameters.
3918
- * Can be synchronous or asynchronous.
3919
- *
3920
- * @param params - Parameters passed from the LLM
3921
- * @param ctx - Optional execution context for cost reporting and LLM access
3922
- * @returns Result as a string, or an object with result and optional cost
3923
- *
3924
- * @example
3925
- * ```typescript
3926
- * // Simple string return (free gadget)
3927
- * execute(params) {
3928
- * return "result";
3929
- * }
3930
- *
3931
- * // Object return with cost tracking
3932
- * execute(params) {
3933
- * return { result: "data", cost: 0.001 };
3934
- * }
3935
- *
3936
- * // Using context for callback-based cost reporting
3937
- * execute(params, ctx) {
3938
- * ctx.reportCost(0.001);
3939
- * return "result";
3940
- * }
3941
- *
3942
- * // Using wrapped LLMist for automatic cost tracking
3943
- * async execute(params, ctx) {
3944
- * const summary = await ctx.llmist.complete('Summarize: ' + params.text);
3945
- * return summary;
3946
- * }
3947
- * ```
3948
- */
3949
- abstract execute(params: Record<string, unknown>, ctx?: ExecutionContext): GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
3950
- /**
3951
- * Throws an AbortException if the execution has been aborted.
3952
- *
3953
- * Call this at key checkpoints in long-running gadgets to allow early exit
3954
- * when the gadget has been cancelled (e.g., due to timeout). This enables
3955
- * resource cleanup and prevents unnecessary work after cancellation.
3956
- *
3957
- * @param ctx - The execution context containing the abort signal
3958
- * @throws AbortException if ctx.signal.aborted is true
3959
- *
3960
- * @example
3961
- * ```typescript
3962
- * class DataProcessor extends Gadget({
3963
- * description: 'Processes data in multiple steps',
3964
- * schema: z.object({ items: z.array(z.string()) }),
3965
- * }) {
3966
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
3967
- * const results: string[] = [];
3968
- *
3969
- * for (const item of params.items) {
3970
- * // Check before each expensive operation
3971
- * this.throwIfAborted(ctx);
3972
- *
3973
- * results.push(await this.processItem(item));
3974
- * }
3975
- *
3976
- * return results.join(', ');
3977
- * }
3978
- * }
3979
- * ```
3980
- */
3981
- throwIfAborted(ctx?: ExecutionContext): void;
3982
- /**
3983
- * Register a cleanup function to run when execution is aborted (timeout or cancellation).
3984
- * The cleanup function is called immediately if the signal is already aborted.
3985
- * Errors thrown by the cleanup function are silently ignored.
3986
- *
3987
- * Use this to clean up resources like browser instances, database connections,
3988
- * or child processes when the gadget is cancelled due to timeout.
3989
- *
3990
- * @param ctx - The execution context containing the abort signal
3991
- * @param cleanup - Function to run on abort (can be sync or async)
3992
- *
3993
- * @example
3994
- * ```typescript
3995
- * class BrowserGadget extends Gadget({
3996
- * description: 'Fetches web page content',
3997
- * schema: z.object({ url: z.string() }),
3998
- * }) {
3999
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4000
- * const browser = await chromium.launch();
4001
- * this.onAbort(ctx, () => browser.close());
4002
- *
4003
- * const page = await browser.newPage();
4004
- * this.onAbort(ctx, () => page.close());
4005
- *
4006
- * await page.goto(params.url);
4007
- * const content = await page.content();
4008
- *
4009
- * await browser.close();
4010
- * return content;
4011
- * }
4012
- * }
4013
- * ```
4014
- */
4015
- onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
4016
- /**
4017
- * Create an AbortController linked to the execution context's signal.
4018
- * When the parent signal aborts, the returned controller also aborts with the same reason.
4019
- *
4020
- * Useful for passing abort signals to child operations like fetch() while still
4021
- * being able to abort them independently if needed.
4022
- *
4023
- * @param ctx - The execution context containing the parent abort signal
4024
- * @returns A new AbortController linked to the parent signal
4025
- *
4026
- * @example
4027
- * ```typescript
4028
- * class FetchGadget extends Gadget({
4029
- * description: 'Fetches data from URL',
4030
- * schema: z.object({ url: z.string() }),
4031
- * }) {
4032
- * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4033
- * const controller = this.createLinkedAbortController(ctx);
4034
- *
4035
- * // fetch() will automatically abort when parent times out
4036
- * const response = await fetch(params.url, { signal: controller.signal });
4037
- * return response.text();
4038
- * }
4039
- * }
4040
- * ```
4041
- */
4042
- createLinkedAbortController(ctx?: ExecutionContext): AbortController;
4043
- /**
4044
- * Auto-generated instruction text for the LLM.
4045
- * Combines name, description, and parameter schema into a formatted instruction.
4046
- * @deprecated Use getInstruction() instead
4047
- */
4048
- get instruction(): string;
4049
- /**
4050
- * Generate instruction text for the LLM.
4051
- * Combines name, description, and parameter schema into a formatted instruction.
4052
- *
4053
- * @param optionsOrArgPrefix - Optional custom prefixes for examples, or just argPrefix string for backwards compatibility
4054
- * @returns Formatted instruction string
4055
- */
4056
- getInstruction(optionsOrArgPrefix?: string | {
4057
- argPrefix?: string;
4058
- startPrefix?: string;
4059
- endPrefix?: string;
4060
- }): string;
4061
- }
4062
-
4063
- /**
4064
- * Context provided to prompt template functions for rendering dynamic content.
4065
- */
4066
- interface PromptContext {
4067
- /** Custom gadget start prefix */
4068
- startPrefix: string;
4069
- /** Custom gadget end prefix */
4070
- endPrefix: string;
4071
- /** Custom argument prefix for block format */
4072
- argPrefix: string;
4073
- /** Number of gadgets being registered */
4074
- gadgetCount: number;
4075
- /** Names of all gadgets */
4076
- gadgetNames: string[];
4077
- }
4078
- /**
4079
- * Context provided to hint template functions for rendering dynamic hints.
4080
- */
4081
- interface HintContext {
4082
- /** Current iteration (1-based for readability) */
4083
- iteration: number;
4084
- /** Maximum iterations allowed */
4085
- maxIterations: number;
4086
- /** Iterations remaining (maxIterations - iteration) */
4087
- remaining: number;
4088
- /** Number of gadget calls in the current response */
4089
- gadgetCallCount?: number;
4090
- }
4091
- /**
4092
- * Template that can be either a static string or a function that renders based on context.
4093
- */
4094
- type PromptTemplate = string | ((context: PromptContext) => string);
4095
- /**
4096
- * Template for hints that can be either a static string or a function that renders based on hint context.
4097
- */
4098
- type HintTemplate = string | ((context: HintContext) => string);
4099
- /**
4100
- * Configuration for customizing all prompts used internally by llmist.
4101
- *
4102
- * Each field can be either a string (static text) or a function that receives
4103
- * context and returns a string (for dynamic content).
4104
- *
4105
- * @example
4106
- * ```typescript
4107
- * const customConfig: PromptTemplateConfig = {
4108
- * mainInstruction: "USE ONLY THE GADGET MARKERS BELOW:",
4109
- * criticalUsage: "Important: Follow the exact format shown.",
4110
- * rules: (ctx) => [
4111
- * "Always use the markers to invoke gadgets",
4112
- * "Never use function calling",
4113
- * `You have ${ctx.gadgetCount} gadgets available`
4114
- * ]
4115
- * };
4116
- * ```
4117
- */
4118
- interface PromptTemplateConfig {
4119
- /**
4120
- * Main instruction block that appears at the start of the gadget system prompt.
4121
- * Default emphasizes using text markers instead of function calling.
4122
- */
4123
- mainInstruction?: PromptTemplate;
4124
- /**
4125
- * Critical usage instruction that appears in the usage section.
4126
- * Default emphasizes the exact format requirement.
4127
- */
4128
- criticalUsage?: PromptTemplate;
4129
- /**
4130
- * Format description for the block parameter format.
4131
- * Default uses the configured argPrefix dynamically.
4132
- */
4133
- formatDescription?: PromptTemplate;
4134
- /**
4135
- * Rules that appear in the rules section.
4136
- * Can be an array of strings or a function that returns an array.
4137
- * Default includes rules about not using function calling.
4138
- */
4139
- rules?: PromptTemplate | string[] | ((context: PromptContext) => string[]);
4140
- /**
4141
- * Custom examples to show in the examples section.
4142
- * If provided, replaces the default examples entirely.
4143
- * Should be a function that returns formatted example strings.
4144
- */
4145
- customExamples?: (context: PromptContext) => string;
4146
- /**
4147
- * Hint shown when LLM uses only one gadget per response.
4148
- * Encourages parallel gadget usage for efficiency.
4149
- */
4150
- parallelGadgetsHint?: HintTemplate;
4151
- /**
4152
- * Template for iteration progress hint.
4153
- * Informs the LLM about remaining iterations to help plan work.
4154
- *
4155
- * When using a string template, supports placeholders:
4156
- * - {iteration}: Current iteration (1-based)
4157
- * - {maxIterations}: Maximum iterations allowed
4158
- * - {remaining}: Iterations remaining
4159
- */
4160
- iterationProgressHint?: HintTemplate;
4161
- }
4162
- /**
4163
- * Default hint templates used by llmist.
4164
- */
4165
- declare const DEFAULT_HINTS: {
4166
- readonly parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.";
4167
- readonly iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly.";
4168
- };
4169
- /**
4170
- * Default prompt templates used by llmist.
4171
- */
4172
- declare const DEFAULT_PROMPTS: Required<Omit<PromptTemplateConfig, "rules" | "customExamples" | "parallelGadgetsHint" | "iterationProgressHint"> & {
4173
- rules: (context: PromptContext) => string[];
4174
- customExamples: null;
4175
- }>;
4176
- /**
4177
- * Resolve a prompt template to a string using the given context.
4178
- */
4179
- declare function resolvePromptTemplate(template: PromptTemplate | undefined, defaultValue: PromptTemplate, context: PromptContext): string;
4180
- /**
4181
- * Resolve rules template to an array of strings.
4182
- */
4183
- declare function resolveRulesTemplate(rules: PromptTemplateConfig["rules"] | undefined, context: PromptContext): string[];
4184
- /**
4185
- * Resolve a hint template to a string using the given context.
4186
- * Supports both function templates and string templates with placeholders.
4187
- *
4188
- * @param template - The hint template to resolve
4189
- * @param defaultValue - Default value if template is undefined
4190
- * @param context - Context for rendering the template
4191
- * @returns The resolved hint string
4192
- */
4193
- declare function resolveHintTemplate(template: HintTemplate | undefined, defaultValue: string, context: HintContext): string;
4194
-
4195
- type MessageRole = "system" | "user" | "assistant";
4196
- /**
4197
- * Message content can be a simple string (text only) or an array of content parts (multimodal).
4198
- * Using a string is simpler for text-only messages, while arrays support images and audio.
4199
- */
4200
- type MessageContent = string | ContentPart[];
4201
- interface LLMMessage {
4202
- role: MessageRole;
4203
- content: MessageContent;
4204
- name?: string;
4205
- metadata?: Record<string, unknown>;
4206
- }
4207
- /**
4208
- * Normalize message content to an array of content parts.
4209
- * Converts string content to a single text part.
4210
- *
4211
- * @param content - Message content (string or ContentPart[])
4212
- * @returns Array of content parts
4213
- */
4214
- declare function normalizeMessageContent(content: MessageContent): ContentPart[];
4215
- /**
4216
- * Extract text from message content.
4217
- * Concatenates all text parts in the content.
4218
- *
4219
- * @param content - Message content (string or ContentPart[])
4220
- * @returns Combined text from all text parts
4221
- */
4222
- declare function extractMessageText(content: MessageContent): string;
4223
- declare class LLMMessageBuilder {
4224
- private readonly messages;
4225
- private startPrefix;
4226
- private endPrefix;
4227
- private argPrefix;
4228
- private promptConfig;
4229
- constructor(promptConfig?: PromptTemplateConfig);
4230
- /**
4231
- * Set custom prefixes for gadget markers.
4232
- * Used to configure history builder to match system prompt markers.
4233
- */
4234
- withPrefixes(startPrefix: string, endPrefix: string, argPrefix?: string): this;
4235
- addSystem(content: string, metadata?: Record<string, unknown>): this;
4236
- addGadgets(gadgets: AbstractGadget[], options?: {
4237
- startPrefix?: string;
4238
- endPrefix?: string;
4239
- argPrefix?: string;
4240
- }): this;
4241
- private buildGadgetsSection;
4242
- private buildUsageSection;
4243
- private buildExamplesSection;
4244
- private buildRulesSection;
4245
- /**
4246
- * Add a user message.
4247
- * Content can be a string (text only) or an array of content parts (multimodal).
4248
- *
4249
- * @param content - Message content
4250
- * @param metadata - Optional metadata
4251
- *
4252
- * @example
4253
- * ```typescript
4254
- * // Text only
4255
- * builder.addUser("Hello!");
4256
- *
4257
- * // Multimodal
4258
- * builder.addUser([
4259
- * text("What's in this image?"),
4260
- * imageFromBuffer(imageData),
4261
- * ]);
4262
- * ```
4263
- */
4264
- addUser(content: MessageContent, metadata?: Record<string, unknown>): this;
4265
- addAssistant(content: string, metadata?: Record<string, unknown>): this;
4266
- /**
4267
- * Add a user message with an image attachment.
4268
- *
4269
- * @param textContent - Text prompt
4270
- * @param imageData - Image data (Buffer, Uint8Array, or base64 string)
4271
- * @param mimeType - Optional MIME type (auto-detected if not provided)
4272
- *
4273
- * @example
4274
- * ```typescript
4275
- * builder.addUserWithImage(
4276
- * "What's in this image?",
4277
- * await fs.readFile("photo.jpg"),
4278
- * "image/jpeg" // Optional - auto-detected
4279
- * );
4280
- * ```
4281
- */
4282
- addUserWithImage(textContent: string, imageData: Buffer | Uint8Array | string, mimeType?: ImageMimeType): this;
4283
- /**
4284
- * Add a user message with an image URL (OpenAI only).
4285
- *
4286
- * @param textContent - Text prompt
4287
- * @param imageUrl - URL to the image
4288
- *
4289
- * @example
4290
- * ```typescript
4291
- * builder.addUserWithImageUrl(
4292
- * "What's in this image?",
4293
- * "https://example.com/image.jpg"
4294
- * );
4295
- * ```
4296
- */
4297
- addUserWithImageUrl(textContent: string, imageUrl: string): this;
4298
- /**
4299
- * Add a user message with an audio attachment (Gemini only).
4300
- *
4301
- * @param textContent - Text prompt
4302
- * @param audioData - Audio data (Buffer, Uint8Array, or base64 string)
4303
- * @param mimeType - Optional MIME type (auto-detected if not provided)
4304
- *
4305
- * @example
4306
- * ```typescript
4307
- * builder.addUserWithAudio(
4308
- * "Transcribe this audio",
4309
- * await fs.readFile("recording.mp3"),
4310
- * "audio/mp3" // Optional - auto-detected
4311
- * );
4312
- * ```
4313
- */
4314
- addUserWithAudio(textContent: string, audioData: Buffer | Uint8Array | string, mimeType?: AudioMimeType): this;
4315
- /**
4316
- * Add a user message with multiple content parts.
4317
- * Provides full flexibility for complex multimodal messages.
4318
- *
4319
- * @param parts - Array of content parts
4320
- *
4321
- * @example
4322
- * ```typescript
4323
- * builder.addUserMultimodal([
4324
- * text("Compare these images:"),
4325
- * imageFromBuffer(image1),
4326
- * imageFromBuffer(image2),
4327
- * ]);
4328
- * ```
4329
- */
4330
- addUserMultimodal(parts: ContentPart[]): this;
4331
- /**
4332
- * Record a gadget execution result in the message history.
4333
- * Creates an assistant message with the gadget invocation and a user message with the result.
4334
- *
4335
- * The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
4336
- *
4337
- * @param gadget - Name of the gadget that was executed
4338
- * @param parameters - Parameters that were passed to the gadget
4339
- * @param result - Text result from the gadget execution
4340
- * @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
4341
- * @param media - Optional media outputs from the gadget
4342
- * @param mediaIds - Optional IDs for the media outputs
4343
- * @param storedMedia - Optional stored media info including file paths
4344
- */
4345
- addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): this;
4346
- /**
4347
- * Format parameters as Block format with JSON Pointer paths.
4348
- * Uses the configured argPrefix for consistency with system prompt.
4349
- */
4350
- private formatBlockParameters;
4351
- build(): LLMMessage[];
4352
- }
4353
-
4354
- /**
4355
- * Provider-agnostic reasoning effort level.
4356
- *
4357
- * Maps to provider-specific values:
4358
- * - **OpenAI**: "none"|"low"|"medium"|"high"|"xhigh"
4359
- * - **Anthropic**: budget_tokens (1024–32768)
4360
- * - **Gemini 3**: thinkingLevel "minimal"|"low"|"medium"|"high"
4361
- * - **Gemini 2.5**: thinkingBudget (0–24576)
4362
- * - **DeepSeek**: binary (enabled/disabled)
4363
- */
4364
- type ReasoningEffort = "none" | "low" | "medium" | "high" | "maximum";
4365
- /**
4366
- * Configuration for reasoning/thinking mode on supported models.
4367
- *
4368
- * When `enabled` is true, the provider will be instructed to use
4369
- * extended reasoning before generating its response.
4370
- */
4371
- interface ReasoningConfig {
4372
- /** Whether reasoning is enabled */
4373
- enabled: boolean;
4374
- /** Reasoning effort level (default: "medium") */
4375
- effort?: ReasoningEffort;
4376
- /** Explicit token budget for thinking (Anthropic/Gemini 2.5, overrides effort) */
4377
- budgetTokens?: number;
4378
- /** Whether to surface thinking content in the stream (default: true) */
4379
- includeThinking?: boolean;
4380
- /** Enable interleaved thinking for multi-turn tool use (Anthropic only) */
4381
- interleaved?: boolean;
4382
- }
4383
- /**
4384
- * A chunk of thinking/reasoning content from a reasoning model.
4385
- *
4386
- * Emitted during streaming when a reasoning model produces thinking output.
4387
- * The `type` field distinguishes actual thinking from redacted content
4388
- * (e.g., Anthropic may redact thinking in certain scenarios).
4389
- */
4390
- interface ThinkingChunk {
4391
- /** The thinking text content */
4392
- content: string;
4393
- /** Whether this is actual thinking or redacted content */
4394
- type: "thinking" | "redacted";
4395
- /** Verification signature (Anthropic/Gemini) */
4396
- signature?: string;
4397
- }
4398
- /**
4399
- * What content to include in the cache.
4400
- *
4401
- * - `"system"`: Cache only system prompt (lowest cost, highest reuse)
4402
- * - `"conversation"`: Cache system prompt + all conversation turns except the latest user message
4403
- */
4404
- type CachingScope = "system" | "conversation";
4405
- /**
4406
- * Configuration for context caching across providers.
4407
- *
4408
- * Context caching allows reusing previously computed key-value pairs across
4409
- * requests, reducing latency and cost for repeated context.
4410
- *
4411
- * Provider behavior:
4412
- * - **Anthropic**: Automatic ephemeral caching via `cache_control` markers (always-on by default).
4413
- * Use `enabled: false` to disable markers and opt out of caching.
4414
- * - **Gemini**: Explicit cache lifecycle via `caches.create()`. Requires `scope` and `ttl`.
4415
- * - **OpenAI**: Server-side automatic caching (no-op, but respects the unified API).
4416
- */
4417
- interface CachingConfig {
4418
- /** Whether context caching is enabled */
4419
- enabled: boolean;
4420
- /**
4421
- * What to cache (Gemini only, default: "conversation").
4422
- * - `"system"`: Cache only system-derived messages
4423
- * - `"conversation"`: Cache system + all turns except the latest user message
4424
- */
4425
- scope?: CachingScope;
4426
- /** TTL for cache entries (Gemini only, format: "3600s", default: "3600s", min: "300s") */
4427
- ttl?: string;
4428
- /** Minimum token count for content to be eligible for caching (Gemini default: 32768) */
4429
- minTokenThreshold?: number;
4430
- }
4431
- interface LLMGenerationOptions {
4432
- model: string;
4433
- messages: LLMMessage[];
4434
- maxTokens?: number;
4435
- temperature?: number;
4436
- topP?: number;
4437
- stopSequences?: string[];
4438
- responseFormat?: "text";
4439
- metadata?: Record<string, unknown>;
4440
- extra?: Record<string, unknown>;
4441
- /**
4442
- * Optional abort signal for cancelling the request mid-flight.
4443
- *
4444
- * When the signal is aborted, the provider will attempt to cancel
4445
- * the underlying HTTP request and the stream will terminate with
4446
- * an abort error. Use `isAbortError()` from `@/core/errors` to
4447
- * detect cancellation in error handling.
4448
- *
4449
- * @example
4450
- * ```typescript
4451
- * const controller = new AbortController();
4452
- *
4453
- * const stream = client.stream({
4454
- * model: "claude-3-5-sonnet-20241022",
4455
- * messages: [{ role: "user", content: "Tell me a long story" }],
4456
- * signal: controller.signal,
4457
- * });
4458
- *
4459
- * // Cancel after 5 seconds
4460
- * setTimeout(() => controller.abort(), 5000);
4461
- *
4462
- * try {
4463
- * for await (const chunk of stream) {
4464
- * process.stdout.write(chunk.text);
4465
- * }
4466
- * } catch (error) {
4467
- * if (isAbortError(error)) {
4468
- * console.log("\nRequest was cancelled");
4469
- * } else {
4470
- * throw error;
4471
- * }
4472
- * }
4473
- * ```
4474
- */
4475
- signal?: AbortSignal;
4476
- /** Reasoning/thinking configuration for reasoning-capable models */
4477
- reasoning?: ReasoningConfig;
4478
- /** Context caching configuration for supported providers */
4479
- caching?: CachingConfig;
4480
- }
4481
- interface TokenUsage {
4482
- inputTokens: number;
4483
- outputTokens: number;
4484
- totalTokens: number;
4485
- /** Number of input tokens served from cache (subset of inputTokens) */
4486
- cachedInputTokens?: number;
4487
- /** Number of input tokens written to cache (subset of inputTokens, Anthropic only) */
4488
- cacheCreationInputTokens?: number;
4489
- /** Number of reasoning/thinking tokens used (subset of outputTokens) */
4490
- reasoningTokens?: number;
4491
- }
4492
- interface LLMStreamChunk {
4493
- text: string;
4494
- /**
4495
- * Indicates that the provider has finished producing output and includes the reason if available.
4496
- */
4497
- finishReason?: string | null;
4498
- /**
4499
- * Token usage information, typically available in the final chunk when the stream completes.
4500
- */
4501
- usage?: TokenUsage;
4502
- /**
4503
- * Provider specific payload emitted at the same time as the text chunk. This is useful for debugging and tests.
4504
- */
4505
- rawEvent?: unknown;
4506
- /** Thinking/reasoning content from reasoning models */
4507
- thinking?: ThinkingChunk;
4508
- }
4509
- interface LLMStream extends AsyncIterable<LLMStreamChunk> {
4510
- }
4511
- type ProviderIdentifier = string;
4512
- interface ModelDescriptor {
4513
- provider: string;
4514
- name: string;
4515
- }
4516
- declare class ModelIdentifierParser {
4517
- private readonly defaultProvider;
4518
- constructor(defaultProvider?: string);
4519
- parse(identifier: string): ModelDescriptor;
4520
- }
4521
-
4522
- type GadgetClass = new (...args: unknown[]) => AbstractGadget;
4523
- type GadgetOrClass = AbstractGadget | GadgetClass;
4524
- declare class GadgetRegistry {
4525
- private readonly gadgets;
4526
- /**
4527
- * Creates a registry from an array of gadget classes or instances,
4528
- * or an object mapping names to gadgets.
4529
- *
4530
- * @param gadgets - Array of gadgets/classes or object with custom names
4531
- * @returns New GadgetRegistry with all gadgets registered
4532
- *
4533
- * @example
4534
- * ```typescript
4535
- * // From array of classes
4536
- * const registry = GadgetRegistry.from([Calculator, Weather]);
4537
- *
4538
- * // From array of instances
4539
- * const registry = GadgetRegistry.from([new Calculator(), new Weather()]);
4540
- *
4541
- * // From object with custom names
4542
- * const registry = GadgetRegistry.from({
4543
- * calc: Calculator,
4544
- * weather: new Weather({ apiKey: "..." })
4545
- * });
4546
- * ```
4547
- */
4548
- static from(gadgets: GadgetOrClass[] | Record<string, GadgetOrClass>): GadgetRegistry;
4549
- /**
4550
- * Registers multiple gadgets at once from an array.
4551
- *
4552
- * @param gadgets - Array of gadget instances or classes
4553
- * @returns This registry for chaining
4554
- *
4555
- * @example
4556
- * ```typescript
4557
- * registry.registerMany([Calculator, Weather, Email]);
4558
- * registry.registerMany([new Calculator(), new Weather()]);
4559
- * ```
4560
- */
4561
- registerMany(gadgets: GadgetOrClass[]): this;
4562
- register(name: string, gadget: AbstractGadget): void;
4563
- registerByClass(gadget: AbstractGadget): void;
4564
- get(name: string): AbstractGadget | undefined;
4565
- has(name: string): boolean;
4566
- getNames(): string[];
4567
- getAll(): AbstractGadget[];
4568
- unregister(name: string): boolean;
4569
- clear(): void;
3585
+ interface GadgetMediaOutput {
3586
+ /** Type of media (discriminator for type-specific handling) */
3587
+ kind: MediaKind;
3588
+ /** Base64-encoded media data */
3589
+ data: string;
3590
+ /** Full MIME type (e.g., "image/png", "audio/mp3", "video/mp4") */
3591
+ mimeType: string;
3592
+ /** Human-readable description of the media */
3593
+ description?: string;
3594
+ /** Type-specific metadata */
3595
+ metadata?: MediaMetadata;
3596
+ /** Optional filename to use when saving (if not provided, auto-generated) */
3597
+ fileName?: string;
4570
3598
  }
4571
-
4572
3599
  /**
4573
- * Event handler sugar for cleaner event processing.
3600
+ * Stored media item with metadata and file path.
4574
3601
  *
4575
- * Instead of verbose if/else chains, use named handlers
4576
- * for each event type.
3602
+ * Created by MediaStore when a gadget returns media outputs.
3603
+ * Contains the abstract ID, file path, and metadata for display.
3604
+ */
3605
+ interface StoredMedia {
3606
+ /** Unique ID for this media item (e.g., "media_a1b2c3") */
3607
+ id: string;
3608
+ /** Type of media */
3609
+ kind: MediaKind;
3610
+ /** Actual file path on disk (internal use) */
3611
+ path: string;
3612
+ /** MIME type */
3613
+ mimeType: string;
3614
+ /** File size in bytes */
3615
+ sizeBytes: number;
3616
+ /** Human-readable description */
3617
+ description?: string;
3618
+ /** Type-specific metadata */
3619
+ metadata?: MediaMetadata;
3620
+ /** Name of the gadget that created this media */
3621
+ gadgetName: string;
3622
+ /** When the media was stored */
3623
+ createdAt: Date;
3624
+ }
3625
+ /**
3626
+ * Example of gadget usage to help LLMs understand proper invocation.
3627
+ *
3628
+ * Examples are rendered alongside the schema in `getInstruction()` to provide
3629
+ * concrete usage patterns for the LLM.
3630
+ *
3631
+ * @template TParams - Inferred parameter type from Zod schema (defaults to Record<string, unknown>)
4577
3632
  *
4578
3633
  * @example
4579
3634
  * ```typescript
4580
- * await agent.runWith({
4581
- * onText: (content) => console.log("LLM:", content),
4582
- * onGadgetResult: (result) => console.log("Result:", result.result),
3635
+ * const calculator = createGadget({
3636
+ * schema: z.object({ a: z.number(), b: z.number() }),
3637
+ * examples: [
3638
+ * { params: { a: 5, b: 3 }, output: "8", comment: "Addition example" }
3639
+ * ],
3640
+ * // ...
4583
3641
  * });
4584
3642
  * ```
4585
3643
  */
3644
+ interface GadgetExample<TParams = Record<string, unknown>> {
3645
+ /** Example parameter values (typed to match schema) */
3646
+ params: TParams;
3647
+ /** Optional expected output/result string */
3648
+ output?: string;
3649
+ /** Optional description explaining what this example demonstrates */
3650
+ comment?: string;
3651
+ }
4586
3652
 
4587
3653
  /**
4588
- * Named event handlers for different event types.
3654
+ * Execution result types for gadget calls.
3655
+ *
3656
+ * Contains result types returned by gadget `execute()` methods and the
3657
+ * internal result type used after execution completes.
3658
+ *
3659
+ * @module
4589
3660
  */
4590
- interface EventHandlers {
4591
- /** Called when text is generated by the LLM */
4592
- onText?: (content: string) => void | Promise<void>;
4593
- /** Called when a gadget is about to be executed */
4594
- onGadgetCall?: (call: {
4595
- gadgetName: string;
4596
- invocationId: string;
4597
- parameters?: Record<string, unknown>;
4598
- parametersRaw: string;
4599
- dependencies: string[];
4600
- }) => void | Promise<void>;
4601
- /** Called when a gadget execution completes */
4602
- onGadgetResult?: (result: {
4603
- gadgetName: string;
4604
- invocationId: string;
4605
- result?: string;
4606
- error?: string;
4607
- parameters: Record<string, unknown>;
4608
- }) => void | Promise<void>;
4609
- /** Called when human input is required */
4610
- onHumanInputRequired?: (data: {
4611
- question: string;
4612
- gadgetName: string;
4613
- }) => void | Promise<void>;
4614
- /** Called for any other event type */
4615
- onOther?: (event: StreamEvent) => void | Promise<void>;
3661
+
3662
+ interface GadgetExecutionResult {
3663
+ gadgetName: string;
3664
+ invocationId: string;
3665
+ parameters: Record<string, unknown>;
3666
+ result?: string;
3667
+ error?: string;
3668
+ executionTimeMs: number;
3669
+ breaksLoop?: boolean;
3670
+ /** Cost of gadget execution in USD. Defaults to 0 if not provided by gadget. */
3671
+ cost?: number;
3672
+ /** Media outputs from the gadget (images, audio, video, files) */
3673
+ media?: GadgetMediaOutput[];
3674
+ /** Abstract IDs for media outputs (e.g., ["media_a1b2c3"]) */
3675
+ mediaIds?: string[];
3676
+ /** Stored media with paths (for CLI display) */
3677
+ storedMedia?: StoredMedia[];
4616
3678
  }
4617
3679
  /**
4618
- * Helper to run an agent with named event handlers.
4619
- *
4620
- * @param agentGenerator - Agent's run() async generator
4621
- * @param handlers - Named event handlers
3680
+ * Result returned by gadget execute() method.
3681
+ * Can be a simple string or an object with result and optional cost.
4622
3682
  *
4623
3683
  * @example
4624
3684
  * ```typescript
4625
- * await runWithHandlers(agent.run(), {
4626
- * onText: (text) => console.log("LLM:", text),
4627
- * onGadgetResult: (result) => console.log("Result:", result.result),
4628
- * });
3685
+ * // Simple string return (free gadget)
3686
+ * execute: () => "result"
3687
+ *
3688
+ * // Object return with cost
3689
+ * execute: () => ({ result: "data", cost: 0.001 })
4629
3690
  * ```
4630
3691
  */
4631
- declare function runWithHandlers(agentGenerator: AsyncGenerator<StreamEvent>, handlers: EventHandlers): Promise<void>;
3692
+ interface GadgetExecuteResult {
3693
+ /** The execution result as a string */
3694
+ result: string;
3695
+ /** Optional cost in USD (e.g., 0.001 for $0.001) */
3696
+ cost?: number;
3697
+ }
4632
3698
  /**
4633
- * Helper to collect events by type.
4634
- *
4635
- * @param agentGenerator - Agent's run() async generator
4636
- * @param collect - Object specifying which event types to collect
4637
- * @returns Object with collected events
3699
+ * Extended result type with media support.
3700
+ * Use this when gadget returns images, audio, video, or files.
4638
3701
  *
4639
3702
  * @example
4640
3703
  * ```typescript
4641
- * const { text, gadgetResults } = await collectEvents(agent.run(), {
4642
- * text: true,
4643
- * gadgetResults: true,
4644
- * });
4645
- *
4646
- * console.log("Full response:", text.join(""));
4647
- * console.log("Gadget calls:", gadgetResults.length);
3704
+ * // Return with image
3705
+ * execute: () => ({
3706
+ * result: "Screenshot captured",
3707
+ * media: [{
3708
+ * kind: "image",
3709
+ * data: base64EncodedPng,
3710
+ * mimeType: "image/png",
3711
+ * description: "Screenshot"
3712
+ * }],
3713
+ * cost: 0.001
3714
+ * })
4648
3715
  * ```
4649
3716
  */
4650
- declare function collectEvents(agentGenerator: AsyncGenerator<StreamEvent>, collect: {
4651
- text?: boolean;
4652
- gadgetCalls?: boolean;
4653
- gadgetResults?: boolean;
4654
- }): Promise<{
4655
- text: string[];
4656
- gadgetCalls: Array<{
4657
- gadgetName: string;
4658
- parameters: Record<string, unknown>;
4659
- }>;
4660
- gadgetResults: Array<{
4661
- gadgetName: string;
4662
- result?: string;
4663
- error?: string;
4664
- parameters: Record<string, unknown>;
4665
- }>;
4666
- }>;
3717
+ interface GadgetExecuteResultWithMedia {
3718
+ /** The execution result as a string */
3719
+ result: string;
3720
+ /** Media outputs (images, audio, video, files) */
3721
+ media?: GadgetMediaOutput[];
3722
+ /** Optional cost in USD (e.g., 0.001 for $0.001) */
3723
+ cost?: number;
3724
+ }
3725
+ /**
3726
+ * Union type for backwards-compatible execute() return type.
3727
+ * Gadgets can return:
3728
+ * - string (legacy, cost = 0)
3729
+ * - GadgetExecuteResult (result + optional cost)
3730
+ * - GadgetExecuteResultWithMedia (result + optional media + optional cost)
3731
+ */
3732
+ type GadgetExecuteReturn = string | GadgetExecuteResult | GadgetExecuteResultWithMedia;
3733
+ interface ParsedGadgetCall {
3734
+ gadgetName: string;
3735
+ invocationId: string;
3736
+ parametersRaw: string;
3737
+ parameters?: Record<string, unknown>;
3738
+ parseError?: string;
3739
+ /** List of invocation IDs this gadget depends on. Empty array if no dependencies. */
3740
+ dependencies: string[];
3741
+ }
3742
+
3743
+ /**
3744
+ * Stream event types emitted during agent execution.
3745
+ *
3746
+ * Contains all discriminated union members for `StreamEvent`, plus
3747
+ * `StreamCompletionEvent` and `GadgetSkippedEvent`.
3748
+ *
3749
+ * @module
3750
+ */
3751
+
3752
+ /** Event emitted when a gadget is skipped due to a failed dependency */
3753
+ interface GadgetSkippedEvent {
3754
+ type: "gadget_skipped";
3755
+ gadgetName: string;
3756
+ invocationId: string;
3757
+ parameters: Record<string, unknown>;
3758
+ /** The invocation ID of the dependency that failed */
3759
+ failedDependency: string;
3760
+ /** The error message from the failed dependency */
3761
+ failedDependencyError: string;
3762
+ }
3763
+ /**
3764
+ * Event emitted when stream processing completes, containing metadata.
3765
+ * This allows the async generator to "return" metadata while still yielding events.
3766
+ */
3767
+ interface StreamCompletionEvent {
3768
+ type: "stream_complete";
3769
+ /** The reason the LLM stopped generating (e.g., "stop", "tool_use") */
3770
+ finishReason: string | null;
3771
+ /** Token usage statistics from the LLM call */
3772
+ usage?: TokenUsage;
3773
+ /** Raw response text from the LLM */
3774
+ rawResponse: string;
3775
+ /** Final message after all interceptors applied */
3776
+ finalMessage: string;
3777
+ /** Whether any gadgets were executed during this iteration */
3778
+ didExecuteGadgets: boolean;
3779
+ /** Whether to break the agent loop (e.g., TaskComplete was called) */
3780
+ shouldBreakLoop: boolean;
3781
+ /** Accumulated thinking/reasoning content from reasoning models */
3782
+ thinkingContent?: string;
3783
+ }
3784
+ type StreamEvent = {
3785
+ type: "text";
3786
+ content: string;
3787
+ } | {
3788
+ type: "thinking";
3789
+ content: string;
3790
+ thinkingType: "thinking" | "redacted";
3791
+ } | {
3792
+ type: "gadget_call";
3793
+ call: ParsedGadgetCall;
3794
+ } | {
3795
+ type: "gadget_result";
3796
+ result: GadgetExecutionResult;
3797
+ } | GadgetSkippedEvent | {
3798
+ type: "human_input_required";
3799
+ question: string;
3800
+ gadgetName: string;
3801
+ invocationId: string;
3802
+ } | {
3803
+ type: "compaction";
3804
+ event: CompactionEvent;
3805
+ } | {
3806
+ type: "llm_response_end";
3807
+ finishReason: string | null;
3808
+ usage?: TokenUsage;
3809
+ } | StreamCompletionEvent;
3810
+
4667
3811
  /**
4668
- * Helper to collect only text from an agent run.
3812
+ * Text-only response handler types.
4669
3813
  *
4670
- * @param agentGenerator - Agent's run() async generator
4671
- * @returns Combined text response
3814
+ * Defines the handler configuration for when the LLM returns a text-only response
3815
+ * (no gadget calls). Supports simple strategies, gadget triggers, and custom handlers.
4672
3816
  *
4673
- * @example
4674
- * ```typescript
4675
- * const response = await collectText(agent.run());
4676
- * console.log(response);
4677
- * ```
3817
+ * @module
4678
3818
  */
4679
- declare function collectText(agentGenerator: AsyncGenerator<StreamEvent>): Promise<string>;
4680
3819
 
3820
+ type TextOnlyHandler = TextOnlyStrategy | TextOnlyGadgetConfig | TextOnlyCustomHandler;
4681
3821
  /**
4682
- * Fluent builder for creating agents with delightful DX.
4683
- *
4684
- * @example
4685
- * ```typescript
4686
- * const agent = await LLMist.createAgent()
4687
- * .withModel("sonnet")
4688
- * .withSystem("You are a helpful assistant")
4689
- * .withGadgets(Calculator, Weather)
4690
- * .withMaxIterations(10)
4691
- * .ask("What's the weather in Paris?");
4692
- *
4693
- * for await (const event of agent.run()) {
4694
- * // process events
4695
- * }
4696
- * ```
3822
+ * Simple strategies for common cases
3823
+ * - 'terminate': End the loop (default behavior)
3824
+ * - 'acknowledge': Continue to next iteration
3825
+ * - 'wait_for_input': Request human input
4697
3826
  */
4698
-
3827
+ type TextOnlyStrategy = "terminate" | "acknowledge" | "wait_for_input";
4699
3828
  /**
4700
- * Message for conversation history.
4701
- * User messages can be text (string) or multimodal (ContentPart[]).
3829
+ * Configuration for triggering a gadget when receiving text-only response
4702
3830
  */
4703
- type HistoryMessage = {
4704
- user: string | ContentPart[];
4705
- } | {
4706
- assistant: string;
4707
- } | {
4708
- system: string;
4709
- };
3831
+ interface TextOnlyGadgetConfig {
3832
+ type: "gadget";
3833
+ name: string;
3834
+ /**
3835
+ * Optional function to map text to gadget parameters.
3836
+ * If not provided, text will be passed as { text: string }
3837
+ */
3838
+ parameterMapping?: (text: string) => Record<string, unknown>;
3839
+ }
4710
3840
  /**
4711
- * Context available to trailing message functions.
4712
- * Provides iteration information for dynamic message generation.
3841
+ * Custom handler for complex text-only response scenarios
4713
3842
  */
4714
- type TrailingMessageContext = Pick<LLMCallControllerContext, "iteration" | "maxIterations" | "budget" | "totalCost">;
3843
+ interface TextOnlyCustomHandler {
3844
+ type: "custom";
3845
+ handler: (context: TextOnlyContext) => Promise<TextOnlyAction> | TextOnlyAction;
3846
+ }
4715
3847
  /**
4716
- * Trailing message can be a static string or a function that generates the message.
4717
- * The function receives context about the current iteration.
3848
+ * Context provided to custom text-only handlers
4718
3849
  */
4719
- type TrailingMessage = string | ((ctx: TrailingMessageContext) => string);
3850
+ interface TextOnlyContext {
3851
+ /** The complete text response from the LLM */
3852
+ text: string;
3853
+ /** Current iteration number */
3854
+ iteration: number;
3855
+ /** Full conversation history */
3856
+ conversation: LLMMessage[];
3857
+ /** Logger instance */
3858
+ logger: Logger<ILogObj>;
3859
+ }
4720
3860
  /**
4721
- * Fluent builder for creating agents.
3861
+ * Actions that can be returned by text-only handlers
3862
+ */
3863
+ type TextOnlyAction = {
3864
+ action: "continue";
3865
+ } | {
3866
+ action: "terminate";
3867
+ } | {
3868
+ action: "wait_for_input";
3869
+ question?: string;
3870
+ } | {
3871
+ action: "trigger_gadget";
3872
+ name: string;
3873
+ parameters: Record<string, unknown>;
3874
+ };
3875
+
3876
+ /**
3877
+ * Abstract base class for gadgets. Most users should use the `Gadget()` factory
3878
+ * or `createGadget()` function instead, as they provide better type safety
3879
+ * and simpler APIs.
4722
3880
  *
4723
- * Provides a chainable API for configuring and creating agents,
4724
- * making the code more expressive and easier to read.
3881
+ * Extend this class directly only when you need advanced control over gadget behavior.
4725
3882
  */
4726
- declare class AgentBuilder {
4727
- private client?;
4728
- private model?;
4729
- private systemPrompt?;
4730
- private temperature?;
4731
- private maxIterations?;
4732
- private budget?;
4733
- private logger?;
4734
- private hooks?;
4735
- private promptConfig?;
4736
- private gadgets;
4737
- private initialMessages;
4738
- private requestHumanInput?;
4739
- private gadgetStartPrefix?;
4740
- private gadgetEndPrefix?;
4741
- private gadgetArgPrefix?;
4742
- private textOnlyHandler?;
4743
- private textWithGadgetsHandler?;
4744
- private defaultGadgetTimeoutMs?;
4745
- private gadgetExecutionMode?;
4746
- private maxGadgetsPerResponse?;
4747
- private gadgetOutputLimit?;
4748
- private gadgetOutputLimitPercent?;
4749
- private compactionConfig?;
4750
- private retryConfig?;
4751
- private rateLimitConfig?;
4752
- private signal?;
4753
- private trailingMessage?;
4754
- private subagentConfig?;
4755
- private parentContext?;
4756
- private parentObservers?;
4757
- private sharedRateLimitTracker?;
4758
- private sharedRetryConfig?;
4759
- private reasoningConfig?;
4760
- private cachingConfig?;
4761
- constructor(client?: LLMist);
3883
+ declare abstract class AbstractGadget {
4762
3884
  /**
4763
- * Set the model to use.
4764
- * Supports aliases like "gpt4", "sonnet", "flash".
4765
- *
4766
- * @param model - Model name or alias
4767
- * @returns This builder for chaining
4768
- *
4769
- * @example
4770
- * ```typescript
4771
- * .withModel("sonnet") // Alias
4772
- * .withModel("gpt-5-nano") // Auto-detects provider
4773
- * .withModel("openai:gpt-5") // Explicit provider
4774
- * ```
3885
+ * The name of the gadget. Used for identification when LLM calls it.
3886
+ * If not provided, defaults to the class name.
4775
3887
  */
4776
- withModel(model: string): this;
3888
+ name?: string;
4777
3889
  /**
4778
- * Set the system prompt.
4779
- *
4780
- * @param prompt - System prompt
4781
- * @returns This builder for chaining
3890
+ * Human-readable description of what the gadget does.
4782
3891
  */
4783
- withSystem(prompt: string): this;
3892
+ abstract description: string;
4784
3893
  /**
4785
- * Set the temperature (0-1).
4786
- *
4787
- * @param temperature - Temperature value
4788
- * @returns This builder for chaining
3894
+ * Optional Zod schema describing the expected input payload. When provided,
3895
+ * it will be validated before execution and transformed into a JSON Schema
3896
+ * representation that is surfaced to the LLM as part of the instructions.
4789
3897
  */
4790
- withTemperature(temperature: number): this;
3898
+ parameterSchema?: ZodTypeAny;
3899
+ /**
3900
+ * Optional timeout in milliseconds for gadget execution.
3901
+ * If execution exceeds this timeout, a TimeoutException will be thrown.
3902
+ * If not set, the global defaultGadgetTimeoutMs from runtime options will be used.
3903
+ * Set to 0 or undefined to disable timeout for this gadget.
3904
+ */
3905
+ timeoutMs?: number;
4791
3906
  /**
4792
- * Set maximum iterations.
3907
+ * Optional usage examples to help LLMs understand proper invocation.
3908
+ * Examples are rendered in getInstruction() alongside the schema.
4793
3909
  *
4794
- * @param max - Maximum number of iterations
4795
- * @returns This builder for chaining
3910
+ * Note: Uses broader `unknown` type to allow typed examples from subclasses
3911
+ * while maintaining runtime compatibility.
4796
3912
  */
4797
- withMaxIterations(max: number): this;
3913
+ examples?: GadgetExample<unknown>[];
4798
3914
  /**
4799
- * Set the budget limit in USD.
4800
- * The agent loop stops when cumulative cost reaches this limit.
3915
+ * Maximum number of concurrent executions allowed for this gadget.
3916
+ * Use this to prevent race conditions in gadgets that modify shared state.
3917
+ *
3918
+ * - `1` = Sequential execution (only one instance runs at a time)
3919
+ * - `0` or `undefined` = Unlimited concurrency (default)
3920
+ * - `N > 1` = At most N concurrent executions
4801
3921
  *
4802
- * @param amountUSD - Maximum spend in USD
4803
- * @returns This builder for chaining
3922
+ * This property sets a safety floor: external configuration (SubagentConfig)
3923
+ * can only make concurrency MORE restrictive, never less. For example, if
3924
+ * a gadget declares `maxConcurrent: 1`, external config cannot override it
3925
+ * to allow parallel execution.
4804
3926
  *
4805
3927
  * @example
4806
3928
  * ```typescript
4807
- * .withBudget(0.50) // Stop after $0.50 spent
3929
+ * // File writer that must run sequentially to avoid race conditions
3930
+ * class WriteFile extends Gadget({
3931
+ * description: 'Writes content to a file',
3932
+ * schema: z.object({ path: z.string(), content: z.string() }),
3933
+ * maxConcurrent: 1, // Sequential - prevents race conditions
3934
+ * }) {
3935
+ * execute(params: this['params']) { ... }
3936
+ * }
4808
3937
  * ```
4809
3938
  */
4810
- withBudget(amountUSD: number): this;
3939
+ maxConcurrent?: number;
4811
3940
  /**
4812
- * Set logger instance.
3941
+ * If true, this gadget must execute alone — no other gadgets in the same
3942
+ * LLM response can run in parallel. When an exclusive gadget arrives and
3943
+ * other gadgets are already in-flight, it is deferred until they complete.
3944
+ *
3945
+ * Use for gadgets that terminate the agent loop (e.g., Finish), where
3946
+ * sibling tool results must be visible to the LLM before the loop ends.
4813
3947
  *
4814
- * @param logger - Logger instance
4815
- * @returns This builder for chaining
3948
+ * This is a safety floor: external config cannot weaken it.
4816
3949
  */
4817
- withLogger(logger: Logger<ILogObj>): this;
3950
+ exclusive?: boolean;
4818
3951
  /**
4819
- * Add hooks for agent lifecycle events.
3952
+ * Execute the gadget with the given parameters.
3953
+ * Can be synchronous or asynchronous.
4820
3954
  *
4821
- * @param hooks - Agent hooks configuration
4822
- * @returns This builder for chaining
3955
+ * @param params - Parameters passed from the LLM
3956
+ * @param ctx - Optional execution context for cost reporting and LLM access
3957
+ * @returns Result as a string, or an object with result and optional cost
4823
3958
  *
4824
3959
  * @example
4825
3960
  * ```typescript
4826
- * import { HookPresets } from 'llmist/hooks';
3961
+ * // Simple string return (free gadget)
3962
+ * execute(params) {
3963
+ * return "result";
3964
+ * }
4827
3965
  *
4828
- * .withHooks(HookPresets.logging())
4829
- * .withHooks(HookPresets.merge(
4830
- * HookPresets.logging(),
4831
- * HookPresets.timing()
4832
- * ))
4833
- * ```
4834
- */
4835
- withHooks(hooks: AgentHooks): this;
4836
- /**
4837
- * Configure custom prompts for gadget system messages.
3966
+ * // Object return with cost tracking
3967
+ * execute(params) {
3968
+ * return { result: "data", cost: 0.001 };
3969
+ * }
4838
3970
  *
4839
- * @param config - Prompt configuration object
4840
- * @returns This builder for chaining
3971
+ * // Using context for callback-based cost reporting
3972
+ * execute(params, ctx) {
3973
+ * ctx.reportCost(0.001);
3974
+ * return "result";
3975
+ * }
4841
3976
  *
4842
- * @example
4843
- * ```typescript
4844
- * .withPromptTemplateConfig({
4845
- * mainInstruction: "Use the gadget markers below:",
4846
- * rules: ["Always use markers", "Never use function calling"]
4847
- * })
3977
+ * // Using wrapped LLMist for automatic cost tracking
3978
+ * async execute(params, ctx) {
3979
+ * const summary = await ctx.llmist.complete('Summarize: ' + params.text);
3980
+ * return summary;
3981
+ * }
4848
3982
  * ```
4849
3983
  */
4850
- withPromptTemplateConfig(config: PromptTemplateConfig): this;
3984
+ abstract execute(params: Record<string, unknown>, ctx?: ExecutionContext): GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
4851
3985
  /**
4852
- * Add gadgets (classes or instances).
4853
- * Can be called multiple times to add more gadgets.
3986
+ * Throws an AbortException if the execution has been aborted.
3987
+ *
3988
+ * Call this at key checkpoints in long-running gadgets to allow early exit
3989
+ * when the gadget has been cancelled (e.g., due to timeout). This enables
3990
+ * resource cleanup and prevents unnecessary work after cancellation.
4854
3991
  *
4855
- * @param gadgets - Gadget classes or instances
4856
- * @returns This builder for chaining
3992
+ * @param ctx - The execution context containing the abort signal
3993
+ * @throws AbortException if ctx.signal.aborted is true
4857
3994
  *
4858
3995
  * @example
4859
3996
  * ```typescript
4860
- * .withGadgets(Calculator, Weather, Email)
4861
- * .withGadgets(new Calculator(), new Weather())
4862
- * .withGadgets(createGadget({ ... }))
4863
- * ```
4864
- */
4865
- withGadgets(...gadgets: GadgetOrClass[]): this;
4866
- /**
4867
- * Add conversation history messages.
4868
- * Useful for continuing previous conversations.
3997
+ * class DataProcessor extends Gadget({
3998
+ * description: 'Processes data in multiple steps',
3999
+ * schema: z.object({ items: z.array(z.string()) }),
4000
+ * }) {
4001
+ * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4002
+ * const results: string[] = [];
4003
+ *
4004
+ * for (const item of params.items) {
4005
+ * // Check before each expensive operation
4006
+ * this.throwIfAborted(ctx);
4869
4007
  *
4870
- * @param messages - Array of history messages
4871
- * @returns This builder for chaining
4008
+ * results.push(await this.processItem(item));
4009
+ * }
4872
4010
  *
4873
- * @example
4874
- * ```typescript
4875
- * .withHistory([
4876
- * { user: "Hello" },
4877
- * { assistant: "Hi there!" },
4878
- * { user: "How are you?" },
4879
- * { assistant: "I'm doing well, thanks!" }
4880
- * ])
4011
+ * return results.join(', ');
4012
+ * }
4013
+ * }
4881
4014
  * ```
4882
4015
  */
4883
- withHistory(messages: HistoryMessage[]): this;
4016
+ throwIfAborted(ctx?: ExecutionContext): void;
4884
4017
  /**
4885
- * Add a single message to the conversation history.
4018
+ * Register a cleanup function to run when execution is aborted (timeout or cancellation).
4019
+ * The cleanup function is called immediately if the signal is already aborted.
4020
+ * Errors thrown by the cleanup function are silently ignored.
4021
+ *
4022
+ * Use this to clean up resources like browser instances, database connections,
4023
+ * or child processes when the gadget is cancelled due to timeout.
4886
4024
  *
4887
- * @param message - Single history message
4888
- * @returns This builder for chaining
4025
+ * @param ctx - The execution context containing the abort signal
4026
+ * @param cleanup - Function to run on abort (can be sync or async)
4889
4027
  *
4890
4028
  * @example
4891
4029
  * ```typescript
4892
- * .addMessage({ user: "Hello" })
4893
- * .addMessage({ assistant: "Hi there!" })
4894
- * ```
4895
- */
4896
- addMessage(message: HistoryMessage): this;
4897
- /**
4898
- * Clear any previously set conversation history.
4899
- * Used before setting new cumulative history in REPL mode.
4030
+ * class BrowserGadget extends Gadget({
4031
+ * description: 'Fetches web page content',
4032
+ * schema: z.object({ url: z.string() }),
4033
+ * }) {
4034
+ * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4035
+ * const browser = await chromium.launch();
4036
+ * this.onAbort(ctx, () => browser.close());
4900
4037
  *
4901
- * @returns This builder for chaining
4038
+ * const page = await browser.newPage();
4039
+ * this.onAbort(ctx, () => page.close());
4902
4040
  *
4903
- * @example
4904
- * ```typescript
4905
- * // Reset history before setting new cumulative history
4906
- * builder.clearHistory().withHistory(cumulativeHistory);
4041
+ * await page.goto(params.url);
4042
+ * const content = await page.content();
4043
+ *
4044
+ * await browser.close();
4045
+ * return content;
4046
+ * }
4047
+ * }
4907
4048
  * ```
4908
4049
  */
4909
- clearHistory(): this;
4050
+ onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
4910
4051
  /**
4911
- * Continue conversation from a previous agent's history.
4912
- * Extracts full conversation history and sets it as initial messages.
4052
+ * Create an AbortController linked to the execution context's signal.
4053
+ * When the parent signal aborts, the returned controller also aborts with the same reason.
4913
4054
  *
4914
- * This is the recommended way to implement REPL session continuation.
4915
- * It automatically handles history extraction and format conversion.
4055
+ * Useful for passing abort signals to child operations like fetch() while still
4056
+ * being able to abort them independently if needed.
4916
4057
  *
4917
- * @param agent - The previous agent to continue from
4918
- * @returns This builder for chaining
4058
+ * @param ctx - The execution context containing the parent abort signal
4059
+ * @returns A new AbortController linked to the parent signal
4919
4060
  *
4920
4061
  * @example
4921
4062
  * ```typescript
4922
- * // REPL loop with session continuity
4923
- * let previousAgent: Agent | null = null;
4063
+ * class FetchGadget extends Gadget({
4064
+ * description: 'Fetches data from URL',
4065
+ * schema: z.object({ url: z.string() }),
4066
+ * }) {
4067
+ * async execute(params: this['params'], ctx?: ExecutionContext): Promise<string> {
4068
+ * const controller = this.createLinkedAbortController(ctx);
4924
4069
  *
4925
- * while (true) {
4926
- * if (previousAgent) {
4927
- * builder.continueFrom(previousAgent);
4070
+ * // fetch() will automatically abort when parent times out
4071
+ * const response = await fetch(params.url, { signal: controller.signal });
4072
+ * return response.text();
4928
4073
  * }
4929
- * const agent = builder.ask(prompt);
4930
- * await runAgent(agent);
4931
- * previousAgent = agent;
4932
4074
  * }
4933
4075
  * ```
4934
4076
  */
4935
- continueFrom(agent: Agent): this;
4077
+ createLinkedAbortController(ctx?: ExecutionContext): AbortController;
4936
4078
  /**
4937
- * Set the human input handler for interactive conversations.
4938
- *
4939
- * @param handler - Function to handle human input requests
4940
- * @returns This builder for chaining
4079
+ * Generate instruction text for the LLM.
4080
+ * Combines name, description, and parameter schema into a formatted instruction.
4941
4081
  *
4942
- * @example
4943
- * ```typescript
4944
- * .onHumanInput(async (question) => {
4945
- * return await promptUser(question);
4946
- * })
4947
- * ```
4082
+ * @param optionsOrArgPrefix - Optional custom prefixes for examples, or just argPrefix string for backwards compatibility
4083
+ * @returns Formatted instruction string
4948
4084
  */
4949
- onHumanInput(handler: (question: string) => Promise<string>): this;
4085
+ getInstruction(optionsOrArgPrefix?: string | {
4086
+ argPrefix?: string;
4087
+ startPrefix?: string;
4088
+ endPrefix?: string;
4089
+ }): string;
4090
+ }
4091
+
4092
+ /**
4093
+ * Context provided to prompt template functions for rendering dynamic content.
4094
+ */
4095
+ interface PromptContext {
4096
+ /** Custom gadget start prefix */
4097
+ startPrefix: string;
4098
+ /** Custom gadget end prefix */
4099
+ endPrefix: string;
4100
+ /** Custom argument prefix for block format */
4101
+ argPrefix: string;
4102
+ /** Number of gadgets being registered */
4103
+ gadgetCount: number;
4104
+ /** Names of all gadgets */
4105
+ gadgetNames: string[];
4106
+ }
4107
+ /**
4108
+ * Context provided to hint template functions for rendering dynamic hints.
4109
+ */
4110
+ interface HintContext {
4111
+ /** Current iteration (1-based for readability) */
4112
+ iteration: number;
4113
+ /** Maximum iterations allowed */
4114
+ maxIterations: number;
4115
+ /** Iterations remaining (maxIterations - iteration) */
4116
+ remaining: number;
4117
+ /** Number of gadget calls in the current response */
4118
+ gadgetCallCount?: number;
4119
+ }
4120
+ /**
4121
+ * Template that can be either a static string or a function that renders based on context.
4122
+ */
4123
+ type PromptTemplate = string | ((context: PromptContext) => string);
4124
+ /**
4125
+ * Template for hints that can be either a static string or a function that renders based on hint context.
4126
+ */
4127
+ type HintTemplate = string | ((context: HintContext) => string);
4128
+ /**
4129
+ * Configuration for customizing all prompts used internally by llmist.
4130
+ *
4131
+ * Each field can be either a string (static text) or a function that receives
4132
+ * context and returns a string (for dynamic content).
4133
+ *
4134
+ * @example
4135
+ * ```typescript
4136
+ * const customConfig: PromptTemplateConfig = {
4137
+ * mainInstruction: "USE ONLY THE GADGET MARKERS BELOW:",
4138
+ * criticalUsage: "Important: Follow the exact format shown.",
4139
+ * rules: (ctx) => [
4140
+ * "Always use the markers to invoke gadgets",
4141
+ * "Never use function calling",
4142
+ * `You have ${ctx.gadgetCount} gadgets available`
4143
+ * ]
4144
+ * };
4145
+ * ```
4146
+ */
4147
+ interface PromptTemplateConfig {
4950
4148
  /**
4951
- * Set custom gadget marker prefix.
4952
- *
4953
- * @param prefix - Custom start prefix for gadget markers
4954
- * @returns This builder for chaining
4955
- *
4956
- * @example
4957
- * ```typescript
4958
- * .withGadgetStartPrefix("<<GADGET_START>>")
4959
- * ```
4149
+ * Main instruction block that appears at the start of the gadget system prompt.
4150
+ * Default emphasizes using text markers instead of function calling.
4960
4151
  */
4961
- withGadgetStartPrefix(prefix: string): this;
4152
+ mainInstruction?: PromptTemplate;
4962
4153
  /**
4963
- * Set custom gadget marker suffix.
4964
- *
4965
- * @param suffix - Custom end suffix for gadget markers
4966
- * @returns This builder for chaining
4967
- *
4968
- * @example
4969
- * ```typescript
4970
- * .withGadgetEndPrefix("<<GADGET_END>>")
4971
- * ```
4154
+ * Critical usage instruction that appears in the usage section.
4155
+ * Default emphasizes the exact format requirement.
4972
4156
  */
4973
- withGadgetEndPrefix(suffix: string): this;
4157
+ criticalUsage?: PromptTemplate;
4974
4158
  /**
4975
- * Set custom argument prefix for block format parameters.
4976
- *
4977
- * @param prefix - Custom prefix for argument markers (default: "!!!ARG:")
4978
- * @returns This builder for chaining
4979
- *
4980
- * @example
4981
- * ```typescript
4982
- * .withGadgetArgPrefix("<<ARG>>")
4983
- * ```
4159
+ * Format description for the block parameter format.
4160
+ * Default uses the configured argPrefix dynamically.
4984
4161
  */
4985
- withGadgetArgPrefix(prefix: string): this;
4162
+ formatDescription?: PromptTemplate;
4986
4163
  /**
4987
- * Set the text-only handler strategy.
4988
- *
4989
- * Controls what happens when the LLM returns text without calling any gadgets:
4990
- * - "terminate": End the agent loop (default)
4991
- * - "acknowledge": Continue the loop for another iteration
4992
- * - "wait_for_input": Wait for human input
4993
- * - Custom handler: Provide a function for dynamic behavior
4994
- *
4995
- * @param handler - Text-only handler strategy or custom handler
4996
- * @returns This builder for chaining
4997
- *
4998
- * @example
4999
- * ```typescript
5000
- * // Simple strategy
5001
- * .withTextOnlyHandler("acknowledge")
5002
- *
5003
- * // Custom handler
5004
- * .withTextOnlyHandler({
5005
- * type: "custom",
5006
- * handler: async (context) => {
5007
- * if (context.text.includes("?")) {
5008
- * return { action: "wait_for_input", question: context.text };
5009
- * }
5010
- * return { action: "continue" };
5011
- * }
5012
- * })
5013
- * ```
4164
+ * Rules that appear in the rules section.
4165
+ * Can be an array of strings or a function that returns an array.
4166
+ * Default includes rules about not using function calling.
5014
4167
  */
5015
- withTextOnlyHandler(handler: TextOnlyHandler): this;
4168
+ rules?: PromptTemplate | string[] | ((context: PromptContext) => string[]);
5016
4169
  /**
5017
- * Set the handler for text content that appears alongside gadget calls.
5018
- *
5019
- * When set, text accompanying gadget responses will be wrapped as a
5020
- * synthetic gadget call before the actual gadget results in the
5021
- * conversation history.
5022
- *
5023
- * @param handler - Configuration for wrapping text
5024
- * @returns This builder for chaining
5025
- *
5026
- * @example
5027
- * ```typescript
5028
- * // Wrap text as TellUser gadget
5029
- * .withTextWithGadgetsHandler({
5030
- * gadgetName: "TellUser",
5031
- * parameterMapping: (text) => ({ message: text, done: false, type: "info" }),
5032
- * resultMapping: (text) => `ℹ️ ${text}`,
5033
- * })
5034
- * ```
4170
+ * Custom examples to show in the examples section.
4171
+ * If provided, replaces the default examples entirely.
4172
+ * Should be a function that returns formatted example strings.
5035
4173
  */
5036
- withTextWithGadgetsHandler(handler: {
5037
- gadgetName: string;
5038
- parameterMapping: (text: string) => Record<string, unknown>;
5039
- resultMapping?: (text: string) => string;
5040
- }): this;
4174
+ customExamples?: (context: PromptContext) => string;
5041
4175
  /**
5042
- * Set default timeout for gadget execution.
5043
- *
5044
- * @param timeoutMs - Timeout in milliseconds (must be non-negative)
5045
- * @returns This builder for chaining
5046
- * @throws {Error} If timeout is negative
5047
- *
5048
- * @example
5049
- * ```typescript
5050
- * .withDefaultGadgetTimeout(5000) // 5 second timeout
5051
- * ```
4176
+ * Hint shown when LLM uses only one gadget per response.
4177
+ * Encourages parallel gadget usage for efficiency.
5052
4178
  */
5053
- withDefaultGadgetTimeout(timeoutMs: number): this;
4179
+ parallelGadgetsHint?: HintTemplate;
5054
4180
  /**
5055
- * Set the gadget execution mode.
5056
- *
5057
- * Controls how multiple gadgets are executed when the LLM calls them:
5058
- * - `'parallel'` (default): Gadgets without dependencies execute concurrently
5059
- * - `'sequential'`: Gadgets execute one at a time, each awaiting completion
5060
- *
5061
- * @param mode - Execution mode ('parallel' or 'sequential')
5062
- * @returns This builder for chaining
5063
- *
5064
- * @example
5065
- * ```typescript
5066
- * // Sequential execution for ordered file operations
5067
- * .withGadgetExecutionMode('sequential')
4181
+ * Template for iteration progress hint.
4182
+ * Informs the LLM about remaining iterations to help plan work.
5068
4183
  *
5069
- * // Parallel execution (default) for independent operations
5070
- * .withGadgetExecutionMode('parallel')
5071
- * ```
4184
+ * When using a string template, supports placeholders:
4185
+ * - {iteration}: Current iteration (1-based)
4186
+ * - {maxIterations}: Maximum iterations allowed
4187
+ * - {remaining}: Iterations remaining
5072
4188
  */
5073
- withGadgetExecutionMode(mode: GadgetExecutionMode): this;
4189
+ iterationProgressHint?: HintTemplate;
4190
+ }
4191
+ /**
4192
+ * Default hint templates used by llmist.
4193
+ */
4194
+ declare const DEFAULT_HINTS: {
4195
+ readonly parallelGadgetsHint: "Tip: You can call multiple gadgets in a single response for efficiency.";
4196
+ readonly iterationProgressHint: "[Iteration {iteration}/{maxIterations}] Plan your actions accordingly.";
4197
+ };
4198
+ /**
4199
+ * Default prompt templates used by llmist.
4200
+ */
4201
+ declare const DEFAULT_PROMPTS: Required<Omit<PromptTemplateConfig, "rules" | "customExamples" | "parallelGadgetsHint" | "iterationProgressHint"> & {
4202
+ rules: (context: PromptContext) => string[];
4203
+ customExamples: null;
4204
+ }>;
4205
+ /**
4206
+ * Resolve a prompt template to a string using the given context.
4207
+ */
4208
+ declare function resolvePromptTemplate(template: PromptTemplate | undefined, defaultValue: PromptTemplate, context: PromptContext): string;
4209
+ /**
4210
+ * Resolve rules template to an array of strings.
4211
+ */
4212
+ declare function resolveRulesTemplate(rules: PromptTemplateConfig["rules"] | undefined, context: PromptContext): string[];
4213
+ /**
4214
+ * Resolve a hint template to a string using the given context.
4215
+ * Supports both function templates and string templates with placeholders.
4216
+ *
4217
+ * @param template - The hint template to resolve
4218
+ * @param defaultValue - Default value if template is undefined
4219
+ * @param context - Context for rendering the template
4220
+ * @returns The resolved hint string
4221
+ */
4222
+ declare function resolveHintTemplate(template: HintTemplate | undefined, defaultValue: string, context: HintContext): string;
4223
+
4224
+ type MessageRole = "system" | "user" | "assistant";
4225
+ /**
4226
+ * Message content can be a simple string (text only) or an array of content parts (multimodal).
4227
+ * Using a string is simpler for text-only messages, while arrays support images and audio.
4228
+ */
4229
+ type MessageContent = string | ContentPart[];
4230
+ interface LLMMessage {
4231
+ role: MessageRole;
4232
+ content: MessageContent;
4233
+ name?: string;
4234
+ metadata?: Record<string, unknown>;
4235
+ }
4236
+ /**
4237
+ * Normalize message content to an array of content parts.
4238
+ * Converts string content to a single text part.
4239
+ *
4240
+ * @param content - Message content (string or ContentPart[])
4241
+ * @returns Array of content parts
4242
+ */
4243
+ declare function normalizeMessageContent(content: MessageContent): ContentPart[];
4244
+ /**
4245
+ * Extract text from message content.
4246
+ * Concatenates all text parts in the content.
4247
+ *
4248
+ * @param content - Message content (string or ContentPart[])
4249
+ * @returns Combined text from all text parts
4250
+ */
4251
+ declare function extractMessageText(content: MessageContent): string;
4252
+ declare class LLMMessageBuilder {
4253
+ private readonly messages;
4254
+ private startPrefix;
4255
+ private endPrefix;
4256
+ private argPrefix;
4257
+ private promptConfig;
4258
+ constructor(promptConfig?: PromptTemplateConfig);
4259
+ /**
4260
+ * Set custom prefixes for gadget markers.
4261
+ * Used to configure history builder to match system prompt markers.
4262
+ */
4263
+ withPrefixes(startPrefix: string, endPrefix: string, argPrefix?: string): this;
4264
+ addSystem(content: string, metadata?: Record<string, unknown>): this;
4265
+ addGadgets(gadgets: AbstractGadget[], options?: {
4266
+ startPrefix?: string;
4267
+ endPrefix?: string;
4268
+ argPrefix?: string;
4269
+ }): this;
4270
+ private buildGadgetsSection;
4271
+ private buildUsageSection;
4272
+ private buildExamplesSection;
4273
+ private buildRulesSection;
5074
4274
  /**
5075
- * Set the maximum number of gadgets to execute per LLM response.
5076
- *
5077
- * When the limit is reached, remaining gadgets are skipped with an informative
5078
- * message visible to the LLM, allowing it to adjust on the next iteration.
5079
- * Gadgets already in-flight (executing in parallel) are allowed to complete.
4275
+ * Add a user message.
4276
+ * Content can be a string (text only) or an array of content parts (multimodal).
5080
4277
  *
5081
- * @param max - Maximum gadgets per response (0 = unlimited, default)
5082
- * @returns This builder for chaining
5083
- * @throws {Error} If max is negative or non-integer
4278
+ * @param content - Message content
4279
+ * @param metadata - Optional metadata
5084
4280
  *
5085
4281
  * @example
5086
4282
  * ```typescript
5087
- * // Limit to 5 gadgets per response
5088
- * LLMist.createAgent()
5089
- * .withModel("sonnet")
5090
- * .withGadgets(ReadFile, WriteFile, Search)
5091
- * .withMaxGadgetsPerResponse(5)
5092
- * .ask("Process these files...");
4283
+ * // Text only
4284
+ * builder.addUser("Hello!");
4285
+ *
4286
+ * // Multimodal
4287
+ * builder.addUser([
4288
+ * text("What's in this image?"),
4289
+ * imageFromBuffer(imageData),
4290
+ * ]);
5093
4291
  * ```
5094
4292
  */
5095
- withMaxGadgetsPerResponse(max: number): this;
4293
+ addUser(content: MessageContent, metadata?: Record<string, unknown>): this;
4294
+ addAssistant(content: string, metadata?: Record<string, unknown>): this;
5096
4295
  /**
5097
- * Enable or disable gadget output limiting.
5098
- *
5099
- * When enabled, gadget outputs exceeding the configured limit are stored
5100
- * and can be browsed using the GadgetOutputViewer gadget.
4296
+ * Add a user message with an image attachment.
5101
4297
  *
5102
- * @param enabled - Whether to enable output limiting (default: true)
5103
- * @returns This builder for chaining
4298
+ * @param textContent - Text prompt
4299
+ * @param imageData - Image data (Buffer, Uint8Array, or base64 string)
4300
+ * @param mimeType - Optional MIME type (auto-detected if not provided)
5104
4301
  *
5105
4302
  * @example
5106
4303
  * ```typescript
5107
- * .withGadgetOutputLimit(false) // Disable output limiting
4304
+ * builder.addUserWithImage(
4305
+ * "What's in this image?",
4306
+ * await fs.readFile("photo.jpg"),
4307
+ * "image/jpeg" // Optional - auto-detected
4308
+ * );
5108
4309
  * ```
5109
4310
  */
5110
- withGadgetOutputLimit(enabled: boolean): this;
4311
+ addUserWithImage(textContent: string, imageData: Buffer | Uint8Array | string, mimeType?: ImageMimeType): this;
5111
4312
  /**
5112
- * Set the maximum gadget output as a percentage of the model's context window.
5113
- *
5114
- * Outputs exceeding this limit are stored for later browsing with GadgetOutputViewer.
4313
+ * Add a user message with an image URL (OpenAI only).
5115
4314
  *
5116
- * @param percent - Percentage of context window (1-100, default: 15)
5117
- * @returns This builder for chaining
5118
- * @throws {Error} If percent is not between 1 and 100
4315
+ * @param textContent - Text prompt
4316
+ * @param imageUrl - URL to the image
5119
4317
  *
5120
4318
  * @example
5121
4319
  * ```typescript
5122
- * .withGadgetOutputLimitPercent(25) // 25% of context window
4320
+ * builder.addUserWithImageUrl(
4321
+ * "What's in this image?",
4322
+ * "https://example.com/image.jpg"
4323
+ * );
5123
4324
  * ```
5124
4325
  */
5125
- withGadgetOutputLimitPercent(percent: number): this;
4326
+ addUserWithImageUrl(textContent: string, imageUrl: string): this;
5126
4327
  /**
5127
- * Configure context compaction.
5128
- *
5129
- * Context compaction automatically manages conversation history to prevent
5130
- * context window overflow in long-running agent conversations.
4328
+ * Add a user message with an audio attachment (Gemini only).
5131
4329
  *
5132
- * @param config - Compaction configuration options
5133
- * @returns This builder for chaining
4330
+ * @param textContent - Text prompt
4331
+ * @param audioData - Audio data (Buffer, Uint8Array, or base64 string)
4332
+ * @param mimeType - Optional MIME type (auto-detected if not provided)
5134
4333
  *
5135
4334
  * @example
5136
4335
  * ```typescript
5137
- * // Custom thresholds
5138
- * .withCompaction({
5139
- * triggerThresholdPercent: 70,
5140
- * targetPercent: 40,
5141
- * preserveRecentTurns: 10,
5142
- * })
5143
- *
5144
- * // Different strategy
5145
- * .withCompaction({
5146
- * strategy: 'sliding-window',
5147
- * })
5148
- *
5149
- * // With callback
5150
- * .withCompaction({
5151
- * onCompaction: (event) => {
5152
- * console.log(`Saved ${event.tokensBefore - event.tokensAfter} tokens`);
5153
- * }
5154
- * })
4336
+ * builder.addUserWithAudio(
4337
+ * "Transcribe this audio",
4338
+ * await fs.readFile("recording.mp3"),
4339
+ * "audio/mp3" // Optional - auto-detected
4340
+ * );
5155
4341
  * ```
5156
4342
  */
5157
- withCompaction(config: CompactionConfig): this;
4343
+ addUserWithAudio(textContent: string, audioData: Buffer | Uint8Array | string, mimeType?: AudioMimeType): this;
5158
4344
  /**
5159
- * Disable context compaction.
5160
- *
5161
- * By default, compaction is enabled. Use this method to explicitly disable it.
4345
+ * Add a user message with multiple content parts.
4346
+ * Provides full flexibility for complex multimodal messages.
5162
4347
  *
5163
- * @returns This builder for chaining
4348
+ * @param parts - Array of content parts
5164
4349
  *
5165
4350
  * @example
5166
4351
  * ```typescript
5167
- * .withoutCompaction() // Disable automatic compaction
4352
+ * builder.addUserMultimodal([
4353
+ * text("Compare these images:"),
4354
+ * imageFromBuffer(image1),
4355
+ * imageFromBuffer(image2),
4356
+ * ]);
5168
4357
  * ```
5169
4358
  */
5170
- withoutCompaction(): this;
4359
+ addUserMultimodal(parts: ContentPart[]): this;
5171
4360
  /**
5172
- * Configure retry behavior for LLM API calls.
5173
- *
5174
- * Retry is enabled by default with conservative settings (3 retries, exponential backoff).
5175
- * Use this method to customize retry behavior for rate limits, timeouts, and transient errors.
5176
- *
5177
- * @param config - Retry configuration options
5178
- * @returns This builder for chaining
5179
- *
5180
- * @example
5181
- * ```typescript
5182
- * // Custom retry configuration
5183
- * .withRetry({
5184
- * retries: 5,
5185
- * minTimeout: 2000,
5186
- * maxTimeout: 60000,
5187
- * })
4361
+ * Record a gadget execution result in the message history.
4362
+ * Creates an assistant message with the gadget invocation and a user message with the result.
5188
4363
  *
5189
- * // With monitoring callbacks
5190
- * .withRetry({
5191
- * onRetry: (error, attempt) => {
5192
- * console.log(`Retry ${attempt}: ${error.message}`);
5193
- * },
5194
- * onRetriesExhausted: (error, attempts) => {
5195
- * alerting.warn(`Failed after ${attempts} attempts`);
5196
- * }
5197
- * })
4364
+ * The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
5198
4365
  *
5199
- * // Custom retry logic
5200
- * .withRetry({
5201
- * shouldRetry: (error) => error.message.includes('429'),
5202
- * })
5203
- * ```
4366
+ * @param gadget - Name of the gadget that was executed
4367
+ * @param parameters - Parameters that were passed to the gadget
4368
+ * @param result - Text result from the gadget execution
4369
+ * @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
4370
+ * @param media - Optional media outputs from the gadget
4371
+ * @param mediaIds - Optional IDs for the media outputs
4372
+ * @param storedMedia - Optional stored media info including file paths
5204
4373
  */
5205
- withRetry(config: RetryConfig): this;
4374
+ addGadgetCallResult(gadget: string, parameters: Record<string, unknown>, result: string, invocationId: string, media?: GadgetMediaOutput[], mediaIds?: string[], storedMedia?: StoredMedia[]): this;
5206
4375
  /**
5207
- * Disable automatic retry for LLM API calls.
5208
- *
5209
- * By default, retry is enabled. Use this method to explicitly disable it.
5210
- *
5211
- * @returns This builder for chaining
5212
- *
5213
- * @example
5214
- * ```typescript
5215
- * .withoutRetry() // Disable automatic retry
5216
- * ```
4376
+ * Format parameters as Block format with JSON Pointer paths.
4377
+ * Uses the configured argPrefix for consistency with system prompt.
5217
4378
  */
5218
- withoutRetry(): this;
4379
+ private formatBlockParameters;
4380
+ build(): LLMMessage[];
4381
+ }
4382
+
4383
+ /**
4384
+ * Provider-agnostic reasoning effort level.
4385
+ *
4386
+ * Maps to provider-specific values:
4387
+ * - **OpenAI**: "none"|"low"|"medium"|"high"|"xhigh"
4388
+ * - **Anthropic**: budget_tokens (1024–32768)
4389
+ * - **Gemini 3**: thinkingLevel "minimal"|"low"|"medium"|"high"
4390
+ * - **Gemini 2.5**: thinkingBudget (0–24576)
4391
+ * - **DeepSeek**: binary (enabled/disabled)
4392
+ */
4393
+ type ReasoningEffort = "none" | "low" | "medium" | "high" | "maximum";
4394
+ /**
4395
+ * Configuration for reasoning/thinking mode on supported models.
4396
+ *
4397
+ * When `enabled` is true, the provider will be instructed to use
4398
+ * extended reasoning before generating its response.
4399
+ */
4400
+ interface ReasoningConfig {
4401
+ /** Whether reasoning is enabled */
4402
+ enabled: boolean;
4403
+ /** Reasoning effort level (default: "medium") */
4404
+ effort?: ReasoningEffort;
4405
+ /** Explicit token budget for thinking (Anthropic/Gemini 2.5, overrides effort) */
4406
+ budgetTokens?: number;
4407
+ /** Whether to surface thinking content in the stream (default: true) */
4408
+ includeThinking?: boolean;
4409
+ /** Enable interleaved thinking for multi-turn tool use (Anthropic only) */
4410
+ interleaved?: boolean;
4411
+ }
4412
+ /**
4413
+ * A chunk of thinking/reasoning content from a reasoning model.
4414
+ *
4415
+ * Emitted during streaming when a reasoning model produces thinking output.
4416
+ * The `type` field distinguishes actual thinking from redacted content
4417
+ * (e.g., Anthropic may redact thinking in certain scenarios).
4418
+ */
4419
+ interface ThinkingChunk {
4420
+ /** The thinking text content */
4421
+ content: string;
4422
+ /** Whether this is actual thinking or redacted content */
4423
+ type: "thinking" | "redacted";
4424
+ /** Verification signature (Anthropic/Gemini) */
4425
+ signature?: string;
4426
+ }
4427
+ /**
4428
+ * What content to include in the cache.
4429
+ *
4430
+ * - `"system"`: Cache only system prompt (lowest cost, highest reuse)
4431
+ * - `"conversation"`: Cache system prompt + all conversation turns except the latest user message
4432
+ */
4433
+ type CachingScope = "system" | "conversation";
4434
+ /**
4435
+ * Configuration for context caching across providers.
4436
+ *
4437
+ * Context caching allows reusing previously computed key-value pairs across
4438
+ * requests, reducing latency and cost for repeated context.
4439
+ *
4440
+ * Provider behavior:
4441
+ * - **Anthropic**: Automatic ephemeral caching via `cache_control` markers (always-on by default).
4442
+ * Use `enabled: false` to disable markers and opt out of caching.
4443
+ * - **Gemini**: Explicit cache lifecycle via `caches.create()`. Requires `scope` and `ttl`.
4444
+ * - **OpenAI**: Server-side automatic caching (no-op, but respects the unified API).
4445
+ */
4446
+ interface CachingConfig {
4447
+ /** Whether context caching is enabled */
4448
+ enabled: boolean;
5219
4449
  /**
5220
- * Configure proactive rate limiting to prevent rate limit errors.
5221
- *
5222
- * Set limits based on your API tier to automatically throttle requests
5223
- * before hitting provider limits. Works in conjunction with reactive
5224
- * retry/backoff for comprehensive rate limit handling.
5225
- *
5226
- * @param config - Rate limit configuration
5227
- * @returns This builder for chaining
5228
- *
5229
- * @example
5230
- * ```typescript
5231
- * // Gemini free tier limits
5232
- * .withRateLimits({
5233
- * requestsPerMinute: 15,
5234
- * tokensPerMinute: 1_000_000,
5235
- * safetyMargin: 0.8, // Start throttling at 80%
5236
- * })
5237
- *
5238
- * // OpenAI Tier 1 limits
5239
- * .withRateLimits({
5240
- * requestsPerMinute: 500,
5241
- * tokensPerMinute: 200_000,
5242
- * })
5243
- *
5244
- * // With daily limit (Gemini free tier)
5245
- * .withRateLimits({
5246
- * requestsPerMinute: 15,
5247
- * tokensPerDay: 1_500_000,
5248
- * })
5249
- * ```
4450
+ * What to cache (Gemini only, default: "conversation").
4451
+ * - `"system"`: Cache only system-derived messages
4452
+ * - `"conversation"`: Cache system + all turns except the latest user message
5250
4453
  */
5251
- withRateLimits(config: RateLimitConfig): this;
4454
+ scope?: CachingScope;
4455
+ /** TTL for cache entries (Gemini only, format: "3600s", default: "3600s", min: "300s") */
4456
+ ttl?: string;
4457
+ /** Minimum token count for content to be eligible for caching (Gemini default: 32768) */
4458
+ minTokenThreshold?: number;
4459
+ }
4460
+ interface LLMGenerationOptions {
4461
+ model: string;
4462
+ messages: LLMMessage[];
4463
+ maxTokens?: number;
4464
+ temperature?: number;
4465
+ topP?: number;
4466
+ stopSequences?: string[];
4467
+ responseFormat?: "text";
4468
+ metadata?: Record<string, unknown>;
4469
+ extra?: Record<string, unknown>;
5252
4470
  /**
5253
- * Set an abort signal for cancelling requests mid-flight.
5254
- *
5255
- * When the signal is aborted, the current LLM request will be cancelled
5256
- * and the agent loop will exit gracefully.
4471
+ * Optional abort signal for cancelling the request mid-flight.
5257
4472
  *
5258
- * @param signal - AbortSignal from an AbortController
5259
- * @returns This builder for chaining
4473
+ * When the signal is aborted, the provider will attempt to cancel
4474
+ * the underlying HTTP request and the stream will terminate with
4475
+ * an abort error. Use `isAbortError()` from `@/core/errors` to
4476
+ * detect cancellation in error handling.
5260
4477
  *
5261
4478
  * @example
5262
4479
  * ```typescript
5263
4480
  * const controller = new AbortController();
5264
4481
  *
5265
- * // Cancel after 30 seconds
5266
- * setTimeout(() => controller.abort(), 30000);
4482
+ * const stream = client.stream({
4483
+ * model: "claude-3-5-sonnet-20241022",
4484
+ * messages: [{ role: "user", content: "Tell me a long story" }],
4485
+ * signal: controller.signal,
4486
+ * });
5267
4487
  *
5268
- * const agent = LLMist.createAgent()
5269
- * .withModel("sonnet")
5270
- * .withSignal(controller.signal)
5271
- * .ask("Write a long story");
4488
+ * // Cancel after 5 seconds
4489
+ * setTimeout(() => controller.abort(), 5000);
5272
4490
  *
5273
- * // Or cancel on user action
5274
- * document.getElementById("cancel").onclick = () => controller.abort();
4491
+ * try {
4492
+ * for await (const chunk of stream) {
4493
+ * process.stdout.write(chunk.text);
4494
+ * }
4495
+ * } catch (error) {
4496
+ * if (isAbortError(error)) {
4497
+ * console.log("\nRequest was cancelled");
4498
+ * } else {
4499
+ * throw error;
4500
+ * }
4501
+ * }
5275
4502
  * ```
5276
4503
  */
5277
- withSignal(signal: AbortSignal): this;
4504
+ signal?: AbortSignal;
4505
+ /** Reasoning/thinking configuration for reasoning-capable models */
4506
+ reasoning?: ReasoningConfig;
4507
+ /** Context caching configuration for supported providers */
4508
+ caching?: CachingConfig;
4509
+ }
4510
+ interface TokenUsage {
4511
+ inputTokens: number;
4512
+ outputTokens: number;
4513
+ totalTokens: number;
4514
+ /** Number of input tokens served from cache (subset of inputTokens) */
4515
+ cachedInputTokens?: number;
4516
+ /** Number of input tokens written to cache (subset of inputTokens, Anthropic only) */
4517
+ cacheCreationInputTokens?: number;
4518
+ /** Number of reasoning/thinking tokens used (subset of outputTokens) */
4519
+ reasoningTokens?: number;
4520
+ }
4521
+ interface LLMStreamChunk {
4522
+ text: string;
5278
4523
  /**
5279
- * Enable reasoning/thinking mode for reasoning-capable models.
5280
- *
5281
- * Can be called with:
5282
- * - No args: enables reasoning at "medium" effort
5283
- * - A string effort level: `withReasoning("high")`
5284
- * - A full config object: `withReasoning({ enabled: true, budgetTokens: 10000 })`
5285
- *
5286
- * @param config - Optional effort level or full reasoning config
5287
- * @returns This builder for chaining
5288
- *
5289
- * @example
5290
- * ```typescript
5291
- * // Simple — medium effort
5292
- * LLMist.createAgent()
5293
- * .withModel("o3")
5294
- * .withReasoning()
5295
- * .ask("Solve this logic puzzle...");
5296
- *
5297
- * // Explicit effort level
5298
- * LLMist.createAgent()
5299
- * .withModel("anthropic:claude-4-opus")
5300
- * .withReasoning("high")
5301
- * .ask("Analyze this complex problem");
5302
- *
5303
- * // Full config with explicit token budget
5304
- * LLMist.createAgent()
5305
- * .withModel("anthropic:claude-4-opus")
5306
- * .withReasoning({ enabled: true, budgetTokens: 16000 })
5307
- * .ask("Step through this proof");
5308
- * ```
4524
+ * Indicates that the provider has finished producing output and includes the reason if available.
5309
4525
  */
5310
- withReasoning(config?: ReasoningConfig | ReasoningEffort): this;
4526
+ finishReason?: string | null;
5311
4527
  /**
5312
- * Explicitly disable reasoning for this agent, even if the model supports it.
5313
- *
5314
- * By default, reasoning is auto-enabled at "medium" effort for models with
5315
- * `features.reasoning: true`. Use this to opt out.
5316
- *
5317
- * @returns This builder for chaining
4528
+ * Token usage information, typically available in the final chunk when the stream completes.
5318
4529
  */
5319
- withoutReasoning(): this;
4530
+ usage?: TokenUsage;
5320
4531
  /**
5321
- * Enable context caching for supported providers.
5322
- *
5323
- * Can be called with:
5324
- * - No args: enables caching with defaults (`{ enabled: true }`)
5325
- * - A full config object: `withCaching({ enabled: true, scope: "system", ttl: "7200s" })`
5326
- *
5327
- * Provider behavior:
5328
- * - **Anthropic**: Caching is always-on by default via `cache_control` markers.
5329
- * Calling `withCaching()` explicitly is a no-op (it's already enabled).
5330
- * - **Gemini**: Creates an explicit cache via `caches.create()` for the configured scope.
5331
- * - **OpenAI**: Server-side automatic caching (no-op).
5332
- *
5333
- * @param config - Optional caching configuration
5334
- * @returns This builder for chaining
5335
- *
5336
- * @example
5337
- * ```typescript
5338
- * // Simple — enable with defaults
5339
- * LLMist.createAgent()
5340
- * .withModel("gemini:gemini-2.5-flash")
5341
- * .withCaching()
5342
- * .ask("Analyze this large codebase...");
5343
- *
5344
- * // Cache only system prompt with longer TTL
5345
- * LLMist.createAgent()
5346
- * .withModel("gemini:gemini-2.5-pro")
5347
- * .withCaching({ enabled: true, scope: "system", ttl: "7200s" })
5348
- * .ask("...");
5349
- * ```
4532
+ * Provider specific payload emitted at the same time as the text chunk. This is useful for debugging and tests.
5350
4533
  */
5351
- withCaching(config?: CachingConfig): this;
4534
+ rawEvent?: unknown;
4535
+ /** Thinking/reasoning content from reasoning models */
4536
+ thinking?: ThinkingChunk;
4537
+ }
4538
+ interface LLMStream extends AsyncIterable<LLMStreamChunk> {
4539
+ }
4540
+ type ProviderIdentifier = string;
4541
+ interface ModelDescriptor {
4542
+ provider: string;
4543
+ name: string;
4544
+ }
4545
+ declare class ModelIdentifierParser {
4546
+ private readonly defaultProvider;
4547
+ constructor(defaultProvider?: string);
4548
+ parse(identifier: string): ModelDescriptor;
4549
+ }
4550
+
4551
+ type GadgetClass = new (...args: unknown[]) => AbstractGadget;
4552
+ type GadgetOrClass = AbstractGadget | GadgetClass;
4553
+ declare class GadgetRegistry {
4554
+ private readonly gadgets;
5352
4555
  /**
5353
- * Explicitly disable context caching.
5354
- *
5355
- * For Anthropic, this removes `cache_control` markers from requests,
5356
- * opting out of prompt caching entirely.
4556
+ * Creates a registry from an array of gadget classes or instances,
4557
+ * or an object mapping names to gadgets.
5357
4558
  *
5358
- * @returns This builder for chaining
4559
+ * @param gadgets - Array of gadgets/classes or object with custom names
4560
+ * @returns New GadgetRegistry with all gadgets registered
5359
4561
  *
5360
4562
  * @example
5361
4563
  * ```typescript
5362
- * // Disable Anthropic's automatic caching
5363
- * LLMist.createAgent()
5364
- * .withModel("sonnet")
5365
- * .withoutCaching()
5366
- * .ask("...");
5367
- * ```
5368
- */
5369
- withoutCaching(): this;
5370
- /**
5371
- * Set subagent configuration overrides.
5372
- *
5373
- * Subagent gadgets (like BrowseWeb) can read these settings from ExecutionContext
5374
- * to inherit model and other options from the CLI configuration.
4564
+ * // From array of classes
4565
+ * const registry = GadgetRegistry.from([Calculator, Weather]);
5375
4566
  *
5376
- * @param config - Subagent configuration map keyed by gadget name
5377
- * @returns This builder for chaining
4567
+ * // From array of instances
4568
+ * const registry = GadgetRegistry.from([new Calculator(), new Weather()]);
5378
4569
  *
5379
- * @example
5380
- * ```typescript
5381
- * .withSubagentConfig({
5382
- * BrowseWeb: { model: "inherit", maxIterations: 20, headless: true },
5383
- * CodeAnalyzer: { model: "sonnet", maxIterations: 10 }
5384
- * })
4570
+ * // From object with custom names
4571
+ * const registry = GadgetRegistry.from({
4572
+ * calc: Calculator,
4573
+ * weather: new Weather({ apiKey: "..." })
4574
+ * });
5385
4575
  * ```
5386
4576
  */
5387
- withSubagentConfig(config: SubagentConfigMap): this;
4577
+ static from(gadgets: GadgetOrClass[] | Record<string, GadgetOrClass>): GadgetRegistry;
5388
4578
  /**
5389
- * Share parent agent's ExecutionTree for unified event visibility.
5390
- *
5391
- * When building a subagent inside a gadget, call this method to share the
5392
- * parent's ExecutionTree. This is the **single source of truth** for all
5393
- * execution events, enabling:
5394
- *
5395
- * - **Unified cost tracking** - All nested agent costs aggregate automatically
5396
- * - **Real-time visibility** - Parent's `tree.onAll()` subscribers see all events
5397
- * - **Depth tracking** - Events have correct `depth` (1 for child, 2 for grandchild, etc.)
5398
- * - **Parent linking** - Events have `parentId` pointing to spawning gadget
5399
- * - **Media aggregation** - Use `tree.getSubtreeMedia(nodeId)` after completion
5400
- *
5401
- * **Signal Forwarding** - When parent context includes a signal, it's automatically
5402
- * forwarded to the subagent for proper cancellation propagation.
5403
- *
5404
- * **Logger Inheritance** - When parent context includes a logger, it's inherited
5405
- * by the subagent for consistent structured logging.
4579
+ * Registers multiple gadgets at once from an array.
5406
4580
  *
5407
- * @param ctx - ExecutionContext passed to the gadget's execute() method
5408
- * @param depth - Nesting depth (default: 1 for direct child)
5409
- * @returns This builder for chaining
4581
+ * @param gadgets - Array of gadget instances or classes
4582
+ * @returns This registry for chaining
5410
4583
  *
5411
4584
  * @example
5412
4585
  * ```typescript
5413
- * // In a subagent gadget like BrowseWeb:
5414
- * execute: async (params, ctx) => {
5415
- * const agent = new AgentBuilder(client)
5416
- * .withModel(model)
5417
- * .withGadgets(Navigate, Click, Screenshot)
5418
- * .withParentContext(ctx) // <-- Shares parent's tree
5419
- * .ask(params.task);
5420
- *
5421
- * for await (const event of agent.run()) {
5422
- * // Events automatically flow through shared tree
5423
- * if (event.type === "text") {
5424
- * result = event.content;
5425
- * }
5426
- * }
5427
- *
5428
- * // After subagent completes, access aggregated data:
5429
- * const totalCost = ctx.tree?.getSubtreeCost(ctx.nodeId!);
5430
- * const allMedia = ctx.tree?.getSubtreeMedia(ctx.nodeId!);
5431
- * }
4586
+ * registry.registerMany([Calculator, Weather, Email]);
4587
+ * registry.registerMany([new Calculator(), new Weather()]);
5432
4588
  * ```
5433
4589
  */
4590
+ registerMany(gadgets: GadgetOrClass[]): this;
4591
+ register(name: string, gadget: AbstractGadget): void;
4592
+ registerByClass(gadget: AbstractGadget): void;
4593
+ get(name: string): AbstractGadget | undefined;
4594
+ has(name: string): boolean;
4595
+ getNames(): string[];
4596
+ getAll(): AbstractGadget[];
4597
+ unregister(name: string): boolean;
4598
+ clear(): void;
4599
+ }
4600
+
4601
+ /**
4602
+ * Context available to trailing message functions.
4603
+ * Provides iteration information for dynamic message generation.
4604
+ */
4605
+ type TrailingMessageContext = Pick<LLMCallControllerContext, "iteration" | "maxIterations" | "budget" | "totalCost">;
4606
+ /**
4607
+ * Trailing message can be a static string or a function that generates the message.
4608
+ * The function receives context about the current iteration.
4609
+ */
4610
+ type TrailingMessage = string | ((ctx: TrailingMessageContext) => string);
4611
+
4612
+ /**
4613
+ * Message for conversation history.
4614
+ * User messages can be text (string) or multimodal (ContentPart[]).
4615
+ */
4616
+ type HistoryMessage = {
4617
+ user: string | ContentPart[];
4618
+ } | {
4619
+ assistant: string;
4620
+ } | {
4621
+ system: string;
4622
+ };
4623
+
4624
+ /**
4625
+ * Event handler sugar for cleaner event processing.
4626
+ *
4627
+ * Instead of verbose if/else chains, use named handlers
4628
+ * for each event type.
4629
+ *
4630
+ * @example
4631
+ * ```typescript
4632
+ * await agent.runWith({
4633
+ * onText: (content) => console.log("LLM:", content),
4634
+ * onGadgetResult: (result) => console.log("Result:", result.result),
4635
+ * });
4636
+ * ```
4637
+ */
4638
+
4639
+ /**
4640
+ * Named event handlers for different event types.
4641
+ */
4642
+ interface EventHandlers {
4643
+ /** Called when text is generated by the LLM */
4644
+ onText?: (content: string) => void | Promise<void>;
4645
+ /** Called when a gadget is about to be executed */
4646
+ onGadgetCall?: (call: {
4647
+ gadgetName: string;
4648
+ invocationId: string;
4649
+ parameters?: Record<string, unknown>;
4650
+ parametersRaw: string;
4651
+ dependencies: string[];
4652
+ }) => void | Promise<void>;
4653
+ /** Called when a gadget execution completes */
4654
+ onGadgetResult?: (result: {
4655
+ gadgetName: string;
4656
+ invocationId: string;
4657
+ result?: string;
4658
+ error?: string;
4659
+ parameters: Record<string, unknown>;
4660
+ }) => void | Promise<void>;
4661
+ /** Called when human input is required */
4662
+ onHumanInputRequired?: (data: {
4663
+ question: string;
4664
+ gadgetName: string;
4665
+ }) => void | Promise<void>;
4666
+ /** Called for any other event type */
4667
+ onOther?: (event: StreamEvent) => void | Promise<void>;
4668
+ }
4669
+ /**
4670
+ * Helper to run an agent with named event handlers.
4671
+ *
4672
+ * @param agentGenerator - Agent's run() async generator
4673
+ * @param handlers - Named event handlers
4674
+ *
4675
+ * @example
4676
+ * ```typescript
4677
+ * await runWithHandlers(agent.run(), {
4678
+ * onText: (text) => console.log("LLM:", text),
4679
+ * onGadgetResult: (result) => console.log("Result:", result.result),
4680
+ * });
4681
+ * ```
4682
+ */
4683
+ declare function runWithHandlers(agentGenerator: AsyncGenerator<StreamEvent>, handlers: EventHandlers): Promise<void>;
4684
+ /**
4685
+ * Helper to collect events by type.
4686
+ *
4687
+ * @param agentGenerator - Agent's run() async generator
4688
+ * @param collect - Object specifying which event types to collect
4689
+ * @returns Object with collected events
4690
+ *
4691
+ * @example
4692
+ * ```typescript
4693
+ * const { text, gadgetResults } = await collectEvents(agent.run(), {
4694
+ * text: true,
4695
+ * gadgetResults: true,
4696
+ * });
4697
+ *
4698
+ * console.log("Full response:", text.join(""));
4699
+ * console.log("Gadget calls:", gadgetResults.length);
4700
+ * ```
4701
+ */
4702
+ declare function collectEvents(agentGenerator: AsyncGenerator<StreamEvent>, collect: {
4703
+ text?: boolean;
4704
+ gadgetCalls?: boolean;
4705
+ gadgetResults?: boolean;
4706
+ }): Promise<{
4707
+ text: string[];
4708
+ gadgetCalls: Array<{
4709
+ gadgetName: string;
4710
+ parameters: Record<string, unknown>;
4711
+ }>;
4712
+ gadgetResults: Array<{
4713
+ gadgetName: string;
4714
+ result?: string;
4715
+ error?: string;
4716
+ parameters: Record<string, unknown>;
4717
+ }>;
4718
+ }>;
4719
+ /**
4720
+ * Helper to collect only text from an agent run.
4721
+ *
4722
+ * @param agentGenerator - Agent's run() async generator
4723
+ * @returns Combined text response
4724
+ *
4725
+ * @example
4726
+ * ```typescript
4727
+ * const response = await collectText(agent.run());
4728
+ * console.log(response);
4729
+ * ```
4730
+ */
4731
+ declare function collectText(agentGenerator: AsyncGenerator<StreamEvent>): Promise<string>;
4732
+
4733
+ /**
4734
+ * Fluent builder for creating agents with delightful DX.
4735
+ */
4736
+
4737
+ /**
4738
+ * Fluent builder for creating agents.
4739
+ *
4740
+ * Provides a chainable API for configuring and creating agents,
4741
+ * making the code more expressive and easier to read.
4742
+ */
4743
+ declare class AgentBuilder {
4744
+ private core;
4745
+ private gadgets;
4746
+ private retry;
4747
+ private subagents;
4748
+ private policies;
4749
+ constructor(client?: LLMist);
4750
+ /** Set the model to use. Supports aliases like "sonnet", "flash". */
4751
+ withModel(model: string): this;
4752
+ /** Set the system prompt. */
4753
+ withSystem(prompt: string): this;
4754
+ /** Set the temperature (0-1). */
4755
+ withTemperature(temperature: number): this;
4756
+ /** Set maximum iterations. */
4757
+ withMaxIterations(max: number): this;
4758
+ /** Set the budget limit in USD. */
4759
+ withBudget(amountUSD: number): this;
4760
+ /** Set logger instance. */
4761
+ withLogger(logger: Logger<ILogObj>): this;
4762
+ /** Add hooks for agent lifecycle events. */
4763
+ withHooks(hooks: AgentHooks): this;
4764
+ /** Configure custom prompts for gadget system messages. */
4765
+ withPromptTemplateConfig(config: PromptTemplateConfig): this;
4766
+ /** Add gadgets (classes or instances). */
4767
+ withGadgets(...gadgets: GadgetOrClass[]): this;
4768
+ /** Add conversation history messages. */
4769
+ withHistory(messages: HistoryMessage[]): this;
4770
+ /** Add a single message to the conversation history. */
4771
+ addMessage(message: HistoryMessage): this;
4772
+ /** Clear any previously set conversation history. */
4773
+ clearHistory(): this;
4774
+ /** Continue conversation from a previous agent's history. */
4775
+ continueFrom(agent: Agent): this;
4776
+ /** Set the human input handler for interactive conversations. */
4777
+ onHumanInput(handler: (question: string) => Promise<string>): this;
4778
+ /** Set custom gadget marker prefix. */
4779
+ withGadgetStartPrefix(prefix: string): this;
4780
+ /** Set custom gadget marker suffix. */
4781
+ withGadgetEndPrefix(suffix: string): this;
4782
+ /** Set custom argument prefix for block format parameters. */
4783
+ withGadgetArgPrefix(prefix: string): this;
4784
+ /** Set the text-only handler strategy. */
4785
+ withTextOnlyHandler(handler: TextOnlyHandler): this;
4786
+ /** Set the handler for text content that appears alongside gadget calls. */
4787
+ withTextWithGadgetsHandler(handler: {
4788
+ gadgetName: string;
4789
+ parameterMapping: (text: string) => Record<string, unknown>;
4790
+ resultMapping?: (text: string) => string;
4791
+ }): this;
4792
+ /** Set default timeout for gadget execution. */
4793
+ withDefaultGadgetTimeout(timeoutMs: number): this;
4794
+ /** Set the gadget execution mode ('parallel' or 'sequential'). */
4795
+ withGadgetExecutionMode(mode: GadgetExecutionMode): this;
4796
+ /** Set the maximum number of gadgets to execute per LLM response. */
4797
+ withMaxGadgetsPerResponse(max: number): this;
4798
+ /** Enable or disable gadget output limiting. */
4799
+ withGadgetOutputLimit(enabled: boolean): this;
4800
+ /** Set the maximum gadget output as a percentage of the context window. */
4801
+ withGadgetOutputLimitPercent(percent: number): this;
4802
+ /** Configure context compaction. */
4803
+ withCompaction(config: CompactionConfig): this;
4804
+ /** Disable context compaction. */
4805
+ withoutCompaction(): this;
4806
+ /** Configure retry behavior for LLM API calls. */
4807
+ withRetry(config: RetryConfig): this;
4808
+ /** Disable automatic retry for LLM API calls. */
4809
+ withoutRetry(): this;
4810
+ /** Configure proactive rate limiting to prevent rate limit errors. */
4811
+ withRateLimits(config: RateLimitConfig): this;
4812
+ /** Set an abort signal for cancelling requests mid-flight. */
4813
+ withSignal(signal: AbortSignal): this;
4814
+ /** Enable reasoning/thinking mode for reasoning-capable models. */
4815
+ withReasoning(config?: ReasoningConfig | ReasoningEffort): this;
4816
+ /** Explicitly disable reasoning for this agent. */
4817
+ withoutReasoning(): this;
4818
+ /** Enable context caching for supported providers. */
4819
+ withCaching(config?: CachingConfig): this;
4820
+ /** Explicitly disable context caching. */
4821
+ withoutCaching(): this;
4822
+ /** Set subagent configuration overrides. */
4823
+ withSubagentConfig(config: SubagentConfigMap): this;
4824
+ /** Share parent agent's ExecutionTree for unified event visibility. */
5434
4825
  withParentContext(ctx: ExecutionContext, depth?: number): this;
5435
- /**
5436
- * Add an ephemeral trailing message that appears at the end of each LLM request.
5437
- *
5438
- * The message is NOT persisted to conversation history - it only appears in the
5439
- * current LLM call. This is useful for injecting context-specific instructions
5440
- * or reminders without polluting the conversation history.
5441
- *
5442
- * @param message - Static string or function that generates the message
5443
- * @returns This builder for chaining
5444
- *
5445
- * @example
5446
- * ```typescript
5447
- * // Static message
5448
- * .withTrailingMessage("Always respond in JSON format.")
5449
- *
5450
- * // Dynamic message based on iteration
5451
- * .withTrailingMessage((ctx) =>
5452
- * `[Iteration ${ctx.iteration}/${ctx.maxIterations}] Stay focused on the task.`
5453
- * )
5454
- * ```
5455
- */
4826
+ /** Add an ephemeral trailing message that appears at the end of each LLM request. */
5456
4827
  withTrailingMessage(message: TrailingMessage): this;
5457
- /**
5458
- * Add a synthetic gadget call to the conversation history.
5459
- *
5460
- * This is useful for in-context learning - showing the LLM what "past self"
5461
- * did correctly so it mimics the pattern. The call is formatted with proper
5462
- * markers and parameter format, including the invocation ID so the LLM can
5463
- * reference previous calls when building dependencies.
5464
- *
5465
- * @param gadgetName - Name of the gadget
5466
- * @param parameters - Parameters passed to the gadget
5467
- * @param result - Result returned by the gadget
5468
- * @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
5469
- * @returns This builder for chaining
5470
- *
5471
- * @example
5472
- * ```typescript
5473
- * .withSyntheticGadgetCall(
5474
- * 'TellUser',
5475
- * {
5476
- * message: '👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
5477
- * done: false,
5478
- * type: 'info'
5479
- * },
5480
- * 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
5481
- * 'gc_1'
5482
- * )
5483
- * ```
5484
- */
4828
+ /** Add a synthetic gadget call to the conversation history for in-context learning. */
5485
4829
  withSyntheticGadgetCall(gadgetName: string, parameters: Record<string, unknown>, result: string, invocationId: string): this;
5486
- /**
5487
- * Compose the final hooks, including trailing message injection if configured.
5488
- *
5489
- * Note: Subagent event visibility is now handled entirely by the ExecutionTree.
5490
- * When a subagent uses withParentContext(ctx), it shares the parent's tree,
5491
- * and all events are automatically visible to tree subscribers (like the TUI).
5492
- *
5493
- * Environment-based file logging (via LLMIST_LOG_RAW_DIRECTORY) is automatically
5494
- * injected if the env var is set. User-provided hooks take precedence.
5495
- */
5496
4830
  private composeHooks;
5497
- /**
5498
- * Format parameters as block format with JSON Pointer paths.
5499
- */
5500
- private formatBlockParameters;
5501
- /**
5502
- * Build and create the agent with the given user prompt.
5503
- * Returns the Agent instance ready to run.
5504
- *
5505
- * @param userPrompt - User's question or request
5506
- * @returns Configured Agent instance
5507
- *
5508
- * @example
5509
- * ```typescript
5510
- * const agent = await LLMist.createAgent()
5511
- * .withModel("sonnet")
5512
- * .withGadgets(Calculator)
5513
- * .ask("What is 2+2?");
5514
- *
5515
- * for await (const event of agent.run()) {
5516
- * // handle events
5517
- * }
5518
- * ```
5519
- */
5520
- /**
5521
- * Build AgentOptions with the given user prompt.
5522
- * Centralizes options construction for ask(), askWithImage(), and askWithContent().
5523
- */
5524
4831
  private buildAgentOptions;
4832
+ /** Create agent and start with a user prompt. */
5525
4833
  ask(userPrompt: string): Agent;
5526
- /**
5527
- * Build and create the agent with a multimodal user prompt (text + image).
5528
- * Returns the Agent instance ready to run.
5529
- *
5530
- * @param textPrompt - Text prompt describing what to do with the image
5531
- * @param imageData - Image data (Buffer, Uint8Array, or base64 string)
5532
- * @param mimeType - Optional MIME type (auto-detected if not provided)
5533
- * @returns Configured Agent instance
5534
- *
5535
- * @example
5536
- * ```typescript
5537
- * const agent = LLMist.createAgent()
5538
- * .withModel("gpt-4o")
5539
- * .withSystem("You analyze images")
5540
- * .askWithImage(
5541
- * "What's in this image?",
5542
- * await fs.readFile("photo.jpg")
5543
- * );
5544
- *
5545
- * for await (const event of agent.run()) {
5546
- * // handle events
5547
- * }
5548
- * ```
5549
- */
4834
+ /** Create agent with multimodal input (text + image). */
5550
4835
  askWithImage(textPrompt: string, imageData: Buffer | Uint8Array | string, mimeType?: ImageMimeType): Agent;
5551
- /**
5552
- * Build and return an Agent configured with multimodal content.
5553
- * More flexible than askWithImage - accepts any combination of content parts.
5554
- *
5555
- * @param content - Array of content parts (text, images, audio)
5556
- * @returns A configured Agent ready for execution
5557
- *
5558
- * @example
5559
- * ```typescript
5560
- * import { text, imageFromBuffer, audioFromBuffer } from "llmist";
5561
- *
5562
- * const agent = LLMist.createAgent()
5563
- * .withModel("gemini:gemini-2.5-flash")
5564
- * .askWithContent([
5565
- * text("Describe this image and transcribe the audio:"),
5566
- * imageFromBuffer(imageData),
5567
- * audioFromBuffer(audioData),
5568
- * ]);
5569
- *
5570
- * for await (const event of agent.run()) {
5571
- * // handle events
5572
- * }
5573
- * ```
5574
- */
4836
+ /** Create agent with flexible multimodal content parts. */
5575
4837
  askWithContent(content: ContentPart[]): Agent;
5576
- /**
5577
- * Build, run, and collect only the text response.
5578
- * Convenient for simple queries where you just want the final answer.
5579
- *
5580
- * @param userPrompt - User's question or request
5581
- * @returns Promise resolving to the complete text response
5582
- *
5583
- * @example
5584
- * ```typescript
5585
- * const answer = await LLMist.createAgent()
5586
- * .withModel("gpt4-mini")
5587
- * .withGadgets(Calculator)
5588
- * .askAndCollect("What is 42 * 7?");
5589
- *
5590
- * console.log(answer); // "294"
5591
- * ```
5592
- */
4838
+ /** Run agent and collect text response. */
5593
4839
  askAndCollect(userPrompt: string): Promise<string>;
5594
- /**
5595
- * Build and run with event handlers.
5596
- * Combines agent creation and event handling in one call.
5597
- *
5598
- * @param userPrompt - User's question or request
5599
- * @param handlers - Event handlers
5600
- *
5601
- * @example
5602
- * ```typescript
5603
- * await LLMist.createAgent()
5604
- * .withModel("sonnet")
5605
- * .withGadgets(Calculator)
5606
- * .askWith("What is 2+2?", {
5607
- * onText: (text) => console.log("LLM:", text),
5608
- * onGadgetResult: (result) => console.log("Result:", result.result),
5609
- * });
5610
- * ```
5611
- */
4840
+ /** Run agent with event handlers. */
5612
4841
  askWith(userPrompt: string, handlers: EventHandlers): Promise<void>;
5613
- /**
5614
- * Build the agent without a user prompt.
5615
- *
5616
- * Returns an Agent instance that can be inspected (e.g., check registered gadgets)
5617
- * but cannot be run without first calling .ask(prompt).
5618
- *
5619
- * This is useful for:
5620
- * - Testing: Inspect the registry, configuration, etc.
5621
- * - Advanced use cases: Build agent configuration separately from execution
5622
- *
5623
- * @returns Configured Agent instance (without user prompt)
5624
- *
5625
- * @example
5626
- * ```typescript
5627
- * // Build agent for inspection
5628
- * const agent = new AgentBuilder()
5629
- * .withModel("sonnet")
5630
- * .withGadgets(Calculator, Weather)
5631
- * .build();
5632
- *
5633
- * // Inspect registered gadgets
5634
- * console.log(agent.getRegistry().getNames()); // ['Calculator', 'Weather']
5635
- *
5636
- * // Note: Calling agent.run() will throw an error
5637
- * // Use .ask(prompt) instead if you want to run the agent
5638
- * ```
5639
- */
4842
+ /** Build agent without a prompt (useful for testing/inspection). */
5640
4843
  build(): Agent;
5641
4844
  }
5642
4845
 
@@ -6244,24 +5447,167 @@ interface IConversationManager {
6244
5447
  * Used by compaction to update history after compression.
6245
5448
  * @param newHistory - The compacted history messages to replace with
6246
5449
  */
6247
- replaceHistory(newHistory: LLMMessage[]): void;
5450
+ replaceHistory(newHistory: LLMMessage[]): void;
5451
+ /**
5452
+ * Gets full conversation history including initial messages and runtime history.
5453
+ * Used for REPL session continuation - returns everything except base (system) messages.
5454
+ * This combines:
5455
+ * - initialMessages: History from previous sessions (set via withHistory())
5456
+ * - historyBuilder: Messages from the current session
5457
+ */
5458
+ getConversationHistory(): LLMMessage[];
5459
+ }
5460
+
5461
+ /**
5462
+ * Storage for large gadget outputs that exceed the configured limit.
5463
+ *
5464
+ * When a gadget returns more data than the configured limit, the output
5465
+ * is stored here and can be browsed later using GadgetOutputViewer.
5466
+ */
5467
+ /**
5468
+ * Metadata and content for a stored gadget output.
5469
+ */
5470
+ interface StoredOutput {
5471
+ /** Unique identifier (e.g., "Search_d34db33f") */
5472
+ id: string;
5473
+ /** Name of the gadget that produced this output */
5474
+ gadgetName: string;
5475
+ /** Full output content */
5476
+ content: string;
5477
+ /** Size in bytes */
5478
+ byteSize: number;
5479
+ /** Number of lines */
5480
+ lineCount: number;
5481
+ /** When the output was stored */
5482
+ timestamp: Date;
5483
+ }
5484
+ /**
5485
+ * In-memory store for large gadget outputs.
5486
+ *
5487
+ * Outputs are stored with generated IDs in the format `{GadgetName}_{hex8}`.
5488
+ * The store is tied to an agent run and cleared when the agent completes.
5489
+ *
5490
+ * @example
5491
+ * ```typescript
5492
+ * const store = new GadgetOutputStore();
5493
+ * const id = store.store("Search", largeOutput);
5494
+ * // id = "Search_a1b2c3d4"
5495
+ *
5496
+ * const stored = store.get(id);
5497
+ * console.log(stored?.lineCount); // 4200
5498
+ * ```
5499
+ */
5500
+ declare class GadgetOutputStore {
5501
+ private outputs;
5502
+ /**
5503
+ * Store a gadget output and return its ID.
5504
+ *
5505
+ * @param gadgetName - Name of the gadget that produced the output
5506
+ * @param content - Full output content to store
5507
+ * @returns Generated ID for retrieving the output later
5508
+ */
5509
+ store(gadgetName: string, content: string): string;
5510
+ /**
5511
+ * Retrieve a stored output by ID.
5512
+ *
5513
+ * @param id - The output ID (e.g., "Search_d34db33f")
5514
+ * @returns The stored output or undefined if not found
5515
+ */
5516
+ get(id: string): StoredOutput | undefined;
5517
+ /**
5518
+ * Check if an output exists.
5519
+ *
5520
+ * @param id - The output ID to check
5521
+ * @returns True if the output exists
5522
+ */
5523
+ has(id: string): boolean;
5524
+ /**
5525
+ * Get all stored output IDs.
5526
+ *
5527
+ * @returns Array of output IDs
5528
+ */
5529
+ getIds(): string[];
5530
+ /**
5531
+ * Get the number of stored outputs.
5532
+ */
5533
+ get size(): number;
5534
+ /**
5535
+ * Clear all stored outputs.
5536
+ * Called when the agent run completes.
5537
+ */
5538
+ clear(): void;
5539
+ /**
5540
+ * Generate a unique ID for a stored output.
5541
+ * Format: {GadgetName}_{8 hex chars}
5542
+ */
5543
+ private generateId;
5544
+ }
5545
+
5546
+ /**
5547
+ * OutputLimitManager - Manages gadget output size limiting.
5548
+ *
5549
+ * Calculates character limits from model context windows, registers
5550
+ * GadgetOutputViewer when enabled, and chains the output limiter
5551
+ * interceptor with user-provided hooks.
5552
+ */
5553
+
5554
+ /**
5555
+ * Configuration for output limiting.
5556
+ */
5557
+ interface OutputLimitConfig {
5558
+ /** Whether output limiting is enabled (default: true) */
5559
+ enabled?: boolean;
5560
+ /** Max gadget output as % of model context window (default: 15) */
5561
+ limitPercent?: number;
5562
+ }
5563
+
5564
+ /**
5565
+ * Agent: Lean orchestrator using the clean hooks architecture.
5566
+ *
5567
+ * The Agent delegates ALL stream processing and hook coordination to StreamProcessor,
5568
+ * making it a simple loop orchestrator with clear responsibilities.
5569
+ */
5570
+
5571
+ /**
5572
+ * Configuration for the execution tree context (shared tree model with subagents).
5573
+ */
5574
+ interface TreeConfig {
5575
+ /**
5576
+ * Shared execution tree for tracking all LLM calls and gadget executions.
5577
+ * If provided (by a parent subagent), nodes are added to this tree.
5578
+ * If not provided, the Agent creates its own tree.
5579
+ */
5580
+ tree?: ExecutionTree;
5581
+ /**
5582
+ * Parent node ID in the tree (when this agent is a subagent).
5583
+ * Used to set parentId on all nodes created by this agent.
5584
+ */
5585
+ parentNodeId?: NodeId;
5586
+ /**
5587
+ * Base depth for nodes created by this agent.
5588
+ * Root agents use 0; subagents use (parentDepth + 1).
5589
+ */
5590
+ baseDepth?: number;
6248
5591
  /**
6249
- * Gets full conversation history including initial messages and runtime history.
6250
- * Used for REPL session continuation - returns everything except base (system) messages.
6251
- * This combines:
6252
- * - initialMessages: History from previous sessions (set via withHistory())
6253
- * - historyBuilder: Messages from the current session
5592
+ * Parent agent's observer hooks for subagent visibility.
5593
+ *
5594
+ * When a subagent is created with withParentContext(ctx), these observers
5595
+ * are also called for gadget events (in addition to the subagent's own hooks),
5596
+ * enabling the parent to observe subagent gadget activity.
6254
5597
  */
6255
- getConversationHistory(): LLMMessage[];
5598
+ parentObservers?: Observers;
6256
5599
  }
6257
-
6258
5600
  /**
6259
- * Agent: Lean orchestrator using the clean hooks architecture.
6260
- *
6261
- * The Agent delegates ALL stream processing and hook coordination to StreamProcessor,
6262
- * making it a simple loop orchestrator with clear responsibilities.
5601
+ * Configuration for custom gadget block format prefixes.
6263
5602
  */
6264
-
5603
+ interface PrefixConfig {
5604
+ /** Custom gadget start prefix */
5605
+ gadgetStartPrefix?: string;
5606
+ /** Custom gadget end prefix */
5607
+ gadgetEndPrefix?: string;
5608
+ /** Custom gadget argument prefix for block format parameters */
5609
+ gadgetArgPrefix?: string;
5610
+ }
6265
5611
  /**
6266
5612
  * Configuration options for the Agent.
6267
5613
  */
@@ -6288,12 +5634,10 @@ interface AgentOptions {
6288
5634
  hooks?: AgentHooks;
6289
5635
  /** Callback for requesting human input during execution */
6290
5636
  requestHumanInput?: (question: string) => Promise<string>;
6291
- /** Custom gadget start prefix */
6292
- gadgetStartPrefix?: string;
6293
- /** Custom gadget end prefix */
6294
- gadgetEndPrefix?: string;
6295
- /** Custom gadget argument prefix for block format parameters */
6296
- gadgetArgPrefix?: string;
5637
+ /**
5638
+ * Gadget prefix configuration (start/end/arg prefixes for block format).
5639
+ */
5640
+ prefixConfig?: PrefixConfig;
6297
5641
  /** Initial messages. User messages support multimodal content. */
6298
5642
  initialMessages?: Array<{
6299
5643
  role: "system" | "user" | "assistant";
@@ -6319,10 +5663,10 @@ interface AgentOptions {
6319
5663
  gadgetExecutionMode?: GadgetExecutionMode;
6320
5664
  /** Custom prompt configuration for gadget system prompts */
6321
5665
  promptConfig?: PromptTemplateConfig;
6322
- /** Enable gadget output limiting (default: true) */
6323
- gadgetOutputLimit?: boolean;
6324
- /** Max gadget output as % of model context window (default: 15) */
6325
- gadgetOutputLimitPercent?: number;
5666
+ /**
5667
+ * Gadget output limit configuration.
5668
+ */
5669
+ outputLimitConfig?: OutputLimitConfig;
6326
5670
  /** Context compaction configuration (enabled by default) */
6327
5671
  compactionConfig?: CompactionConfig;
6328
5672
  /** Retry configuration for LLM API calls (enabled by default) */
@@ -6340,29 +5684,9 @@ interface AgentOptions {
6340
5684
  /** Maximum gadgets to execute per LLM response (0 = unlimited) */
6341
5685
  maxGadgetsPerResponse?: number;
6342
5686
  /**
6343
- * Shared execution tree for tracking all LLM calls and gadget executions.
6344
- * If provided (by a parent subagent), nodes are added to this tree.
6345
- * If not provided, the Agent creates its own tree.
6346
- */
6347
- parentTree?: ExecutionTree;
6348
- /**
6349
- * Parent node ID in the tree (when this agent is a subagent).
6350
- * Used to set parentId on all nodes created by this agent.
6351
- */
6352
- parentNodeId?: NodeId;
6353
- /**
6354
- * Base depth for nodes created by this agent.
6355
- * Root agents use 0; subagents use (parentDepth + 1).
6356
- */
6357
- baseDepth?: number;
6358
- /**
6359
- * Parent agent's observer hooks for subagent visibility.
6360
- *
6361
- * When a subagent is created with withParentContext(ctx), these observers
6362
- * are also called for gadget events (in addition to the subagent's own hooks),
6363
- * enabling the parent to observe subagent gadget activity.
5687
+ * Execution tree configuration (shared tree model with subagents).
6364
5688
  */
6365
- parentObservers?: Observers;
5689
+ treeConfig?: TreeConfig;
6366
5690
  /**
6367
5691
  * Shared rate limit tracker from parent agent.
6368
5692
  *
@@ -6403,19 +5727,11 @@ declare class Agent {
6403
5727
  private readonly hooks;
6404
5728
  private readonly conversation;
6405
5729
  private readonly registry;
6406
- private readonly gadgetStartPrefix?;
6407
- private readonly gadgetEndPrefix?;
6408
- private readonly gadgetArgPrefix?;
6409
- private readonly requestHumanInput?;
6410
- private readonly textOnlyHandler;
6411
- private readonly textWithGadgetsHandler?;
6412
- private readonly defaultGadgetTimeoutMs?;
6413
- private readonly gadgetExecutionMode;
5730
+ private readonly prefixConfig?;
5731
+ private readonly conversationUpdater;
6414
5732
  private readonly defaultMaxTokens?;
6415
5733
  private hasUserPrompt;
6416
- private readonly outputStore;
6417
- private readonly outputLimitEnabled;
6418
- private readonly outputLimitCharLimit;
5734
+ private readonly outputLimitManager;
6419
5735
  private readonly compactionManager?;
6420
5736
  private readonly mediaStore;
6421
5737
  private readonly signal?;
@@ -6423,17 +5739,13 @@ declare class Agent {
6423
5739
  private readonly caching?;
6424
5740
  private readonly retryConfig;
6425
5741
  private readonly rateLimitTracker?;
6426
- private readonly agentContextConfig;
6427
- private readonly subagentConfig?;
6428
- private readonly maxGadgetsPerResponse;
6429
- private syntheticInvocationCounter;
6430
5742
  private readonly completedInvocationIds;
6431
5743
  private readonly failedInvocationIds;
6432
5744
  private readonly pendingUserMessages;
6433
5745
  private readonly tree;
6434
5746
  private readonly parentNodeId;
6435
- private readonly baseDepth;
6436
- private readonly parentObservers?;
5747
+ private readonly streamProcessorFactory;
5748
+ private readonly llmCallLifecycle;
6437
5749
  /**
6438
5750
  * Creates a new Agent instance.
6439
5751
  * @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
@@ -6614,6 +5926,16 @@ declare class Agent {
6614
5926
  * @throws {Error} If no user prompt was provided (when using build() without ask())
6615
5927
  */
6616
5928
  run(): AsyncGenerator<StreamEvent>;
5929
+ /**
5930
+ * Execute a single LLM call attempt with full retry orchestration.
5931
+ *
5932
+ * Delegates all retry logic to RetryOrchestrator, then propagates the accumulated
5933
+ * invocation IDs back to the agent's cross-iteration tracking sets.
5934
+ *
5935
+ * Yields stream events in real-time and returns the final stream completion metadata
5936
+ * along with accumulated tracking state from the final successful attempt only.
5937
+ */
5938
+ private executeWithRetry;
6617
5939
  /**
6618
5940
  * Create LLM stream with proactive rate limit protection.
6619
5941
  *
@@ -6622,30 +5944,20 @@ declare class Agent {
6622
5944
  */
6623
5945
  private createStream;
6624
5946
  /**
6625
- * Simple sleep utility for rate limit delays.
6626
- */
6627
- private sleep;
6628
- /**
6629
- * Handle LLM error through controller.
6630
- */
6631
- private handleLLMError;
6632
- /**
6633
- * Handle text-only response (no gadgets called).
5947
+ * Factory method for constructing a StreamProcessor for a given iteration.
5948
+ *
5949
+ * Delegates to StreamProcessorFactory, which encapsulates all static
5950
+ * StreamProcessor configuration. Cross-iteration mutable state is passed here.
6634
5951
  */
6635
- private handleTextOnlyResponse;
5952
+ private createStreamProcessor;
6636
5953
  /**
6637
- * Safely execute an observer, catching and logging any errors.
5954
+ * Simple sleep utility for rate limit delays.
6638
5955
  */
6639
- private safeObserve;
5956
+ private sleep;
6640
5957
  /**
6641
5958
  * Resolve max tokens from model catalog.
6642
5959
  */
6643
5960
  private resolveMaxTokensFromCatalog;
6644
- /**
6645
- * Chain the output limiter interceptor with user-provided hooks.
6646
- * The limiter runs first, then chains to any user interceptor.
6647
- */
6648
- private chainOutputLimiterWithUserHooks;
6649
5961
  /**
6650
5962
  * Check abort signal and notify observers if aborted.
6651
5963
  * @returns true if agent should terminate
@@ -6656,43 +5968,6 @@ declare class Agent {
6656
5968
  * @returns compaction stream event if compaction occurred, null otherwise
6657
5969
  */
6658
5970
  private checkAndPerformCompaction;
6659
- /**
6660
- * Resolve reasoning configuration with auto-enable logic.
6661
- *
6662
- * Priority: explicit config > auto-enable for reasoning models > undefined
6663
- * When a model has `features.reasoning: true` and no explicit config is set,
6664
- * reasoning is automatically enabled at "medium" effort.
6665
- */
6666
- private resolveReasoningConfig;
6667
- /**
6668
- * Resolve caching configuration.
6669
- *
6670
- * Priority: explicit config > default enabled (preserves Anthropic's existing behavior)
6671
- * Default is `{ enabled: true }` which means:
6672
- * - Anthropic: `cache_control` markers are added (existing behavior preserved)
6673
- * - Gemini: Cache manager is consulted but skips if no explicit config was set
6674
- * - OpenAI: No-op (server-side automatic)
6675
- */
6676
- private resolveCachingConfig;
6677
- /**
6678
- * Prepare LLM call options, create tree node, and process beforeLLMCall controller.
6679
- * @returns options, node ID, and optional skipWithSynthetic response if controller wants to skip
6680
- */
6681
- private prepareLLMCall;
6682
- /**
6683
- * Calculate cost and complete LLM call in execution tree.
6684
- * Also records usage to rate limit tracker for proactive throttling.
6685
- */
6686
- private completeLLMCallInTree;
6687
- /**
6688
- * Process afterLLMCall controller and return modified final message.
6689
- */
6690
- private processAfterLLMCallController;
6691
- /**
6692
- * Update conversation history with gadget results or text-only response.
6693
- * @returns true if loop should break (text-only handler requested termination)
6694
- */
6695
- private updateConversationWithResults;
6696
5971
  /**
6697
5972
  * Run agent with named event handlers (syntactic sugar).
6698
5973
  *
@@ -7143,6 +6418,8 @@ declare class HookPresets {
7143
6418
  /**
7144
6419
  * Tracks cumulative token usage across all LLM calls.
7145
6420
  *
6421
+ * @public
6422
+ *
7146
6423
  * **Output:**
7147
6424
  * - Per-call token count with 📊 emoji
7148
6425
  * - Cumulative total across all calls
@@ -7320,6 +6597,8 @@ declare class HookPresets {
7320
6597
  /**
7321
6598
  * Logs detailed error information for debugging and troubleshooting.
7322
6599
  *
6600
+ * @public
6601
+ *
7323
6602
  * **Output:**
7324
6603
  * - LLM errors with ❌ emoji, including model and recovery status
7325
6604
  * - Gadget errors with full context (parameters, error message)
@@ -7381,6 +6660,8 @@ declare class HookPresets {
7381
6660
  /**
7382
6661
  * Tracks context compaction events.
7383
6662
  *
6663
+ * @public
6664
+ *
7384
6665
  * **Output:**
7385
6666
  * - Compaction events with 🗜️ emoji
7386
6667
  * - Strategy name, tokens before/after, and savings
@@ -7474,6 +6755,8 @@ declare class HookPresets {
7474
6755
  /**
7475
6756
  * Returns empty hook configuration for clean output without any logging.
7476
6757
  *
6758
+ * @public
6759
+ *
7477
6760
  * **Output:**
7478
6761
  * - None. Returns {} (empty object).
7479
6762
  *
@@ -7600,6 +6883,8 @@ declare class HookPresets {
7600
6883
  /**
7601
6884
  * Composite preset combining logging, timing, tokenTracking, and errorLogging.
7602
6885
  *
6886
+ * @public
6887
+ *
7603
6888
  * This is the recommended preset for development and initial production deployments,
7604
6889
  * providing comprehensive observability with a single method call.
7605
6890
  *
@@ -7842,91 +7127,6 @@ declare class ConversationManager implements IConversationManager {
7842
7127
  getConversationHistory(): LLMMessage[];
7843
7128
  }
7844
7129
 
7845
- /**
7846
- * Storage for large gadget outputs that exceed the configured limit.
7847
- *
7848
- * When a gadget returns more data than the configured limit, the output
7849
- * is stored here and can be browsed later using GadgetOutputViewer.
7850
- */
7851
- /**
7852
- * Metadata and content for a stored gadget output.
7853
- */
7854
- interface StoredOutput {
7855
- /** Unique identifier (e.g., "Search_d34db33f") */
7856
- id: string;
7857
- /** Name of the gadget that produced this output */
7858
- gadgetName: string;
7859
- /** Full output content */
7860
- content: string;
7861
- /** Size in bytes */
7862
- byteSize: number;
7863
- /** Number of lines */
7864
- lineCount: number;
7865
- /** When the output was stored */
7866
- timestamp: Date;
7867
- }
7868
- /**
7869
- * In-memory store for large gadget outputs.
7870
- *
7871
- * Outputs are stored with generated IDs in the format `{GadgetName}_{hex8}`.
7872
- * The store is tied to an agent run and cleared when the agent completes.
7873
- *
7874
- * @example
7875
- * ```typescript
7876
- * const store = new GadgetOutputStore();
7877
- * const id = store.store("Search", largeOutput);
7878
- * // id = "Search_a1b2c3d4"
7879
- *
7880
- * const stored = store.get(id);
7881
- * console.log(stored?.lineCount); // 4200
7882
- * ```
7883
- */
7884
- declare class GadgetOutputStore {
7885
- private outputs;
7886
- /**
7887
- * Store a gadget output and return its ID.
7888
- *
7889
- * @param gadgetName - Name of the gadget that produced the output
7890
- * @param content - Full output content to store
7891
- * @returns Generated ID for retrieving the output later
7892
- */
7893
- store(gadgetName: string, content: string): string;
7894
- /**
7895
- * Retrieve a stored output by ID.
7896
- *
7897
- * @param id - The output ID (e.g., "Search_d34db33f")
7898
- * @returns The stored output or undefined if not found
7899
- */
7900
- get(id: string): StoredOutput | undefined;
7901
- /**
7902
- * Check if an output exists.
7903
- *
7904
- * @param id - The output ID to check
7905
- * @returns True if the output exists
7906
- */
7907
- has(id: string): boolean;
7908
- /**
7909
- * Get all stored output IDs.
7910
- *
7911
- * @returns Array of output IDs
7912
- */
7913
- getIds(): string[];
7914
- /**
7915
- * Get the number of stored outputs.
7916
- */
7917
- get size(): number;
7918
- /**
7919
- * Clear all stored outputs.
7920
- * Called when the agent run completes.
7921
- */
7922
- clear(): void;
7923
- /**
7924
- * Generate a unique ID for a stored output.
7925
- * Format: {GadgetName}_{8 hex chars}
7926
- */
7927
- private generateId;
7928
- }
7929
-
7930
7130
  /**
7931
7131
  * LLM Assistance Hints System
7932
7132
  *
@@ -8105,6 +7305,18 @@ declare function createHints(config: HintsConfig): AgentHooks;
8105
7305
  *
8106
7306
  * Replaces the complex wiring between Agent, ResponseProcessor, and GadgetRuntime.
8107
7307
  * Owns ALL stream processing and hook coordination with a clean, predictable flow.
7308
+ *
7309
+ * After refactoring, StreamProcessor is a thin orchestrator (~300 lines) that:
7310
+ * - Iterates over raw LLM stream chunks
7311
+ * - Applies raw-chunk and text-chunk interceptors
7312
+ * - Delegates gadget dispatch to GadgetDispatcher
7313
+ * - Yields events in real-time
7314
+ * - Applies the final assistant-message interceptor
7315
+ *
7316
+ * Extracted classes:
7317
+ * - GadgetLimitGuard — maxGadgetsPerResponse enforcement
7318
+ * - GadgetHookLifecycle — full hook sequence for a single gadget
7319
+ * - GadgetDispatcher — dispatch decision tree + concurrency + dependency
8108
7320
  */
8109
7321
 
8110
7322
  /**
@@ -8172,6 +7384,11 @@ interface StreamProcessorOptions {
8172
7384
  }
8173
7385
  /**
8174
7386
  * Result of stream processing.
7387
+ *
7388
+ * @deprecated StreamProcessor.process() is now an async generator that yields
7389
+ * StreamEvent items directly. Use StreamCompletionEvent (the final yielded event)
7390
+ * to obtain the metadata formerly returned in this type. This interface is retained
7391
+ * for backward compatibility but is not used internally.
8175
7392
  */
8176
7393
  interface StreamProcessingResult {
8177
7394
  /** All emitted events */
@@ -8190,21 +7407,14 @@ interface StreamProcessingResult {
8190
7407
  finalMessage: string;
8191
7408
  }
8192
7409
  /**
8193
- * StreamProcessor: Coordinates all stream processing and hook execution.
7410
+ * StreamProcessor: Thin orchestrator for stream processing and hook coordination.
8194
7411
  *
8195
7412
  * Execution order:
8196
7413
  * 1. Raw chunk arrives from LLM
8197
7414
  * 2. Interceptor: interceptRawChunk (transform raw text)
8198
7415
  * 3. Observer: onStreamChunk (logging)
8199
7416
  * 4. Parse for gadgets
8200
- * 5. If gadget found:
8201
- * a. Interceptor: interceptGadgetParameters (transform params)
8202
- * b. Controller: beforeGadgetExecution (can skip)
8203
- * c. Observer: onGadgetExecutionStart
8204
- * d. Execute gadget
8205
- * e. Interceptor: interceptGadgetResult (transform result)
8206
- * f. Controller: afterGadgetExecution (can provide fallback)
8207
- * g. Observer: onGadgetExecutionComplete
7417
+ * 5. If gadget found → delegate to GadgetDispatcher
8208
7418
  * 6. If text chunk:
8209
7419
  * a. Interceptor: interceptTextChunk (transform display text)
8210
7420
  * b. Yield to user
@@ -8213,43 +7423,16 @@ interface StreamProcessingResult {
8213
7423
  */
8214
7424
  declare class StreamProcessor {
8215
7425
  private readonly iteration;
8216
- private readonly registry;
8217
7426
  private readonly hooks;
8218
7427
  private readonly logger;
8219
7428
  private readonly parser;
8220
- private readonly executor;
8221
7429
  private readonly tree?;
8222
- private readonly parentNodeId;
8223
- private readonly baseDepth;
8224
- private readonly gadgetExecutionMode;
8225
7430
  private responseText;
8226
- private observerFailureCount;
8227
- /** Gadgets waiting for their dependencies to complete */
8228
- private gadgetsAwaitingDependencies;
8229
- /** Completed gadget results, keyed by invocation ID */
8230
- private completedResults;
8231
- /** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
8232
- private failedInvocations;
8233
- /** Promises for independent gadgets currently executing (fire-and-forget) */
8234
- private inFlightExecutions;
7431
+ private readonly dependencyResolver;
8235
7432
  /** Queue of completed gadget results ready to be yielded (for real-time streaming) */
8236
7433
  private completedResultsQueue;
8237
- /** Subagent configuration map for checking maxConcurrent limits */
8238
- private readonly subagentConfig?;
8239
- /** Track active execution count per gadget name */
8240
- private activeCountByGadget;
8241
- /** Queue of gadgets waiting for a concurrency slot (per gadget name) */
8242
- private concurrencyQueue;
8243
- /** Queue of exclusive gadgets deferred until in-flight gadgets complete */
8244
- private exclusiveQueue;
8245
- /** Invocation IDs completed in previous iterations (read-only reference from Agent) */
8246
- private readonly priorCompletedInvocations;
8247
- /** Invocation IDs that failed in previous iterations (read-only reference from Agent) */
8248
- private readonly priorFailedInvocations;
8249
- private readonly parentObservers?;
8250
- private readonly maxGadgetsPerResponse;
8251
- private gadgetStartedCount;
8252
- private limitExceeded;
7434
+ private readonly dispatcher;
7435
+ private readonly limitGuard;
8253
7436
  constructor(options: StreamProcessorOptions);
8254
7437
  /**
8255
7438
  * Process an LLM stream and yield events in real-time.
@@ -8270,45 +7453,6 @@ declare class StreamProcessor {
8270
7453
  * Process a text event through interceptors.
8271
7454
  */
8272
7455
  private processTextEvent;
8273
- /**
8274
- * Process a gadget call, yielding events in real-time.
8275
- *
8276
- * Yields gadget_call event IMMEDIATELY when parsed (before execution),
8277
- * enabling real-time UI feedback.
8278
- */
8279
- private processGadgetCallGenerator;
8280
- /**
8281
- * Get the effective concurrency limit for a gadget.
8282
- * Uses "most restrictive wins" strategy: the lowest non-zero value from
8283
- * external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
8284
- *
8285
- * This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
8286
- * for file writers) that cannot be weakened by external configuration.
8287
- *
8288
- * @returns 0 if unlimited, otherwise the effective limit
8289
- */
8290
- private getConcurrencyLimit;
8291
- /**
8292
- * Start a gadget execution with concurrency tracking.
8293
- * Increments active count, starts execution, and schedules queue processing on completion.
8294
- */
8295
- private startGadgetWithConcurrencyTracking;
8296
- /**
8297
- * Process the next queued gadget for a given gadget name if a slot is available.
8298
- */
8299
- private processQueuedGadget;
8300
- /**
8301
- * Execute a gadget through the full hook lifecycle and yield events.
8302
- * Handles parameter interception, before/after controllers, observers,
8303
- * execution, result interception, and tree tracking.
8304
- */
8305
- private executeGadgetGenerator;
8306
- /**
8307
- * Execute a gadget and push events to the completed results queue (non-blocking).
8308
- * Used for fire-and-forget parallel execution of independent gadgets.
8309
- * Results are pushed to completedResultsQueue for real-time streaming to the caller.
8310
- */
8311
- private executeGadgetAndCollect;
8312
7456
  /**
8313
7457
  * Drain all completed results from the queue.
8314
7458
  * Used to yield results as they complete during stream processing.
@@ -8316,53 +7460,14 @@ declare class StreamProcessor {
8316
7460
  */
8317
7461
  private drainCompletedResults;
8318
7462
  /**
8319
- * Wait for all in-flight gadget executions to complete, yielding events in real-time.
8320
- * Called at stream end to ensure all parallel executions finish.
8321
- * Results and subagent events are pushed to completedResultsQueue during execution.
8322
- * This generator yields queued events while polling, enabling real-time display
8323
- * of subagent activity (LLM calls, nested gadgets) during long-running gadgets.
8324
- * Clears the inFlightExecutions map after all gadgets complete.
8325
- */
8326
- private waitForInFlightExecutions;
8327
- /**
8328
- * Check if there are any gadgets waiting in concurrency queues.
8329
- */
8330
- private hasQueuedGadgets;
8331
- /**
8332
- * Get total count of queued gadgets across all queues.
8333
- */
8334
- private getQueuedGadgetCount;
8335
- /**
8336
- * Get total count of actively executing gadgets across all types.
8337
- * Used to know when all work is truly complete (not just when allDone resolves).
8338
- */
8339
- private getTotalActiveGadgetCount;
8340
- /**
8341
- * Handle a gadget that cannot execute because a dependency failed.
8342
- * Calls the onDependencySkipped controller to allow customization.
8343
- */
8344
- private handleFailedDependency;
8345
- /**
8346
- * Check if gadget execution should be skipped due to maxGadgetsPerResponse limit.
8347
- * If limit is exceeded, yields skip events and returns true.
8348
- * If execution can proceed, increments counter and returns false.
8349
- *
8350
- * @returns true if gadget should be skipped, false if execution can proceed
8351
- */
8352
- private checkGadgetLimitExceeded;
8353
- /**
8354
- * Process pending gadgets whose dependencies are now satisfied.
8355
- * Yields events in real-time as gadgets complete.
7463
+ * Update gadget result tracking flags based on a stream event.
7464
+ * Checks if the event is a gadget_result and, if so, marks gadgets as executed
7465
+ * and sets the break-loop flag when the result requests it.
8356
7466
  *
8357
- * Gadgets are executed in parallel for efficiency,
8358
- * but results are yielded as they become available.
7467
+ * @param evt - The stream event to inspect
7468
+ * @param state - Mutable state object holding the tracking flags
8359
7469
  */
8360
- private processPendingGadgetsGenerator;
8361
- /**
8362
- * Safely execute an observer, catching and logging any errors.
8363
- * Observers are non-critical, so errors are logged but don't crash the system.
8364
- */
8365
- private safeObserve;
7470
+ private trackGadgetResult;
8366
7471
  /**
8367
7472
  * Execute multiple observers in parallel.
8368
7473
  * All observers run concurrently and failures are tracked but don't crash.
@@ -8536,6 +7641,26 @@ declare function getProvider(model: string): string | undefined;
8536
7641
  * ```
8537
7642
  */
8538
7643
  declare function getModelId(model: string): string;
7644
+ /**
7645
+ * Strip the provider prefix from a model string.
7646
+ *
7647
+ * Identical to {@link getModelId}: removes the `provider:` portion and returns
7648
+ * just the model ID. If there is no prefix, the original string is returned.
7649
+ *
7650
+ * Use this when you need the bare model ID (e.g. for a cost-registry lookup)
7651
+ * and the input may or may not carry a provider prefix.
7652
+ *
7653
+ * @param modelId - Full model string, optionally with provider prefix
7654
+ * @returns Model ID without provider prefix
7655
+ *
7656
+ * @example
7657
+ * ```typescript
7658
+ * stripProviderPrefix('openai:gpt-4o') // → 'gpt-4o'
7659
+ * stripProviderPrefix('anthropic:claude-sonnet') // → 'claude-sonnet'
7660
+ * stripProviderPrefix('gpt-4o') // → 'gpt-4o' (no prefix — returned as-is)
7661
+ * ```
7662
+ */
7663
+ declare function stripProviderPrefix(modelId: string): string;
8539
7664
 
8540
7665
  /**
8541
7666
  * Signal that a gadget throws to indicate task completion and agent termination.
@@ -8700,6 +7825,51 @@ interface ErrorFormatterOptions {
8700
7825
  endPrefix?: string;
8701
7826
  }
8702
7827
 
7828
+ /**
7829
+ * Options for constructing a GadgetExecutor.
7830
+ */
7831
+ interface GadgetExecutorOptions {
7832
+ /** Gadget registry used to look up and execute gadgets */
7833
+ registry: GadgetRegistry;
7834
+ /** Optional callback to request human input during gadget execution */
7835
+ requestHumanInput?: (question: string) => Promise<string>;
7836
+ /** Logger instance; defaults to a new "llmist:executor" logger if omitted */
7837
+ logger?: Logger<ILogObj>;
7838
+ /** Default timeout in milliseconds applied to all gadgets without an explicit timeout */
7839
+ defaultGadgetTimeoutMs?: number;
7840
+ /** Options for formatting gadget execution errors */
7841
+ errorFormatterOptions?: ErrorFormatterOptions;
7842
+ /** LLMist client made available to gadgets via ExecutionContext.llmist */
7843
+ client?: LLMist;
7844
+ /** Media store for persisting gadget media outputs */
7845
+ mediaStore?: MediaStore;
7846
+ /** Parent agent configuration inherited by subagents */
7847
+ agentConfig?: AgentContextConfig;
7848
+ /** Per-gadget configuration overrides (e.g., timeoutMs, model) */
7849
+ subagentConfig?: SubagentConfigMap;
7850
+ /** Execution tree for tracking LLM calls and gadget executions */
7851
+ tree?: ExecutionTree;
7852
+ /** Parent node ID in the execution tree */
7853
+ parentNodeId?: NodeId | null;
7854
+ /** Base depth for nodes created during execution */
7855
+ baseDepth?: number;
7856
+ /**
7857
+ * Parent agent's observer hooks for subagent visibility.
7858
+ * When a subagent uses withParentContext(ctx), these observers are also called
7859
+ * for gadget events in addition to the subagent's own hooks.
7860
+ */
7861
+ parentObservers?: Observers;
7862
+ /**
7863
+ * Current agent's observers.
7864
+ * Passed to ExecutionContext.parentObservers so gadgets creating subagents
7865
+ * can inherit them via withParentContext(ctx).
7866
+ */
7867
+ currentObservers?: Observers;
7868
+ /** Shared rate limit tracker for coordinated throttling across subagents */
7869
+ rateLimitTracker?: RateLimitTracker;
7870
+ /** Shared retry config for consistent backoff behavior across subagents */
7871
+ retryConfig?: ResolvedRetryConfig;
7872
+ }
8703
7873
  declare class GadgetExecutor {
8704
7874
  private readonly registry;
8705
7875
  private readonly requestHumanInput?;
@@ -8718,7 +7888,7 @@ declare class GadgetExecutor {
8718
7888
  private readonly logger;
8719
7889
  private readonly errorFormatter;
8720
7890
  private readonly argPrefix;
8721
- constructor(registry: GadgetRegistry, requestHumanInput?: ((question: string) => Promise<string>) | undefined, logger?: Logger<ILogObj>, defaultGadgetTimeoutMs?: number | undefined, errorFormatterOptions?: ErrorFormatterOptions, client?: LLMist | undefined, mediaStore?: MediaStore | undefined, agentConfig?: AgentContextConfig | undefined, subagentConfig?: SubagentConfigMap | undefined, tree?: ExecutionTree | undefined, parentNodeId?: (NodeId | null) | undefined, baseDepth?: number | undefined, parentObservers?: Observers | undefined, currentObservers?: Observers | undefined, rateLimitTracker?: RateLimitTracker | undefined, retryConfig?: ResolvedRetryConfig | undefined);
7891
+ constructor(options: GadgetExecutorOptions);
8722
7892
  /**
8723
7893
  * Creates a promise that rejects with a TimeoutException after the specified timeout.
8724
7894
  * Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
@@ -8732,7 +7902,6 @@ declare class GadgetExecutor {
8732
7902
  */
8733
7903
  private unifyExecuteResult;
8734
7904
  execute(call: ParsedGadgetCall): Promise<GadgetExecutionResult>;
8735
- executeAll(calls: ParsedGadgetCall[]): Promise<GadgetExecutionResult[]>;
8736
7905
  }
8737
7906
 
8738
7907
  /**
@@ -10913,4 +10082,4 @@ declare const timing: {
10913
10082
  */
10914
10083
  declare function getHostExports(ctx: ExecutionContext): HostExports;
10915
10084
 
10916
- export { AbortException, AbstractGadget, type AddGadgetParams, type AddLLMCallParams, type AfterGadgetExecutionAction, type AfterGadgetExecutionControllerContext, type AfterLLMCallAction, type AfterLLMCallControllerContext, type AfterLLMErrorAction, Agent, AgentBuilder, type AgentHooks, type AgentOptions, AnthropicMessagesProvider, type AudioContentPart, type AudioMimeType, type AudioSource, type BaseExecutionEvent, BaseSessionManager, type BeforeGadgetExecutionAction, type BeforeLLMCallAction, BudgetPricingUnavailableError, type CachingConfig, type CachingScope, type ChunkInterceptorContext, type CompactionConfig, type CompactionContext, type CompactionEvent, CompactionManager, type CompactionResult, type CompactionStats, type CompactionStrategy, type CompleteGadgetParams, type CompleteLLMCallParams, type ContentPart, type Controllers, ConversationManager, type CostEstimate, type CostReportingLLMist, type CreateGadgetConfig, DEFAULT_COMPACTION_CONFIG, DEFAULT_HINTS, DEFAULT_PROMPTS, DEFAULT_RATE_LIMIT_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_SUMMARIZATION_PROMPT, type EventHandlers, type ExecutionContext, type ExecutionEvent, type ExecutionEventType, type ExecutionNode, type ExecutionNodeType, ExecutionTree, FALLBACK_CHARS_PER_TOKEN, type FileLoggingOptions, type FileLoggingState, type FileWrittenInfo, type FormatLLMErrorContext, GADGET_ARG_PREFIX, GADGET_END_PREFIX, GADGET_START_PREFIX, Gadget, type GadgetCallEvent, GadgetCallParser, type GadgetClass, type GadgetCompleteEvent, type GadgetConfig, type GadgetErrorEvent, type GadgetEvent, type GadgetExample, type GadgetExecuteResult, type GadgetExecuteResultWithMedia, type GadgetExecuteReturn, type GadgetExecutionControllerContext, type GadgetExecutionMode, type GadgetExecutionResult, GadgetExecutor, type GadgetFactoryExports, type GadgetMediaOutput, type GadgetNode, type GadgetOrClass, GadgetOutputStore, type GadgetParameterInterceptorContext, GadgetRegistry, type GadgetResultInterceptorContext, type GadgetSkippedEvent, type GadgetStartEvent, type GadgetState, GeminiGenerativeProvider, type HintContext, type HintTemplate, type HintsConfig, type HistoryMessage, HookPresets, type HostExports, HuggingFaceProvider, type HumanInputRequiredEvent, HumanInputRequiredException, HybridStrategy, type IConversationManager, type ISessionManager, type ImageBase64Source, type ImageContentPart, type ImageGenerationOptions, type ImageGenerationResult, type ImageMimeType, type ImageModelSpec, type ImageSource, type ImageUrlSource, type Interceptors, type IterationHintOptions, type LLMCallCompleteEvent, type LLMCallControllerContext, type LLMCallErrorEvent, type LLMCallNode, type LLMCallStartEvent, type LLMCallStreamEvent, type LLMErrorControllerContext, type LLMEvent, type LLMGenerationOptions, type LLMMessage, LLMMessageBuilder, type LLMResponseEndEvent, type LLMStream, type LLMStreamChunk, LLMist, type LLMistOptions, type LLMistPackageManifest, type LoggerOptions, type LoggingOptions, MODEL_ALIASES, type MediaKind, type MediaMetadata, MediaStore, type MessageContent, type MessageInterceptorContext, type MessageRole, type MessageTurn, type ModelDescriptor, type ModelFeatures, ModelIdentifierParser, type ModelLimits, type ModelPricing, ModelRegistry, type ModelSpec, type NodeId, type ObserveChunkContext, type ObserveCompactionContext, type ObserveGadgetCompleteContext, type ObserveGadgetStartContext, type ObserveLLMCallContext, type ObserveLLMCompleteContext, type ObserveLLMErrorContext, type ObserveRateLimitThrottleContext, type ObserveRetryAttemptContext, type Observers, OpenAIChatProvider, type OpenAICompatibleConfig, OpenAICompatibleProvider, type OpenRouterConfig, OpenRouterProvider, type OpenRouterRouting, type ParallelGadgetHintOptions, type ParsedGadgetCall, type PresetDefinition, type PromptContext, type PromptTemplate, type PromptTemplateConfig, type ProviderAdapter, type ProviderIdentifier, type RateLimitConfig, type RateLimitStats, RateLimitTracker, type ReasoningConfig, type ReasoningEffort, type ResolveValueOptions, type ResolvedCompactionConfig, type ResolvedRateLimitConfig, type ResolvedRetryConfig, type RetryConfig, type RetryOptions, type SessionManifestEntry, SimpleSessionManager, SlidingWindowStrategy, type SpeechGenerationOptions, type SpeechGenerationResult, type SpeechModelSpec, type StoredMedia, type StoredOutput, type StreamCompleteEvent, type StreamEvent, type StreamProcessingResult, StreamProcessor, type StreamProcessorOptions, type SubagentConfig, type SubagentConfigMap, type SubagentContext, type SubagentManifestEntry, type SubagentOptions, SummarizationStrategy, TaskCompletionSignal, type TextContentPart, type TextEvent, type TextGenerationOptions, type TextOnlyAction, type TextOnlyContext, type TextOnlyCustomHandler, type TextOnlyGadgetConfig, type TextOnlyHandler, type TextOnlyStrategy, type ThinkingChunk, type ThinkingEvent, TimeoutException, type TokenUsage, type TrailingMessage, type TrailingMessageContext, type CompactionEvent$1 as TreeCompactionEvent, type GadgetSkippedEvent$1 as TreeGadgetSkippedEvent, type TriggeredLimitInfo, type ValidationIssue, type ValidationResult, type VisionAnalyzeOptions, type VisionAnalyzeResult, audioFromBase64, audioFromBuffer, collectEvents, collectText, complete, createAnthropicProviderFromEnv, createFileLoggingState, createGadget, createGadgetOutputViewer, createGeminiProviderFromEnv, createHints, createHuggingFaceProviderFromEnv, createLogger, createMediaOutput, createOpenAIProviderFromEnv, createOpenRouterProviderFromEnv, createSubagent, defaultLogger, detectAudioMimeType, detectImageMimeType, discoverProviderAdapters, extractMessageText, extractRetryAfterMs, filterByDepth, filterByParent, filterRootEvents, format, formatBytes, formatCallNumber, formatDate, formatDuration, formatLLMError, formatLlmRequest, gadgetError, gadgetSuccess, getErrorMessage, getHostExports, getModelId, getPresetGadgets, getProvider, getSubagent, groupByParent, hasHostExports, hasPreset, hasProviderPrefix, hasSubagents, humanDelay, imageFromBase64, imageFromBuffer, imageFromUrl, isAbortError, isAudioPart, isDataUrl, isGadgetEvent, isImagePart, isLLMEvent, isRetryableError, isRootEvent, isSubagentEvent, isTextPart, iterationProgressHint, listPresets, listSubagents, normalizeMessageContent, parallelGadgetHint, parseDataUrl, parseManifest, parseRetryAfterHeader, randomDelay, resetFileLoggingState, resolveConfig, resolveHintTemplate, resolveModel, resolvePromptTemplate, resolveRateLimitConfig, resolveRetryConfig, resolveRulesTemplate, resolveSubagentModel, resolveSubagentTimeout, resolveValue, resultWithAudio, resultWithFile, resultWithImage, resultWithImages, resultWithMedia, runWithHandlers, schemaToJSONSchema, stream, text, timing, toBase64, truncate, validateAndApplyDefaults, validateGadgetParams, validateGadgetSchema, withErrorHandling, withRetry, withTimeout };
10085
+ export { AbortException, AbstractGadget, type AddGadgetParams, type AddLLMCallParams, type AfterGadgetExecutionAction, type AfterGadgetExecutionControllerContext, type AfterLLMCallAction, type AfterLLMCallControllerContext, type AfterLLMErrorAction, Agent, AgentBuilder, type AgentHooks, type AgentOptions, AnthropicMessagesProvider, type AudioContentPart, type AudioMimeType, type AudioSource, type BaseExecutionEvent, BaseSessionManager, type BeforeGadgetExecutionAction, type BeforeLLMCallAction, BudgetPricingUnavailableError, type CachingConfig, type CachingScope, type ChunkInterceptorContext, type CompactionConfig, type CompactionContext, type CompactionEvent, CompactionManager, type CompactionResult, type CompactionStats, type CompactionStrategy, type CompleteGadgetParams, type CompleteLLMCallParams, type ContentPart, type Controllers, ConversationManager, type CostEstimate, type CostReportingLLMist, type CreateGadgetConfig, DEFAULT_COMPACTION_CONFIG, DEFAULT_HINTS, DEFAULT_PROMPTS, DEFAULT_RATE_LIMIT_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_SUMMARIZATION_PROMPT, type EventHandlers, type ExecutionContext, type ExecutionEvent, type ExecutionEventType, type ExecutionNode, type ExecutionNodeType, ExecutionTree, FALLBACK_CHARS_PER_TOKEN, type FileLoggingOptions, type FileLoggingState, type FileWrittenInfo, type FormatLLMErrorContext, GADGET_ARG_PREFIX, GADGET_END_PREFIX, GADGET_START_PREFIX, Gadget, type GadgetCallEvent, GadgetCallParser, type GadgetClass, type GadgetCompleteEvent, type GadgetConfig, type GadgetErrorEvent, type GadgetEvent, type GadgetExample, type GadgetExecuteResult, type GadgetExecuteResultWithMedia, type GadgetExecuteReturn, type GadgetExecutionControllerContext, type GadgetExecutionMode, type GadgetExecutionResult, GadgetExecutor, type GadgetExecutorOptions, type GadgetFactoryExports, type GadgetMediaOutput, type GadgetNode, type GadgetOrClass, GadgetOutputStore, type GadgetParameterInterceptorContext, GadgetRegistry, type GadgetResultInterceptorContext, type GadgetSkippedEvent, type GadgetStartEvent, type GadgetState, GeminiGenerativeProvider, type HintContext, type HintTemplate, type HintsConfig, type HistoryMessage, HookPresets, type HostExports, HuggingFaceProvider, type HumanInputRequiredEvent, HumanInputRequiredException, HybridStrategy, type IConversationManager, type ISessionManager, type ImageBase64Source, type ImageContentPart, type ImageGenerationOptions, type ImageGenerationResult, type ImageMimeType, type ImageModelSpec, type ImageSource, type ImageUrlSource, type Interceptors, type IterationHintOptions, type LLMCallCompleteEvent, type LLMCallControllerContext, type LLMCallErrorEvent, type LLMCallNode, type LLMCallStartEvent, type LLMCallStreamEvent, type LLMErrorControllerContext, type LLMEvent, type LLMGenerationOptions, type LLMMessage, LLMMessageBuilder, type LLMResponseEndEvent, type LLMStream, type LLMStreamChunk, LLMist, type LLMistOptions, type LLMistPackageManifest, type LoggerOptions, type LoggingOptions, MODEL_ALIASES, type MediaKind, type MediaMetadata, MediaStore, type MessageContent, type MessageInterceptorContext, type MessageRole, type MessageTurn, type ModelDescriptor, type ModelFeatures, ModelIdentifierParser, type ModelLimits, type ModelPricing, ModelRegistry, type ModelSpec, type NodeId, type ObserveChunkContext, type ObserveCompactionContext, type ObserveGadgetCompleteContext, type ObserveGadgetStartContext, type ObserveLLMCallContext, type ObserveLLMCompleteContext, type ObserveLLMErrorContext, type ObserveRateLimitThrottleContext, type ObserveRetryAttemptContext, type Observers, OpenAIChatProvider, type OpenAICompatibleConfig, OpenAICompatibleProvider, type OpenRouterConfig, OpenRouterProvider, type OpenRouterRouting, type OutputLimitConfig, type ParallelGadgetHintOptions, type ParsedGadgetCall, type PrefixConfig, type PresetDefinition, type PromptContext, type PromptTemplate, type PromptTemplateConfig, type ProviderAdapter, type ProviderIdentifier, type RateLimitConfig, type RateLimitStats, RateLimitTracker, type ReasoningConfig, type ReasoningEffort, type ResolveValueOptions, type ResolvedCompactionConfig, type ResolvedRateLimitConfig, type ResolvedRetryConfig, type RetryConfig, type RetryOptions, type SessionManifestEntry, SimpleSessionManager, SlidingWindowStrategy, type SpeechGenerationOptions, type SpeechGenerationResult, type SpeechModelSpec, type StoredMedia, type StoredOutput, type StreamCompleteEvent, type StreamEvent, type StreamProcessingResult, StreamProcessor, type StreamProcessorOptions, type SubagentConfig, type SubagentConfigMap, type SubagentContext, type SubagentManifestEntry, type SubagentOptions, SummarizationStrategy, TaskCompletionSignal, type TextContentPart, type TextEvent, type TextGenerationOptions, type TextOnlyAction, type TextOnlyContext, type TextOnlyCustomHandler, type TextOnlyGadgetConfig, type TextOnlyHandler, type TextOnlyStrategy, type ThinkingChunk, type ThinkingEvent, TimeoutException, type TokenUsage, type TrailingMessage, type TrailingMessageContext, type CompactionEvent$1 as TreeCompactionEvent, type TreeConfig, type GadgetSkippedEvent$1 as TreeGadgetSkippedEvent, type TriggeredLimitInfo, type ValidationIssue, type ValidationResult, type VisionAnalyzeOptions, type VisionAnalyzeResult, audioFromBase64, audioFromBuffer, collectEvents, collectText, complete, createAnthropicProviderFromEnv, createFileLoggingState, createGadget, createGadgetOutputViewer, createGeminiProviderFromEnv, createHints, createHuggingFaceProviderFromEnv, createLogger, createMediaOutput, createOpenAIProviderFromEnv, createOpenRouterProviderFromEnv, createSubagent, defaultLogger, detectAudioMimeType, detectImageMimeType, discoverProviderAdapters, extractMessageText, extractRetryAfterMs, filterByDepth, filterByParent, filterRootEvents, format, formatBytes, formatCallNumber, formatDate, formatDuration, formatLLMError, formatLlmRequest, gadgetError, gadgetSuccess, getErrorMessage, getHostExports, getModelId, getPresetGadgets, getProvider, getSubagent, groupByParent, hasHostExports, hasPreset, hasProviderPrefix, hasSubagents, humanDelay, imageFromBase64, imageFromBuffer, imageFromUrl, isAbortError, isAudioPart, isDataUrl, isGadgetEvent, isImagePart, isLLMEvent, isRetryableError, isRootEvent, isSubagentEvent, isTextPart, iterationProgressHint, listPresets, listSubagents, normalizeMessageContent, parallelGadgetHint, parseDataUrl, parseManifest, parseRetryAfterHeader, randomDelay, resetFileLoggingState, resolveConfig, resolveHintTemplate, resolveModel, resolvePromptTemplate, resolveRateLimitConfig, resolveRetryConfig, resolveRulesTemplate, resolveSubagentModel, resolveSubagentTimeout, resolveValue, resultWithAudio, resultWithFile, resultWithImage, resultWithImages, resultWithMedia, runWithHandlers, schemaToJSONSchema, stream, stripProviderPrefix, text, timing, toBase64, truncate, validateAndApplyDefaults, validateGadgetParams, validateGadgetSchema, withErrorHandling, withRetry, withTimeout };