llmist 6.1.0 → 6.2.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.
@@ -1,4 +1,5 @@
1
- import { ZodTypeAny } from 'zod';
1
+ import * as zod from 'zod';
2
+ import { ZodType, ZodTypeAny } from 'zod';
2
3
  import { Logger, ILogObj } from 'tslog';
3
4
 
4
5
  /**
@@ -213,990 +214,1255 @@ interface SpeechModelSpec {
213
214
  };
214
215
  }
215
216
 
216
- /**
217
- * Model Catalog Types
218
- *
219
- * Type definitions for LLM model specifications including
220
- * context windows, pricing, features, and capabilities.
221
- */
222
- interface ModelPricing {
223
- /** Price per 1 million input tokens in USD */
224
- input: number;
225
- /** Price per 1 million output tokens in USD */
226
- output: number;
227
- /** Price per 1 million cached input tokens in USD (if supported) */
228
- cachedInput?: number;
229
- /** Price per 1 million cache write tokens in USD (Anthropic: 1.25x input price) */
230
- cacheWriteInput?: number;
217
+ interface LLMGenerationOptions {
218
+ model: string;
219
+ messages: LLMMessage[];
220
+ maxTokens?: number;
221
+ temperature?: number;
222
+ topP?: number;
223
+ stopSequences?: string[];
224
+ responseFormat?: "text";
225
+ metadata?: Record<string, unknown>;
226
+ extra?: Record<string, unknown>;
227
+ /**
228
+ * Optional abort signal for cancelling the request mid-flight.
229
+ *
230
+ * When the signal is aborted, the provider will attempt to cancel
231
+ * the underlying HTTP request and the stream will terminate with
232
+ * an abort error. Use `isAbortError()` from `@/core/errors` to
233
+ * detect cancellation in error handling.
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * const controller = new AbortController();
238
+ *
239
+ * const stream = client.stream({
240
+ * model: "claude-3-5-sonnet-20241022",
241
+ * messages: [{ role: "user", content: "Tell me a long story" }],
242
+ * signal: controller.signal,
243
+ * });
244
+ *
245
+ * // Cancel after 5 seconds
246
+ * setTimeout(() => controller.abort(), 5000);
247
+ *
248
+ * try {
249
+ * for await (const chunk of stream) {
250
+ * process.stdout.write(chunk.text);
251
+ * }
252
+ * } catch (error) {
253
+ * if (isAbortError(error)) {
254
+ * console.log("\nRequest was cancelled");
255
+ * } else {
256
+ * throw error;
257
+ * }
258
+ * }
259
+ * ```
260
+ */
261
+ signal?: AbortSignal;
231
262
  }
232
- interface ModelFeatures {
233
- /** Supports streaming responses */
234
- streaming: boolean;
235
- /** Supports function/tool calling */
236
- functionCalling: boolean;
237
- /** Supports vision/image input */
238
- vision: boolean;
239
- /** Supports extended thinking/reasoning */
240
- reasoning?: boolean;
241
- /** Supports structured outputs */
242
- structuredOutputs?: boolean;
243
- /** Supports fine-tuning */
244
- fineTuning?: boolean;
263
+ interface TokenUsage {
264
+ inputTokens: number;
265
+ outputTokens: number;
266
+ totalTokens: number;
267
+ /** Number of input tokens served from cache (subset of inputTokens) */
268
+ cachedInputTokens?: number;
269
+ /** Number of input tokens written to cache (subset of inputTokens, Anthropic only) */
270
+ cacheCreationInputTokens?: number;
245
271
  }
246
- interface ModelSpec {
247
- /** Provider identifier (e.g., 'openai', 'anthropic', 'gemini') */
248
- provider: string;
249
- /** Full model identifier used in API calls */
250
- modelId: string;
251
- /** Human-readable display name */
252
- displayName: string;
253
- /** Maximum context window size in tokens */
254
- contextWindow: number;
255
- /** Maximum output tokens per request */
256
- maxOutputTokens: number;
257
- /** Pricing per 1M tokens */
258
- pricing: ModelPricing;
259
- /** Training data knowledge cutoff date (YYYY-MM-DD or description) */
260
- knowledgeCutoff: string;
261
- /** Supported features and capabilities */
262
- features: ModelFeatures;
263
- /** Additional metadata */
264
- metadata?: {
265
- /** Model family/series */
266
- family?: string;
267
- /** Release date */
268
- releaseDate?: string;
269
- /** Deprecation date if applicable */
270
- deprecationDate?: string;
271
- /** Notes or special information */
272
- notes?: string;
273
- /** Whether manual temperature configuration is supported (defaults to true) */
274
- supportsTemperature?: boolean;
275
- };
272
+ interface LLMStreamChunk {
273
+ text: string;
274
+ /**
275
+ * Indicates that the provider has finished producing output and includes the reason if available.
276
+ */
277
+ finishReason?: string | null;
278
+ /**
279
+ * Token usage information, typically available in the final chunk when the stream completes.
280
+ */
281
+ usage?: TokenUsage;
282
+ /**
283
+ * Provider specific payload emitted at the same time as the text chunk. This is useful for debugging and tests.
284
+ */
285
+ rawEvent?: unknown;
276
286
  }
277
- interface ModelLimits {
278
- contextWindow: number;
279
- maxOutputTokens: number;
287
+ interface LLMStream extends AsyncIterable<LLMStreamChunk> {
280
288
  }
281
- interface CostEstimate {
282
- inputCost: number;
283
- /** Cost for cached input tokens (already included in inputCost calculation) */
284
- cachedInputCost: number;
285
- /** Cost for cache creation tokens (already included in inputCost calculation, Anthropic only) */
286
- cacheCreationCost: number;
287
- outputCost: number;
288
- totalCost: number;
289
- currency: "USD";
289
+ type ProviderIdentifier = string;
290
+ interface ModelDescriptor {
291
+ provider: string;
292
+ name: string;
293
+ }
294
+ declare class ModelIdentifierParser {
295
+ private readonly defaultProvider;
296
+ constructor(defaultProvider?: string);
297
+ parse(identifier: string): ModelDescriptor;
290
298
  }
291
299
 
292
300
  /**
293
- * Strategy interface for context compaction.
301
+ * Unified event types for the Execution Tree.
294
302
  *
295
- * Strategies define how conversation history is compressed to fit within
296
- * context window limits. Different strategies trade off between:
297
- * - Speed (LLM calls vs local processing)
298
- * - Context preservation (summary quality vs simple truncation)
299
- * - Cost (summarization model usage)
303
+ * All events carry full tree context (nodeId, parentId, depth, path).
304
+ * No special SubagentEvent wrapper needed - subagent events are regular
305
+ * events with depth > 0.
306
+ *
307
+ * @module core/execution-events
300
308
  */
301
309
 
302
310
  /**
303
- * Context provided to compaction strategies.
311
+ * Base properties shared by all execution events.
312
+ * Every event carries full tree context.
304
313
  */
305
- interface CompactionContext {
306
- /** Current token count of the conversation */
307
- currentTokens: number;
308
- /** Target token count after compaction */
309
- targetTokens: number;
310
- /** Model's context window limits */
311
- modelLimits: ModelLimits;
312
- /** LLMist client for summarization calls */
313
- client: LLMist;
314
- /** Model identifier for token counting and summarization */
314
+ interface BaseExecutionEvent {
315
+ /** Monotonically increasing event ID */
316
+ eventId: number;
317
+ /** Event timestamp */
318
+ timestamp: number;
319
+ /** Node that emitted this event */
320
+ nodeId: string;
321
+ /** Parent node ID (null for root events) */
322
+ parentId: string | null;
323
+ /** Nesting depth (0 = root, 1 = child, etc.) */
324
+ depth: number;
325
+ /** Full path from root to this node */
326
+ path: string[];
327
+ }
328
+ /**
329
+ * Emitted when an LLM call starts.
330
+ */
331
+ interface LLMCallStartEvent extends BaseExecutionEvent {
332
+ type: "llm_call_start";
333
+ /** Iteration number within agent loop (1-indexed) */
334
+ iteration: number;
335
+ /** Model identifier */
315
336
  model: string;
337
+ /** Request messages */
338
+ request?: LLMMessage[];
316
339
  }
317
340
  /**
318
- * Result of a compaction operation.
341
+ * Emitted for each streaming chunk from LLM.
319
342
  */
320
- interface CompactionResult {
321
- /** Compacted messages to replace history with */
322
- messages: LLMMessage[];
323
- /** Summary text if summarization was used */
324
- summary?: string;
325
- /** The name of the strategy that was ultimately executed */
326
- strategyName: string;
327
- /** Metadata about the compaction */
328
- metadata: {
329
- /** Number of messages before compaction */
330
- originalCount: number;
331
- /** Number of messages after compaction */
332
- compactedCount: number;
333
- /** Estimated tokens before compaction */
334
- tokensBefore: number;
335
- /** Estimated tokens after compaction */
336
- tokensAfter: number;
337
- };
343
+ interface LLMCallStreamEvent extends BaseExecutionEvent {
344
+ type: "llm_call_stream";
345
+ /** Text chunk */
346
+ chunk: string;
338
347
  }
339
348
  /**
340
- * Interface for compaction strategy implementations.
341
- *
342
- * Strategies receive the conversation history (excluding base messages like
343
- * system prompt and gadget instructions) and must return a compacted version.
344
- *
345
- * @example
346
- * ```typescript
347
- * class MyCustomStrategy implements CompactionStrategy {
348
- * readonly name = 'my-custom';
349
- *
350
- * async compact(
351
- * messages: LLMMessage[],
352
- * config: ResolvedCompactionConfig,
353
- * context: CompactionContext
354
- * ): Promise<CompactionResult> {
355
- * // Custom compaction logic
356
- * return {
357
- * messages: compactedMessages,
358
- * metadata: { ... }
359
- * };
360
- * }
361
- * }
362
- * ```
349
+ * Emitted when an LLM call completes successfully.
363
350
  */
364
- interface CompactionStrategy {
365
- /** Human-readable name of the strategy */
366
- readonly name: string;
367
- /**
368
- * Compact the given messages to fit within target token count.
369
- *
370
- * @param messages - Conversation history messages (excludes system/gadget base)
371
- * @param config - Resolved compaction configuration
372
- * @param context - Context including token counts and LLM client
373
- * @returns Compacted messages with metadata
374
- */
375
- compact(messages: LLMMessage[], config: ResolvedCompactionConfig, context: CompactionContext): Promise<CompactionResult>;
351
+ interface LLMCallCompleteEvent extends BaseExecutionEvent {
352
+ type: "llm_call_complete";
353
+ /** Complete response text */
354
+ response: string;
355
+ /** Token usage */
356
+ usage?: TokenUsage;
357
+ /** Finish reason from LLM */
358
+ finishReason?: string | null;
359
+ /** Cost in USD */
360
+ cost?: number;
376
361
  }
377
362
  /**
378
- * Utility to group messages into logical conversation turns.
379
- *
380
- * A "turn" is typically a user message followed by an assistant response.
381
- * Gadget calls are grouped with the preceding assistant message.
363
+ * Emitted when an LLM call fails.
382
364
  */
383
- interface MessageTurn {
384
- /** Messages in this turn (user + assistant + any gadget results) */
385
- messages: LLMMessage[];
386
- /** Estimated token count for this turn */
387
- tokenEstimate: number;
365
+ interface LLMCallErrorEvent extends BaseExecutionEvent {
366
+ type: "llm_call_error";
367
+ /** The error that occurred */
368
+ error: Error;
369
+ /** Whether the error was recovered by a controller */
370
+ recovered: boolean;
388
371
  }
389
-
390
372
  /**
391
- * Configuration types for the context compaction system.
392
- *
393
- * Context compaction automatically manages conversation history to prevent
394
- * context window overflow in long-running agent conversations.
373
+ * Emitted when a gadget call is parsed from LLM output (before execution).
395
374
  */
396
-
375
+ interface GadgetCallEvent extends BaseExecutionEvent {
376
+ type: "gadget_call";
377
+ /** Invocation ID */
378
+ invocationId: string;
379
+ /** Gadget name */
380
+ name: string;
381
+ /** Parameters */
382
+ parameters: Record<string, unknown>;
383
+ /** Dependencies (other invocation IDs) */
384
+ dependencies: string[];
385
+ }
397
386
  /**
398
- * Event emitted when compaction occurs.
399
- * This is included in StreamEvent for UI visibility.
387
+ * Emitted when gadget execution starts.
400
388
  */
401
- interface CompactionEvent$1 {
402
- /** The strategy that performed the compaction */
403
- strategy: string;
404
- /** Token count before compaction */
405
- tokensBefore: number;
406
- /** Token count after compaction */
407
- tokensAfter: number;
408
- /** Number of messages before compaction */
409
- messagesBefore: number;
410
- /** Number of messages after compaction */
411
- messagesAfter: number;
412
- /** Summary text if summarization was used */
413
- summary?: string;
414
- /** Agent iteration when compaction occurred */
415
- iteration: number;
389
+ interface GadgetStartEvent extends BaseExecutionEvent {
390
+ type: "gadget_start";
391
+ /** Invocation ID */
392
+ invocationId: string;
393
+ /** Gadget name */
394
+ name: string;
416
395
  }
417
396
  /**
418
- * Statistics about compaction activity.
397
+ * Emitted when gadget execution completes successfully.
419
398
  */
420
- interface CompactionStats {
421
- /** Total number of compactions performed */
422
- totalCompactions: number;
423
- /** Total tokens saved across all compactions */
424
- totalTokensSaved: number;
425
- /** Current context usage */
426
- currentUsage: {
427
- tokens: number;
428
- percent: number;
429
- };
430
- /** Model's context window size */
431
- contextWindow: number;
399
+ interface GadgetCompleteEvent extends BaseExecutionEvent {
400
+ type: "gadget_complete";
401
+ /** Invocation ID */
402
+ invocationId: string;
403
+ /** Gadget name */
404
+ name: string;
405
+ /** Result string */
406
+ result: string;
407
+ /** Execution time in ms */
408
+ executionTimeMs: number;
409
+ /** Cost in USD */
410
+ cost?: number;
411
+ /** Media outputs */
412
+ media?: GadgetMediaOutput[];
432
413
  }
433
414
  /**
434
- * Configuration for the context compaction system.
435
- *
436
- * @example
437
- * ```typescript
438
- * // Custom configuration
439
- * const agent = await LLMist.createAgent()
440
- * .withModel('sonnet')
441
- * .withCompaction({
442
- * triggerThresholdPercent: 70,
443
- * targetPercent: 40,
444
- * preserveRecentTurns: 10,
445
- * })
446
- * .ask('...');
447
- *
448
- * // Disable compaction
449
- * const agent = await LLMist.createAgent()
450
- * .withModel('sonnet')
451
- * .withoutCompaction()
452
- * .ask('...');
453
- * ```
415
+ * Emitted when gadget execution fails.
454
416
  */
455
- interface CompactionConfig {
456
- /**
457
- * Enable or disable compaction.
458
- * @default true
459
- */
460
- enabled?: boolean;
461
- /**
462
- * The compaction strategy to use.
463
- * - 'sliding-window': Fast, drops oldest turns (no LLM call)
464
- * - 'summarization': LLM-based compression of old messages
465
- * - 'hybrid': Summarizes old messages + keeps recent turns (recommended)
466
- * - Or provide a custom CompactionStrategy instance
467
- * @default 'hybrid'
468
- */
469
- strategy?: "sliding-window" | "summarization" | "hybrid" | CompactionStrategy;
470
- /**
471
- * Context usage percentage that triggers compaction.
472
- * When token count exceeds this percentage of the context window,
473
- * compaction is performed before the next LLM call.
474
- * @default 80
475
- */
476
- triggerThresholdPercent?: number;
477
- /**
478
- * Target context usage percentage after compaction.
479
- * The compaction will aim to reduce tokens to this percentage.
480
- * @default 50
481
- */
482
- targetPercent?: number;
483
- /**
484
- * Number of recent turns to preserve during compaction.
485
- * A "turn" is a user message + assistant response pair.
486
- * Recent turns are kept verbatim while older ones are summarized/dropped.
487
- * @default 5
488
- */
489
- preserveRecentTurns?: number;
490
- /**
491
- * Model to use for summarization.
492
- * If not specified, uses the agent's model.
493
- * @default undefined (uses agent's model)
494
- */
495
- summarizationModel?: string;
496
- /**
497
- * Custom system prompt for summarization.
498
- * If not specified, uses a default prompt optimized for context preservation.
499
- */
500
- summarizationPrompt?: string;
501
- /**
502
- * Callback invoked when compaction occurs.
503
- * Useful for logging or analytics.
504
- */
505
- onCompaction?: (event: CompactionEvent$1) => void;
417
+ interface GadgetErrorEvent extends BaseExecutionEvent {
418
+ type: "gadget_error";
419
+ /** Invocation ID */
420
+ invocationId: string;
421
+ /** Gadget name */
422
+ name: string;
423
+ /** Error message */
424
+ error: string;
425
+ /** Execution time in ms */
426
+ executionTimeMs: number;
427
+ }
428
+ /**
429
+ * Emitted when a gadget is skipped.
430
+ */
431
+ interface GadgetSkippedEvent$1 extends BaseExecutionEvent {
432
+ type: "gadget_skipped";
433
+ /** Invocation ID */
434
+ invocationId: string;
435
+ /** Gadget name */
436
+ name: string;
437
+ /** Reason for skipping */
438
+ reason: "dependency_failed" | "controller_skip";
439
+ /** Error message (combines reason and failedDependencyError for consistency with GadgetErrorEvent) */
440
+ error: string;
441
+ /** Failed dependency invocation ID (if dependency_failed) */
442
+ failedDependency?: string;
443
+ /** Error message from failed dependency */
444
+ failedDependencyError?: string;
445
+ }
446
+ /**
447
+ * Emitted for text output from LLM (pure notification, not a tree node).
448
+ */
449
+ interface TextEvent extends BaseExecutionEvent {
450
+ type: "text";
451
+ /** Text content */
452
+ content: string;
453
+ }
454
+ /**
455
+ * Emitted when context compaction occurs.
456
+ */
457
+ interface CompactionEvent$1 extends BaseExecutionEvent {
458
+ type: "compaction";
459
+ /** Tokens before compaction */
460
+ tokensBefore: number;
461
+ /** Tokens after compaction */
462
+ tokensAfter: number;
463
+ /** Compaction strategy used */
464
+ strategy: string;
465
+ /** Messages removed */
466
+ messagesRemoved: number;
467
+ }
468
+ /**
469
+ * Emitted when human input is required.
470
+ */
471
+ interface HumanInputRequiredEvent extends BaseExecutionEvent {
472
+ type: "human_input_required";
473
+ /** Question for the user */
474
+ question: string;
475
+ /** Gadget name requesting input */
476
+ gadgetName: string;
477
+ /** Invocation ID */
478
+ invocationId: string;
506
479
  }
507
480
  /**
508
- * Default configuration values for compaction.
509
- * Compaction is enabled by default with the hybrid strategy.
481
+ * Emitted when the execution stream completes.
510
482
  */
511
- declare const DEFAULT_COMPACTION_CONFIG: Required<Omit<CompactionConfig, "summarizationModel" | "summarizationPrompt" | "onCompaction">>;
483
+ interface StreamCompleteEvent extends BaseExecutionEvent {
484
+ type: "stream_complete";
485
+ /** Whether any gadgets were executed */
486
+ didExecuteGadgets: boolean;
487
+ /** Whether the agent loop should break */
488
+ shouldBreakLoop: boolean;
489
+ /** Total cost for this iteration */
490
+ iterationCost?: number;
491
+ }
512
492
  /**
513
- * Default prompt used for summarization strategy.
493
+ * All LLM-related events.
514
494
  */
515
- declare const DEFAULT_SUMMARIZATION_PROMPT = "Summarize this conversation history concisely, preserving:\n1. Key decisions made and their rationale\n2. Important facts and data discovered\n3. Errors encountered and how they were resolved\n4. Current task context and goals\n\nFormat as a brief narrative paragraph, not bullet points.\nPrevious conversation:";
495
+ type LLMEvent = LLMCallStartEvent | LLMCallStreamEvent | LLMCallCompleteEvent | LLMCallErrorEvent;
516
496
  /**
517
- * Resolved configuration with all defaults applied.
497
+ * All gadget-related events.
518
498
  */
519
- interface ResolvedCompactionConfig {
520
- enabled: boolean;
521
- strategy: "sliding-window" | "summarization" | "hybrid";
522
- triggerThresholdPercent: number;
523
- targetPercent: number;
524
- preserveRecentTurns: number;
525
- summarizationModel?: string;
526
- summarizationPrompt: string;
527
- onCompaction?: (event: CompactionEvent$1) => void;
528
- }
529
-
530
- interface LLMGenerationOptions {
531
- model: string;
532
- messages: LLMMessage[];
533
- maxTokens?: number;
534
- temperature?: number;
535
- topP?: number;
536
- stopSequences?: string[];
537
- responseFormat?: "text";
538
- metadata?: Record<string, unknown>;
539
- extra?: Record<string, unknown>;
540
- /**
541
- * Optional abort signal for cancelling the request mid-flight.
542
- *
543
- * When the signal is aborted, the provider will attempt to cancel
544
- * the underlying HTTP request and the stream will terminate with
545
- * an abort error. Use `isAbortError()` from `@/core/errors` to
546
- * detect cancellation in error handling.
547
- *
548
- * @example
549
- * ```typescript
550
- * const controller = new AbortController();
551
- *
552
- * const stream = client.stream({
553
- * model: "claude-3-5-sonnet-20241022",
554
- * messages: [{ role: "user", content: "Tell me a long story" }],
555
- * signal: controller.signal,
556
- * });
557
- *
558
- * // Cancel after 5 seconds
559
- * setTimeout(() => controller.abort(), 5000);
560
- *
561
- * try {
562
- * for await (const chunk of stream) {
563
- * process.stdout.write(chunk.text);
564
- * }
565
- * } catch (error) {
566
- * if (isAbortError(error)) {
567
- * console.log("\nRequest was cancelled");
568
- * } else {
569
- * throw error;
570
- * }
571
- * }
572
- * ```
573
- */
574
- signal?: AbortSignal;
575
- }
576
- interface TokenUsage {
577
- inputTokens: number;
578
- outputTokens: number;
579
- totalTokens: number;
580
- /** Number of input tokens served from cache (subset of inputTokens) */
581
- cachedInputTokens?: number;
582
- /** Number of input tokens written to cache (subset of inputTokens, Anthropic only) */
583
- cacheCreationInputTokens?: number;
584
- }
585
- interface LLMStreamChunk {
586
- text: string;
587
- /**
588
- * Indicates that the provider has finished producing output and includes the reason if available.
589
- */
590
- finishReason?: string | null;
591
- /**
592
- * Token usage information, typically available in the final chunk when the stream completes.
593
- */
594
- usage?: TokenUsage;
595
- /**
596
- * Provider specific payload emitted at the same time as the text chunk. This is useful for debugging and tests.
597
- */
598
- rawEvent?: unknown;
599
- }
600
- interface LLMStream extends AsyncIterable<LLMStreamChunk> {
601
- }
602
- type ProviderIdentifier = string;
603
- interface ModelDescriptor {
604
- provider: string;
605
- name: string;
606
- }
607
- declare class ModelIdentifierParser {
608
- private readonly defaultProvider;
609
- constructor(defaultProvider?: string);
610
- parse(identifier: string): ModelDescriptor;
611
- }
499
+ type GadgetEvent = GadgetCallEvent | GadgetStartEvent | GadgetCompleteEvent | GadgetErrorEvent | GadgetSkippedEvent$1;
500
+ /**
501
+ * Union of all execution events.
502
+ */
503
+ type ExecutionEvent = LLMCallStartEvent | LLMCallStreamEvent | LLMCallCompleteEvent | LLMCallErrorEvent | GadgetCallEvent | GadgetStartEvent | GadgetCompleteEvent | GadgetErrorEvent | GadgetSkippedEvent$1 | TextEvent | CompactionEvent$1 | HumanInputRequiredEvent | StreamCompleteEvent;
504
+ /**
505
+ * Event type discriminator.
506
+ */
507
+ type ExecutionEventType = ExecutionEvent["type"] | "*";
508
+ /**
509
+ * Check if an event is an LLM event.
510
+ */
511
+ declare function isLLMEvent(event: ExecutionEvent): event is LLMEvent;
512
+ /**
513
+ * Check if an event is a gadget event.
514
+ */
515
+ declare function isGadgetEvent(event: ExecutionEvent): event is GadgetEvent;
516
+ /**
517
+ * Check if an event is from a subagent (nested execution).
518
+ */
519
+ declare function isSubagentEvent(event: ExecutionEvent): boolean;
520
+ /**
521
+ * Check if an event is from the root agent.
522
+ */
523
+ declare function isRootEvent(event: ExecutionEvent): boolean;
524
+ /**
525
+ * Filter events by depth.
526
+ */
527
+ declare function filterByDepth(events: ExecutionEvent[], depth: number): ExecutionEvent[];
528
+ /**
529
+ * Filter events by parent node.
530
+ */
531
+ declare function filterByParent(events: ExecutionEvent[], parentId: string): ExecutionEvent[];
532
+ /**
533
+ * Filter events to only root-level events.
534
+ */
535
+ declare function filterRootEvents(events: ExecutionEvent[]): ExecutionEvent[];
536
+ /**
537
+ * Group events by their parent node.
538
+ */
539
+ declare function groupByParent(events: ExecutionEvent[]): Map<string | null, ExecutionEvent[]>;
612
540
 
613
541
  /**
614
- * Unified event types for the Execution Tree.
542
+ * First-class Execution Tree model for nested subagent support.
615
543
  *
616
- * All events carry full tree context (nodeId, parentId, depth, path).
617
- * No special SubagentEvent wrapper needed - subagent events are regular
618
- * events with depth > 0.
544
+ * The ExecutionTree is THE single source of truth for execution state.
545
+ * All nodes (including nested subagent nodes) live in one tree.
546
+ * Events are projections of tree changes.
619
547
  *
620
- * @module core/execution-events
548
+ * @module core/execution-tree
621
549
  */
622
550
 
623
551
  /**
624
- * Base properties shared by all execution events.
625
- * Every event carries full tree context.
552
+ * Unique identifier for any execution node.
553
+ * Format examples: "llm_1", "gadget_abc123", "llm_1_2" (nested)
626
554
  */
627
- interface BaseExecutionEvent {
628
- /** Monotonically increasing event ID */
629
- eventId: number;
630
- /** Event timestamp */
631
- timestamp: number;
632
- /** Node that emitted this event */
633
- nodeId: string;
634
- /** Parent node ID (null for root events) */
635
- parentId: string | null;
636
- /** Nesting depth (0 = root, 1 = child, etc.) */
555
+ type NodeId = string;
556
+ /**
557
+ * Node type discriminator.
558
+ */
559
+ type ExecutionNodeType = "llm_call" | "gadget";
560
+ /**
561
+ * Base properties shared by all execution nodes.
562
+ */
563
+ interface BaseExecutionNode {
564
+ /** Unique identifier for this node */
565
+ id: NodeId;
566
+ /** Node type discriminator */
567
+ type: ExecutionNodeType;
568
+ /** Parent node ID (null for root nodes) */
569
+ parentId: NodeId | null;
570
+ /** Nesting depth (0 = root, 1 = child of gadget, etc.) */
637
571
  depth: number;
638
- /** Full path from root to this node */
639
- path: string[];
572
+ /** Path from root to this node: ["llm_1", "gadget_abc", "llm_1_1"] */
573
+ path: NodeId[];
574
+ /** Creation timestamp */
575
+ createdAt: number;
576
+ /** Completion timestamp (null if in progress) */
577
+ completedAt: number | null;
640
578
  }
641
579
  /**
642
- * Emitted when an LLM call starts.
580
+ * LLM call execution node.
643
581
  */
644
- interface LLMCallStartEvent extends BaseExecutionEvent {
645
- type: "llm_call_start";
646
- /** Iteration number within agent loop (1-indexed) */
582
+ interface LLMCallNode extends BaseExecutionNode {
583
+ type: "llm_call";
584
+ /** Iteration number within the agent loop (1-indexed for display) */
647
585
  iteration: number;
648
586
  /** Model identifier */
649
587
  model: string;
650
- /** Request messages */
588
+ /** Request messages (set when call starts) */
651
589
  request?: LLMMessage[];
590
+ /** Accumulated response text */
591
+ response: string;
592
+ /** Token usage (set on completion) */
593
+ usage?: TokenUsage;
594
+ /** Finish reason from LLM */
595
+ finishReason?: string | null;
596
+ /** Cost in USD */
597
+ cost?: number;
598
+ /** Child node IDs (gadgets spawned by this LLM call) */
599
+ children: NodeId[];
652
600
  }
653
601
  /**
654
- * Emitted for each streaming chunk from LLM.
602
+ * Gadget execution state.
655
603
  */
656
- interface LLMCallStreamEvent extends BaseExecutionEvent {
657
- type: "llm_call_stream";
658
- /** Text chunk */
659
- chunk: string;
660
- }
604
+ type GadgetState = "pending" | "running" | "completed" | "failed" | "skipped";
661
605
  /**
662
- * Emitted when an LLM call completes successfully.
606
+ * Gadget execution node.
663
607
  */
664
- interface LLMCallCompleteEvent extends BaseExecutionEvent {
665
- type: "llm_call_complete";
666
- /** Complete response text */
667
- response: string;
668
- /** Token usage */
669
- usage?: TokenUsage;
670
- /** Finish reason from LLM */
671
- finishReason?: string | null;
608
+ interface GadgetNode extends BaseExecutionNode {
609
+ type: "gadget";
610
+ /** Invocation ID (LLM-generated or auto) */
611
+ invocationId: string;
612
+ /** Gadget name */
613
+ name: string;
614
+ /** Parameters passed to the gadget */
615
+ parameters: Record<string, unknown>;
616
+ /** Dependencies (other invocation IDs this gadget waits for) */
617
+ dependencies: string[];
618
+ /** Execution state */
619
+ state: GadgetState;
620
+ /** Result string (if completed successfully) */
621
+ result?: string;
622
+ /** Error message (if failed or skipped) */
623
+ error?: string;
624
+ /** Failed dependency invocation ID (if skipped due to dependency) */
625
+ failedDependency?: string;
626
+ /** Execution time in milliseconds */
627
+ executionTimeMs?: number;
672
628
  /** Cost in USD */
673
629
  cost?: number;
630
+ /** Media outputs from this gadget */
631
+ media?: GadgetMediaOutput[];
632
+ /** Child node IDs (nested LLM calls for subagent gadgets) */
633
+ children: NodeId[];
634
+ /** Whether this gadget is a subagent (has nested LLM calls) */
635
+ isSubagent: boolean;
674
636
  }
675
637
  /**
676
- * Emitted when an LLM call fails.
638
+ * Union of all execution node types.
677
639
  */
678
- interface LLMCallErrorEvent extends BaseExecutionEvent {
679
- type: "llm_call_error";
680
- /** The error that occurred */
681
- error: Error;
682
- /** Whether the error was recovered by a controller */
683
- recovered: boolean;
640
+ type ExecutionNode = LLMCallNode | GadgetNode;
641
+ interface AddLLMCallParams {
642
+ /** Iteration number (1-indexed) */
643
+ iteration: number;
644
+ /** Model identifier */
645
+ model: string;
646
+ /** Request messages */
647
+ request?: LLMMessage[];
648
+ /** Parent node ID (for subagent LLM calls) */
649
+ parentId?: NodeId | null;
684
650
  }
685
- /**
686
- * Emitted when a gadget call is parsed from LLM output (before execution).
687
- */
688
- interface GadgetCallEvent extends BaseExecutionEvent {
689
- type: "gadget_call";
651
+ interface AddGadgetParams {
690
652
  /** Invocation ID */
691
653
  invocationId: string;
692
654
  /** Gadget name */
693
655
  name: string;
694
656
  /** Parameters */
695
657
  parameters: Record<string, unknown>;
696
- /** Dependencies (other invocation IDs) */
697
- dependencies: string[];
658
+ /** Dependencies */
659
+ dependencies?: string[];
660
+ /** Parent LLM call node ID */
661
+ parentId?: NodeId | null;
698
662
  }
699
- /**
700
- * Emitted when gadget execution starts.
701
- */
702
- interface GadgetStartEvent extends BaseExecutionEvent {
703
- type: "gadget_start";
704
- /** Invocation ID */
705
- invocationId: string;
706
- /** Gadget name */
707
- name: string;
663
+ interface CompleteLLMCallParams {
664
+ /** Accumulated response text */
665
+ response?: string;
666
+ /** Token usage */
667
+ usage?: TokenUsage;
668
+ /** Finish reason */
669
+ finishReason?: string | null;
670
+ /** Cost in USD */
671
+ cost?: number;
708
672
  }
709
- /**
710
- * Emitted when gadget execution completes successfully.
711
- */
712
- interface GadgetCompleteEvent extends BaseExecutionEvent {
713
- type: "gadget_complete";
714
- /** Invocation ID */
715
- invocationId: string;
716
- /** Gadget name */
717
- name: string;
673
+ interface CompleteGadgetParams {
718
674
  /** Result string */
719
- result: string;
675
+ result?: string;
676
+ /** Error message */
677
+ error?: string;
678
+ /** Failed dependency (for skipped gadgets) */
679
+ failedDependency?: string;
720
680
  /** Execution time in ms */
721
- executionTimeMs: number;
681
+ executionTimeMs?: number;
722
682
  /** Cost in USD */
723
683
  cost?: number;
724
684
  /** Media outputs */
725
685
  media?: GadgetMediaOutput[];
726
686
  }
687
+
688
+ /** Event listener function type */
689
+ type EventListener = (event: ExecutionEvent) => void;
727
690
  /**
728
- * Emitted when gadget execution fails.
729
- */
730
- interface GadgetErrorEvent extends BaseExecutionEvent {
731
- type: "gadget_error";
732
- /** Invocation ID */
733
- invocationId: string;
734
- /** Gadget name */
735
- name: string;
736
- /** Error message */
737
- error: string;
738
- /** Execution time in ms */
739
- executionTimeMs: number;
740
- }
741
- /**
742
- * Emitted when a gadget is skipped.
743
- */
744
- interface GadgetSkippedEvent$1 extends BaseExecutionEvent {
745
- type: "gadget_skipped";
746
- /** Invocation ID */
747
- invocationId: string;
748
- /** Gadget name */
749
- name: string;
750
- /** Reason for skipping */
751
- reason: "dependency_failed" | "controller_skip";
752
- /** Error message (combines reason and failedDependencyError for consistency with GadgetErrorEvent) */
753
- error: string;
754
- /** Failed dependency invocation ID (if dependency_failed) */
755
- failedDependency?: string;
756
- /** Error message from failed dependency */
757
- failedDependencyError?: string;
758
- }
759
- /**
760
- * Emitted for text output from LLM (pure notification, not a tree node).
691
+ * The Execution Tree - single source of truth for all execution state.
692
+ *
693
+ * Features:
694
+ * - Stores all nodes (LLM calls, gadgets) in a hierarchical structure
695
+ * - Emits events on mutations
696
+ * - Provides query methods for aggregation (costs, media, descendants)
697
+ * - Supports single shared tree model for nested subagents
698
+ *
699
+ * @example
700
+ * ```typescript
701
+ * const tree = new ExecutionTree();
702
+ *
703
+ * // Add root LLM call
704
+ * const llmNode = tree.addLLMCall({ iteration: 1, model: "sonnet" });
705
+ *
706
+ * // Add gadget under the LLM call
707
+ * const gadgetNode = tree.addGadget({
708
+ * invocationId: "gc_1",
709
+ * name: "ReadFile",
710
+ * parameters: { path: "/foo.txt" },
711
+ * parentId: llmNode.id,
712
+ * });
713
+ *
714
+ * // Complete the gadget
715
+ * tree.completeGadget(gadgetNode.id, { result: "file contents", executionTimeMs: 50 });
716
+ *
717
+ * // Query total cost
718
+ * console.log(tree.getTotalCost());
719
+ * ```
761
720
  */
762
- interface TextEvent extends BaseExecutionEvent {
763
- type: "text";
764
- /** Text content */
765
- content: string;
721
+ declare class ExecutionTree {
722
+ private nodes;
723
+ private rootIds;
724
+ private eventListeners;
725
+ private eventIdCounter;
726
+ private invocationIdToNodeId;
727
+ private eventQueue;
728
+ private eventWaiters;
729
+ private isCompleted;
730
+ /**
731
+ * Base depth for all nodes in this tree.
732
+ * Used when this tree is a subagent's view into a parent tree.
733
+ */
734
+ readonly baseDepth: number;
735
+ /**
736
+ * Parent node ID for subagent trees.
737
+ * All root nodes in this tree will have this as their parentId.
738
+ */
739
+ readonly parentNodeId: NodeId | null;
740
+ constructor(options?: {
741
+ baseDepth?: number;
742
+ parentNodeId?: NodeId | null;
743
+ });
744
+ private generateLLMCallId;
745
+ private gadgetIdCounter;
746
+ private generateGadgetId;
747
+ private emit;
748
+ private createBaseEventProps;
749
+ /**
750
+ * Add a new LLM call node to the tree.
751
+ */
752
+ addLLMCall(params: AddLLMCallParams): LLMCallNode;
753
+ /**
754
+ * Add text to an LLM call's response (for streaming).
755
+ */
756
+ appendLLMResponse(nodeId: NodeId, chunk: string): void;
757
+ /**
758
+ * Complete an LLM call node.
759
+ */
760
+ completeLLMCall(nodeId: NodeId, params: CompleteLLMCallParams): void;
761
+ /**
762
+ * Mark an LLM call as failed.
763
+ */
764
+ failLLMCall(nodeId: NodeId, error: Error, recovered: boolean): void;
765
+ /**
766
+ * Add a new gadget node to the tree.
767
+ */
768
+ addGadget(params: AddGadgetParams): GadgetNode;
769
+ /**
770
+ * Mark a gadget as started (running).
771
+ */
772
+ startGadget(nodeId: NodeId): void;
773
+ /**
774
+ * Complete a gadget node successfully.
775
+ */
776
+ completeGadget(nodeId: NodeId, params: CompleteGadgetParams): void;
777
+ /**
778
+ * Mark a gadget as skipped due to dependency failure.
779
+ */
780
+ skipGadget(nodeId: NodeId, failedDependency: string, failedDependencyError: string, reason: "dependency_failed" | "controller_skip"): void;
781
+ /**
782
+ * Emit a text event (notification only, not stored in tree).
783
+ */
784
+ emitText(content: string, llmCallNodeId: NodeId): void;
785
+ /**
786
+ * Get a node by ID.
787
+ */
788
+ getNode(id: NodeId): ExecutionNode | undefined;
789
+ /**
790
+ * Get a gadget node by invocation ID.
791
+ */
792
+ getNodeByInvocationId(invocationId: string): GadgetNode | undefined;
793
+ /**
794
+ * Get all root nodes (depth 0 for this tree).
795
+ */
796
+ getRoots(): ExecutionNode[];
797
+ /**
798
+ * Get children of a node.
799
+ */
800
+ getChildren(id: NodeId): ExecutionNode[];
801
+ /**
802
+ * Get ancestors of a node (from root to parent).
803
+ */
804
+ getAncestors(id: NodeId): ExecutionNode[];
805
+ /**
806
+ * Get all descendants of a node.
807
+ */
808
+ getDescendants(id: NodeId, type?: ExecutionNodeType): ExecutionNode[];
809
+ /**
810
+ * Get the current (most recent incomplete) LLM call node.
811
+ */
812
+ getCurrentLLMCallId(): NodeId | undefined;
813
+ /**
814
+ * Get total cost for entire tree.
815
+ */
816
+ getTotalCost(): number;
817
+ /**
818
+ * Get total cost for a subtree (node and all descendants).
819
+ */
820
+ getSubtreeCost(nodeId: NodeId): number;
821
+ /**
822
+ * Get token usage for entire tree.
823
+ */
824
+ getTotalTokens(): {
825
+ input: number;
826
+ output: number;
827
+ cached: number;
828
+ };
829
+ /**
830
+ * Get token usage for a subtree.
831
+ */
832
+ getSubtreeTokens(nodeId: NodeId): {
833
+ input: number;
834
+ output: number;
835
+ cached: number;
836
+ };
837
+ /**
838
+ * Collect all media from a subtree.
839
+ */
840
+ getSubtreeMedia(nodeId: NodeId): GadgetMediaOutput[];
841
+ /**
842
+ * Check if a subtree is complete (all nodes finished).
843
+ */
844
+ isSubtreeComplete(nodeId: NodeId): boolean;
845
+ /**
846
+ * Get node counts.
847
+ */
848
+ getNodeCount(): {
849
+ llmCalls: number;
850
+ gadgets: number;
851
+ };
852
+ /**
853
+ * Subscribe to events of a specific type.
854
+ * Returns unsubscribe function.
855
+ *
856
+ * @param type - Event type to subscribe to (use "*" for all events)
857
+ * @param listener - Callback function that receives matching events
858
+ * @returns Unsubscribe function
859
+ *
860
+ * @example
861
+ * ```typescript
862
+ * const unsubscribe = tree.on("gadget_complete", (event) => {
863
+ * if (event.type === "gadget_complete") {
864
+ * console.log(`Gadget ${event.name} completed`);
865
+ * }
866
+ * });
867
+ * ```
868
+ */
869
+ on(type: ExecutionEventType, listener: EventListener): () => void;
870
+ /**
871
+ * Subscribe to all events.
872
+ */
873
+ onAll(listener: EventListener): () => void;
874
+ /**
875
+ * Get async iterable of all events.
876
+ * Events are yielded as they occur.
877
+ */
878
+ events(): AsyncGenerator<ExecutionEvent>;
879
+ /**
880
+ * Mark the tree as complete (no more events will be emitted).
881
+ */
882
+ complete(): void;
883
+ /**
884
+ * Check if the tree is complete.
885
+ */
886
+ isComplete(): boolean;
766
887
  }
888
+
767
889
  /**
768
- * Emitted when context compaction occurs.
890
+ * Function-based gadget creation helper.
891
+ *
892
+ * For simple gadgets, use createGadget() instead of defining a class.
893
+ * Parameters are automatically typed from the Zod schema.
894
+ *
895
+ * @example
896
+ * ```typescript
897
+ * const calculator = createGadget({
898
+ * description: "Performs arithmetic operations",
899
+ * schema: z.object({
900
+ * operation: z.enum(["add", "subtract"]),
901
+ * a: z.number(),
902
+ * b: z.number(),
903
+ * }),
904
+ * execute: ({ operation, a, b }) => {
905
+ * // Automatically typed!
906
+ * return operation === "add" ? String(a + b) : String(a - b);
907
+ * },
908
+ * });
909
+ * ```
769
910
  */
770
- interface CompactionEvent extends BaseExecutionEvent {
771
- type: "compaction";
772
- /** Tokens before compaction */
773
- tokensBefore: number;
774
- /** Tokens after compaction */
775
- tokensAfter: number;
776
- /** Compaction strategy used */
777
- strategy: string;
778
- /** Messages removed */
779
- messagesRemoved: number;
780
- }
911
+
781
912
  /**
782
- * Emitted when human input is required.
913
+ * Infer the TypeScript type from a Zod schema.
783
914
  */
784
- interface HumanInputRequiredEvent extends BaseExecutionEvent {
785
- type: "human_input_required";
786
- /** Question for the user */
787
- question: string;
788
- /** Gadget name requesting input */
789
- gadgetName: string;
790
- /** Invocation ID */
791
- invocationId: string;
792
- }
915
+ type InferSchema$1<T> = T extends ZodType<infer U> ? U : never;
793
916
  /**
794
- * Emitted when the execution stream completes.
917
+ * Configuration for creating a function-based gadget.
795
918
  */
796
- interface StreamCompleteEvent extends BaseExecutionEvent {
797
- type: "stream_complete";
798
- /** Whether any gadgets were executed */
799
- didExecuteGadgets: boolean;
800
- /** Whether the agent loop should break */
801
- shouldBreakLoop: boolean;
802
- /** Total cost for this iteration */
803
- iterationCost?: number;
919
+ interface CreateGadgetConfig<TSchema extends ZodType> {
920
+ /** Optional custom name (defaults to "FunctionGadget") */
921
+ name?: string;
922
+ /** Human-readable description of what the gadget does */
923
+ description: string;
924
+ /** Zod schema for parameter validation */
925
+ schema: TSchema;
926
+ /**
927
+ * Execution function with typed parameters.
928
+ * Can return string or { result, cost? }.
929
+ * Optionally receives ExecutionContext for callback-based cost reporting.
930
+ */
931
+ execute: (params: InferSchema$1<TSchema>, ctx?: ExecutionContext) => GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
932
+ /** Optional timeout in milliseconds */
933
+ timeoutMs?: number;
934
+ /** Optional usage examples to help LLMs understand proper invocation */
935
+ examples?: GadgetExample<InferSchema$1<TSchema>>[];
804
936
  }
805
937
  /**
806
- * All LLM-related events.
807
- */
808
- type LLMEvent = LLMCallStartEvent | LLMCallStreamEvent | LLMCallCompleteEvent | LLMCallErrorEvent;
809
- /**
810
- * All gadget-related events.
811
- */
812
- type GadgetEvent = GadgetCallEvent | GadgetStartEvent | GadgetCompleteEvent | GadgetErrorEvent | GadgetSkippedEvent$1;
813
- /**
814
- * Union of all execution events.
815
- */
816
- type ExecutionEvent = LLMCallStartEvent | LLMCallStreamEvent | LLMCallCompleteEvent | LLMCallErrorEvent | GadgetCallEvent | GadgetStartEvent | GadgetCompleteEvent | GadgetErrorEvent | GadgetSkippedEvent$1 | TextEvent | CompactionEvent | HumanInputRequiredEvent | StreamCompleteEvent;
817
- /**
818
- * Event type discriminator.
819
- */
820
- type ExecutionEventType = ExecutionEvent["type"] | "*";
821
- /**
822
- * Check if an event is an LLM event.
823
- */
824
- declare function isLLMEvent(event: ExecutionEvent): event is LLMEvent;
825
- /**
826
- * Check if an event is a gadget event.
827
- */
828
- declare function isGadgetEvent(event: ExecutionEvent): event is GadgetEvent;
829
- /**
830
- * Check if an event is from a subagent (nested execution).
938
+ * Creates a gadget from a function (simpler than class-based approach).
939
+ *
940
+ * This is perfect for simple gadgets where you don't need the full
941
+ * power of a class. Parameters are automatically typed from the schema.
942
+ *
943
+ * @param config - Configuration with execute function and schema
944
+ * @returns Gadget instance ready to be registered
945
+ *
946
+ * @example
947
+ * ```typescript
948
+ * import { z } from 'zod';
949
+ * import { createGadget } from 'llmist';
950
+ *
951
+ * // Simple calculator gadget
952
+ * const calculator = createGadget({
953
+ * description: "Performs arithmetic operations",
954
+ * schema: z.object({
955
+ * operation: z.enum(["add", "subtract", "multiply", "divide"]),
956
+ * a: z.number().describe("First number"),
957
+ * b: z.number().describe("Second number"),
958
+ * }),
959
+ * execute: ({ operation, a, b }) => {
960
+ * // Parameters are automatically typed!
961
+ * switch (operation) {
962
+ * case "add": return String(a + b);
963
+ * case "subtract": return String(a - b);
964
+ * case "multiply": return String(a * b);
965
+ * case "divide": return String(a / b);
966
+ * }
967
+ * },
968
+ * });
969
+ * ```
970
+ *
971
+ * @example
972
+ * ```typescript
973
+ * // Async gadget with custom name and timeout
974
+ * const weather = createGadget({
975
+ * name: "weather",
976
+ * description: "Fetches current weather for a city",
977
+ * schema: z.object({
978
+ * city: z.string().min(1).describe("City name"),
979
+ * }),
980
+ * timeoutMs: 10000,
981
+ * execute: async ({ city }) => {
982
+ * const response = await fetch(`https://api.weather.com/${city}`);
983
+ * const data = await response.json();
984
+ * return `Weather in ${city}: ${data.description}, ${data.temp}°C`;
985
+ * },
986
+ * });
987
+ * ```
988
+ *
989
+ * @example
990
+ * ```typescript
991
+ * // Use with agent
992
+ * const agent = LLMist.createAgent()
993
+ * .withGadgets(calculator, weather)
994
+ * .ask("What's the weather in Paris and what's 10 + 5?");
995
+ * ```
831
996
  */
832
- declare function isSubagentEvent(event: ExecutionEvent): boolean;
997
+ declare function createGadget<TSchema extends ZodType>(config: CreateGadgetConfig<TSchema>): AbstractGadget;
998
+
833
999
  /**
834
- * Check if an event is from the root agent.
1000
+ * Type-safe gadget factory with automatic parameter inference.
1001
+ *
1002
+ * Gadget eliminates the need for manual type assertions
1003
+ * by automatically inferring parameter types from the Zod schema.
1004
+ *
1005
+ * @example
1006
+ * ```typescript
1007
+ * class Calculator extends Gadget({
1008
+ * description: "Performs arithmetic operations",
1009
+ * schema: z.object({
1010
+ * operation: z.enum(["add", "subtract"]),
1011
+ * a: z.number(),
1012
+ * b: z.number(),
1013
+ * }),
1014
+ * }) {
1015
+ * // ✨ params is automatically typed!
1016
+ * execute(params: this['params']): string {
1017
+ * const { operation, a, b } = params; // All typed!
1018
+ * return operation === "add" ? String(a + b) : String(a - b);
1019
+ * }
1020
+ * }
1021
+ * ```
835
1022
  */
836
- declare function isRootEvent(event: ExecutionEvent): boolean;
1023
+
837
1024
  /**
838
- * Filter events by depth.
1025
+ * Infer the TypeScript type from a Zod schema.
839
1026
  */
840
- declare function filterByDepth(events: ExecutionEvent[], depth: number): ExecutionEvent[];
1027
+ type InferSchema<T> = T extends ZodType<infer U> ? U : never;
841
1028
  /**
842
- * Filter events by parent node.
1029
+ * Configuration for creating a typed gadget.
843
1030
  */
844
- declare function filterByParent(events: ExecutionEvent[], parentId: string): ExecutionEvent[];
1031
+ interface GadgetConfig<TSchema extends ZodType> {
1032
+ /** Human-readable description of what the gadget does */
1033
+ description: string;
1034
+ /** Zod schema for parameter validation */
1035
+ schema: TSchema;
1036
+ /** Optional custom name (defaults to class name) */
1037
+ name?: string;
1038
+ /** Optional timeout in milliseconds */
1039
+ timeoutMs?: number;
1040
+ /** Optional usage examples to help LLMs understand proper invocation */
1041
+ examples?: GadgetExample<InferSchema<TSchema>>[];
1042
+ }
845
1043
  /**
846
- * Filter events to only root-level events.
1044
+ * Factory function to create a typed gadget base class.
1045
+ *
1046
+ * The returned class automatically infers parameter types from the Zod schema,
1047
+ * eliminating the need for manual type assertions in the execute method.
1048
+ *
1049
+ * @param config - Configuration with description and schema
1050
+ * @returns Base class to extend with typed execute method
1051
+ *
1052
+ * @example
1053
+ * ```typescript
1054
+ * import { z } from 'zod';
1055
+ * import { Gadget } from 'llmist';
1056
+ *
1057
+ * class Calculator extends Gadget({
1058
+ * description: "Performs arithmetic operations",
1059
+ * schema: z.object({
1060
+ * operation: z.enum(["add", "subtract", "multiply", "divide"]),
1061
+ * a: z.number().describe("First number"),
1062
+ * b: z.number().describe("Second number"),
1063
+ * }),
1064
+ * }) {
1065
+ * execute(params: this['params']): string {
1066
+ * // params is automatically typed as:
1067
+ * // { operation: "add" | "subtract" | "multiply" | "divide"; a: number; b: number }
1068
+ * const { operation, a, b } = params;
1069
+ *
1070
+ * switch (operation) {
1071
+ * case "add": return String(a + b);
1072
+ * case "subtract": return String(a - b);
1073
+ * case "multiply": return String(a * b);
1074
+ * case "divide": return String(a / b);
1075
+ * }
1076
+ * }
1077
+ * }
1078
+ * ```
1079
+ *
1080
+ * @example
1081
+ * ```typescript
1082
+ * // With async execution
1083
+ * class WeatherGadget extends Gadget({
1084
+ * description: "Fetches weather for a city",
1085
+ * schema: z.object({
1086
+ * city: z.string().min(1).describe("City name"),
1087
+ * }),
1088
+ * timeoutMs: 10000,
1089
+ * }) {
1090
+ * async execute(params: this['params']): Promise<string> {
1091
+ * const { city } = params; // Automatically typed as { city: string }
1092
+ * const weather = await fetchWeather(city);
1093
+ * return `Weather in ${city}: ${weather}`;
1094
+ * }
1095
+ * }
1096
+ * ```
847
1097
  */
848
- declare function filterRootEvents(events: ExecutionEvent[]): ExecutionEvent[];
1098
+ declare function Gadget<TSchema extends ZodType>(config: GadgetConfig<TSchema>): {
1099
+ new (): {
1100
+ description: string;
1101
+ parameterSchema: TSchema;
1102
+ name: string | undefined;
1103
+ timeoutMs: number | undefined;
1104
+ examples: GadgetExample<InferSchema<TSchema>>[] | undefined;
1105
+ /**
1106
+ * Type helper property for accessing inferred parameter type.
1107
+ * This is used in the execute method signature: `execute(params: this['params'])`
1108
+ *
1109
+ * Note: This is just for type inference - the actual params in execute()
1110
+ * will be Record<string, unknown> which you can safely cast to this['params']
1111
+ */
1112
+ readonly params: InferSchema<TSchema>;
1113
+ /**
1114
+ * Execute the gadget. Subclasses should cast params to this['params'].
1115
+ *
1116
+ * @param params - Validated parameters from the LLM
1117
+ * @param ctx - Optional execution context for cost reporting and LLM access
1118
+ * @returns Result as a string, or an object with result and optional cost
1119
+ *
1120
+ * @example
1121
+ * ```typescript
1122
+ * // Simple string return (free gadget)
1123
+ * execute(params: this['params']) {
1124
+ * return String(params.a + params.b);
1125
+ * }
1126
+ *
1127
+ * // Using context for callback-based cost reporting
1128
+ * execute(params: this['params'], ctx) {
1129
+ * ctx.reportCost(0.001);
1130
+ * return "result";
1131
+ * }
1132
+ *
1133
+ * // Using wrapped LLMist for automatic cost tracking
1134
+ * async execute(params: this['params'], ctx) {
1135
+ * return ctx.llmist.complete('Summarize: ' + params.text);
1136
+ * }
1137
+ * ```
1138
+ */
1139
+ execute(params: Record<string, unknown>, ctx?: ExecutionContext): GadgetExecuteReturn | Promise<GadgetExecuteReturn>;
1140
+ throwIfAborted(ctx?: ExecutionContext): void;
1141
+ onAbort(ctx: ExecutionContext | undefined, cleanup: () => void | Promise<void>): void;
1142
+ createLinkedAbortController(ctx?: ExecutionContext): AbortController;
1143
+ get instruction(): string;
1144
+ getInstruction(optionsOrArgPrefix?: string | {
1145
+ argPrefix?: string;
1146
+ startPrefix?: string;
1147
+ endPrefix?: string;
1148
+ }): string;
1149
+ } & {
1150
+ params: InferSchema<TSchema>;
1151
+ };
1152
+ };
1153
+
849
1154
  /**
850
- * Group events by their parent node.
1155
+ * Model Catalog Types
1156
+ *
1157
+ * Type definitions for LLM model specifications including
1158
+ * context windows, pricing, features, and capabilities.
851
1159
  */
852
- declare function groupByParent(events: ExecutionEvent[]): Map<string | null, ExecutionEvent[]>;
1160
+ interface ModelPricing {
1161
+ /** Price per 1 million input tokens in USD */
1162
+ input: number;
1163
+ /** Price per 1 million output tokens in USD */
1164
+ output: number;
1165
+ /** Price per 1 million cached input tokens in USD (if supported) */
1166
+ cachedInput?: number;
1167
+ /** Price per 1 million cache write tokens in USD (Anthropic: 1.25x input price) */
1168
+ cacheWriteInput?: number;
1169
+ }
1170
+ interface ModelFeatures {
1171
+ /** Supports streaming responses */
1172
+ streaming: boolean;
1173
+ /** Supports function/tool calling */
1174
+ functionCalling: boolean;
1175
+ /** Supports vision/image input */
1176
+ vision: boolean;
1177
+ /** Supports extended thinking/reasoning */
1178
+ reasoning?: boolean;
1179
+ /** Supports structured outputs */
1180
+ structuredOutputs?: boolean;
1181
+ /** Supports fine-tuning */
1182
+ fineTuning?: boolean;
1183
+ }
1184
+ interface ModelSpec {
1185
+ /** Provider identifier (e.g., 'openai', 'anthropic', 'gemini') */
1186
+ provider: string;
1187
+ /** Full model identifier used in API calls */
1188
+ modelId: string;
1189
+ /** Human-readable display name */
1190
+ displayName: string;
1191
+ /** Maximum context window size in tokens */
1192
+ contextWindow: number;
1193
+ /** Maximum output tokens per request */
1194
+ maxOutputTokens: number;
1195
+ /** Pricing per 1M tokens */
1196
+ pricing: ModelPricing;
1197
+ /** Training data knowledge cutoff date (YYYY-MM-DD or description) */
1198
+ knowledgeCutoff: string;
1199
+ /** Supported features and capabilities */
1200
+ features: ModelFeatures;
1201
+ /** Additional metadata */
1202
+ metadata?: {
1203
+ /** Model family/series */
1204
+ family?: string;
1205
+ /** Release date */
1206
+ releaseDate?: string;
1207
+ /** Deprecation date if applicable */
1208
+ deprecationDate?: string;
1209
+ /** Notes or special information */
1210
+ notes?: string;
1211
+ /** Whether manual temperature configuration is supported (defaults to true) */
1212
+ supportsTemperature?: boolean;
1213
+ };
1214
+ }
1215
+ interface ModelLimits {
1216
+ contextWindow: number;
1217
+ maxOutputTokens: number;
1218
+ }
1219
+ interface CostEstimate {
1220
+ inputCost: number;
1221
+ /** Cost for cached input tokens (already included in inputCost calculation) */
1222
+ cachedInputCost: number;
1223
+ /** Cost for cache creation tokens (already included in inputCost calculation, Anthropic only) */
1224
+ cacheCreationCost: number;
1225
+ outputCost: number;
1226
+ totalCost: number;
1227
+ currency: "USD";
1228
+ }
853
1229
 
854
1230
  /**
855
- * First-class Execution Tree model for nested subagent support.
856
- *
857
- * The ExecutionTree is THE single source of truth for execution state.
858
- * All nodes (including nested subagent nodes) live in one tree.
859
- * Events are projections of tree changes.
1231
+ * Strategy interface for context compaction.
860
1232
  *
861
- * @module core/execution-tree
1233
+ * Strategies define how conversation history is compressed to fit within
1234
+ * context window limits. Different strategies trade off between:
1235
+ * - Speed (LLM calls vs local processing)
1236
+ * - Context preservation (summary quality vs simple truncation)
1237
+ * - Cost (summarization model usage)
862
1238
  */
863
1239
 
864
1240
  /**
865
- * Unique identifier for any execution node.
866
- * Format examples: "llm_1", "gadget_abc123", "llm_1_2" (nested)
867
- */
868
- type NodeId = string;
869
- /**
870
- * Node type discriminator.
1241
+ * Context provided to compaction strategies.
871
1242
  */
872
- type ExecutionNodeType = "llm_call" | "gadget";
1243
+ interface CompactionContext {
1244
+ /** Current token count of the conversation */
1245
+ currentTokens: number;
1246
+ /** Target token count after compaction */
1247
+ targetTokens: number;
1248
+ /** Model's context window limits */
1249
+ modelLimits: ModelLimits;
1250
+ /** LLMist client for summarization calls */
1251
+ client: LLMist;
1252
+ /** Model identifier for token counting and summarization */
1253
+ model: string;
1254
+ }
873
1255
  /**
874
- * Base properties shared by all execution nodes.
1256
+ * Result of a compaction operation.
875
1257
  */
876
- interface BaseExecutionNode {
877
- /** Unique identifier for this node */
878
- id: NodeId;
879
- /** Node type discriminator */
880
- type: ExecutionNodeType;
881
- /** Parent node ID (null for root nodes) */
882
- parentId: NodeId | null;
883
- /** Nesting depth (0 = root, 1 = child of gadget, etc.) */
884
- depth: number;
885
- /** Path from root to this node: ["llm_1", "gadget_abc", "llm_1_1"] */
886
- path: NodeId[];
887
- /** Creation timestamp */
888
- createdAt: number;
889
- /** Completion timestamp (null if in progress) */
890
- completedAt: number | null;
1258
+ interface CompactionResult {
1259
+ /** Compacted messages to replace history with */
1260
+ messages: LLMMessage[];
1261
+ /** Summary text if summarization was used */
1262
+ summary?: string;
1263
+ /** The name of the strategy that was ultimately executed */
1264
+ strategyName: string;
1265
+ /** Metadata about the compaction */
1266
+ metadata: {
1267
+ /** Number of messages before compaction */
1268
+ originalCount: number;
1269
+ /** Number of messages after compaction */
1270
+ compactedCount: number;
1271
+ /** Estimated tokens before compaction */
1272
+ tokensBefore: number;
1273
+ /** Estimated tokens after compaction */
1274
+ tokensAfter: number;
1275
+ };
891
1276
  }
892
1277
  /**
893
- * LLM call execution node.
1278
+ * Interface for compaction strategy implementations.
1279
+ *
1280
+ * Strategies receive the conversation history (excluding base messages like
1281
+ * system prompt and gadget instructions) and must return a compacted version.
1282
+ *
1283
+ * @example
1284
+ * ```typescript
1285
+ * class MyCustomStrategy implements CompactionStrategy {
1286
+ * readonly name = 'my-custom';
1287
+ *
1288
+ * async compact(
1289
+ * messages: LLMMessage[],
1290
+ * config: ResolvedCompactionConfig,
1291
+ * context: CompactionContext
1292
+ * ): Promise<CompactionResult> {
1293
+ * // Custom compaction logic
1294
+ * return {
1295
+ * messages: compactedMessages,
1296
+ * metadata: { ... }
1297
+ * };
1298
+ * }
1299
+ * }
1300
+ * ```
894
1301
  */
895
- interface LLMCallNode extends BaseExecutionNode {
896
- type: "llm_call";
897
- /** Iteration number within the agent loop (1-indexed for display) */
898
- iteration: number;
899
- /** Model identifier */
900
- model: string;
901
- /** Request messages (set when call starts) */
902
- request?: LLMMessage[];
903
- /** Accumulated response text */
904
- response: string;
905
- /** Token usage (set on completion) */
906
- usage?: TokenUsage;
907
- /** Finish reason from LLM */
908
- finishReason?: string | null;
909
- /** Cost in USD */
910
- cost?: number;
911
- /** Child node IDs (gadgets spawned by this LLM call) */
912
- children: NodeId[];
1302
+ interface CompactionStrategy {
1303
+ /** Human-readable name of the strategy */
1304
+ readonly name: string;
1305
+ /**
1306
+ * Compact the given messages to fit within target token count.
1307
+ *
1308
+ * @param messages - Conversation history messages (excludes system/gadget base)
1309
+ * @param config - Resolved compaction configuration
1310
+ * @param context - Context including token counts and LLM client
1311
+ * @returns Compacted messages with metadata
1312
+ */
1313
+ compact(messages: LLMMessage[], config: ResolvedCompactionConfig, context: CompactionContext): Promise<CompactionResult>;
913
1314
  }
914
1315
  /**
915
- * Gadget execution state.
1316
+ * Utility to group messages into logical conversation turns.
1317
+ *
1318
+ * A "turn" is typically a user message followed by an assistant response.
1319
+ * Gadget calls are grouped with the preceding assistant message.
916
1320
  */
917
- type GadgetState = "pending" | "running" | "completed" | "failed" | "skipped";
1321
+ interface MessageTurn {
1322
+ /** Messages in this turn (user + assistant + any gadget results) */
1323
+ messages: LLMMessage[];
1324
+ /** Estimated token count for this turn */
1325
+ tokenEstimate: number;
1326
+ }
1327
+
918
1328
  /**
919
- * Gadget execution node.
1329
+ * Configuration types for the context compaction system.
1330
+ *
1331
+ * Context compaction automatically manages conversation history to prevent
1332
+ * context window overflow in long-running agent conversations.
920
1333
  */
921
- interface GadgetNode extends BaseExecutionNode {
922
- type: "gadget";
923
- /** Invocation ID (LLM-generated or auto) */
924
- invocationId: string;
925
- /** Gadget name */
926
- name: string;
927
- /** Parameters passed to the gadget */
928
- parameters: Record<string, unknown>;
929
- /** Dependencies (other invocation IDs this gadget waits for) */
930
- dependencies: string[];
931
- /** Execution state */
932
- state: GadgetState;
933
- /** Result string (if completed successfully) */
934
- result?: string;
935
- /** Error message (if failed or skipped) */
936
- error?: string;
937
- /** Failed dependency invocation ID (if skipped due to dependency) */
938
- failedDependency?: string;
939
- /** Execution time in milliseconds */
940
- executionTimeMs?: number;
941
- /** Cost in USD */
942
- cost?: number;
943
- /** Media outputs from this gadget */
944
- media?: GadgetMediaOutput[];
945
- /** Child node IDs (nested LLM calls for subagent gadgets) */
946
- children: NodeId[];
947
- /** Whether this gadget is a subagent (has nested LLM calls) */
948
- isSubagent: boolean;
949
- }
1334
+
950
1335
  /**
951
- * Union of all execution node types.
1336
+ * Event emitted when compaction occurs.
1337
+ * This is included in StreamEvent for UI visibility.
952
1338
  */
953
- type ExecutionNode = LLMCallNode | GadgetNode;
954
- interface AddLLMCallParams {
955
- /** Iteration number (1-indexed) */
1339
+ interface CompactionEvent {
1340
+ /** The strategy that performed the compaction */
1341
+ strategy: string;
1342
+ /** Token count before compaction */
1343
+ tokensBefore: number;
1344
+ /** Token count after compaction */
1345
+ tokensAfter: number;
1346
+ /** Number of messages before compaction */
1347
+ messagesBefore: number;
1348
+ /** Number of messages after compaction */
1349
+ messagesAfter: number;
1350
+ /** Summary text if summarization was used */
1351
+ summary?: string;
1352
+ /** Agent iteration when compaction occurred */
956
1353
  iteration: number;
957
- /** Model identifier */
958
- model: string;
959
- /** Request messages */
960
- request?: LLMMessage[];
961
- /** Parent node ID (for subagent LLM calls) */
962
- parentId?: NodeId | null;
963
- }
964
- interface AddGadgetParams {
965
- /** Invocation ID */
966
- invocationId: string;
967
- /** Gadget name */
968
- name: string;
969
- /** Parameters */
970
- parameters: Record<string, unknown>;
971
- /** Dependencies */
972
- dependencies?: string[];
973
- /** Parent LLM call node ID */
974
- parentId?: NodeId | null;
975
- }
976
- interface CompleteLLMCallParams {
977
- /** Accumulated response text */
978
- response?: string;
979
- /** Token usage */
980
- usage?: TokenUsage;
981
- /** Finish reason */
982
- finishReason?: string | null;
983
- /** Cost in USD */
984
- cost?: number;
985
- }
986
- interface CompleteGadgetParams {
987
- /** Result string */
988
- result?: string;
989
- /** Error message */
990
- error?: string;
991
- /** Failed dependency (for skipped gadgets) */
992
- failedDependency?: string;
993
- /** Execution time in ms */
994
- executionTimeMs?: number;
995
- /** Cost in USD */
996
- cost?: number;
997
- /** Media outputs */
998
- media?: GadgetMediaOutput[];
999
1354
  }
1000
-
1001
- /** Event listener function type */
1002
- type EventListener = (event: ExecutionEvent) => void;
1003
1355
  /**
1004
- * The Execution Tree - single source of truth for all execution state.
1005
- *
1006
- * Features:
1007
- * - Stores all nodes (LLM calls, gadgets) in a hierarchical structure
1008
- * - Emits events on mutations
1009
- * - Provides query methods for aggregation (costs, media, descendants)
1010
- * - Supports single shared tree model for nested subagents
1356
+ * Statistics about compaction activity.
1357
+ */
1358
+ interface CompactionStats {
1359
+ /** Total number of compactions performed */
1360
+ totalCompactions: number;
1361
+ /** Total tokens saved across all compactions */
1362
+ totalTokensSaved: number;
1363
+ /** Current context usage */
1364
+ currentUsage: {
1365
+ tokens: number;
1366
+ percent: number;
1367
+ };
1368
+ /** Model's context window size */
1369
+ contextWindow: number;
1370
+ }
1371
+ /**
1372
+ * Configuration for the context compaction system.
1011
1373
  *
1012
1374
  * @example
1013
1375
  * ```typescript
1014
- * const tree = new ExecutionTree();
1015
- *
1016
- * // Add root LLM call
1017
- * const llmNode = tree.addLLMCall({ iteration: 1, model: "sonnet" });
1018
- *
1019
- * // Add gadget under the LLM call
1020
- * const gadgetNode = tree.addGadget({
1021
- * invocationId: "gc_1",
1022
- * name: "ReadFile",
1023
- * parameters: { path: "/foo.txt" },
1024
- * parentId: llmNode.id,
1025
- * });
1026
- *
1027
- * // Complete the gadget
1028
- * tree.completeGadget(gadgetNode.id, { result: "file contents", executionTimeMs: 50 });
1376
+ * // Custom configuration
1377
+ * const agent = await LLMist.createAgent()
1378
+ * .withModel('sonnet')
1379
+ * .withCompaction({
1380
+ * triggerThresholdPercent: 70,
1381
+ * targetPercent: 40,
1382
+ * preserveRecentTurns: 10,
1383
+ * })
1384
+ * .ask('...');
1029
1385
  *
1030
- * // Query total cost
1031
- * console.log(tree.getTotalCost());
1386
+ * // Disable compaction
1387
+ * const agent = await LLMist.createAgent()
1388
+ * .withModel('sonnet')
1389
+ * .withoutCompaction()
1390
+ * .ask('...');
1032
1391
  * ```
1033
1392
  */
1034
- declare class ExecutionTree {
1035
- private nodes;
1036
- private rootIds;
1037
- private eventListeners;
1038
- private eventIdCounter;
1039
- private invocationIdToNodeId;
1040
- private eventQueue;
1041
- private eventWaiters;
1042
- private isCompleted;
1043
- /**
1044
- * Base depth for all nodes in this tree.
1045
- * Used when this tree is a subagent's view into a parent tree.
1046
- */
1047
- readonly baseDepth: number;
1048
- /**
1049
- * Parent node ID for subagent trees.
1050
- * All root nodes in this tree will have this as their parentId.
1051
- */
1052
- readonly parentNodeId: NodeId | null;
1053
- constructor(options?: {
1054
- baseDepth?: number;
1055
- parentNodeId?: NodeId | null;
1056
- });
1057
- private generateLLMCallId;
1058
- private gadgetIdCounter;
1059
- private generateGadgetId;
1060
- private emit;
1061
- private createBaseEventProps;
1062
- /**
1063
- * Add a new LLM call node to the tree.
1064
- */
1065
- addLLMCall(params: AddLLMCallParams): LLMCallNode;
1066
- /**
1067
- * Add text to an LLM call's response (for streaming).
1068
- */
1069
- appendLLMResponse(nodeId: NodeId, chunk: string): void;
1070
- /**
1071
- * Complete an LLM call node.
1072
- */
1073
- completeLLMCall(nodeId: NodeId, params: CompleteLLMCallParams): void;
1074
- /**
1075
- * Mark an LLM call as failed.
1076
- */
1077
- failLLMCall(nodeId: NodeId, error: Error, recovered: boolean): void;
1078
- /**
1079
- * Add a new gadget node to the tree.
1080
- */
1081
- addGadget(params: AddGadgetParams): GadgetNode;
1082
- /**
1083
- * Mark a gadget as started (running).
1084
- */
1085
- startGadget(nodeId: NodeId): void;
1086
- /**
1087
- * Complete a gadget node successfully.
1088
- */
1089
- completeGadget(nodeId: NodeId, params: CompleteGadgetParams): void;
1090
- /**
1091
- * Mark a gadget as skipped due to dependency failure.
1092
- */
1093
- skipGadget(nodeId: NodeId, failedDependency: string, failedDependencyError: string, reason: "dependency_failed" | "controller_skip"): void;
1094
- /**
1095
- * Emit a text event (notification only, not stored in tree).
1096
- */
1097
- emitText(content: string, llmCallNodeId: NodeId): void;
1098
- /**
1099
- * Get a node by ID.
1100
- */
1101
- getNode(id: NodeId): ExecutionNode | undefined;
1102
- /**
1103
- * Get a gadget node by invocation ID.
1104
- */
1105
- getNodeByInvocationId(invocationId: string): GadgetNode | undefined;
1106
- /**
1107
- * Get all root nodes (depth 0 for this tree).
1108
- */
1109
- getRoots(): ExecutionNode[];
1110
- /**
1111
- * Get children of a node.
1112
- */
1113
- getChildren(id: NodeId): ExecutionNode[];
1114
- /**
1115
- * Get ancestors of a node (from root to parent).
1116
- */
1117
- getAncestors(id: NodeId): ExecutionNode[];
1118
- /**
1119
- * Get all descendants of a node.
1120
- */
1121
- getDescendants(id: NodeId, type?: ExecutionNodeType): ExecutionNode[];
1122
- /**
1123
- * Get the current (most recent incomplete) LLM call node.
1124
- */
1125
- getCurrentLLMCallId(): NodeId | undefined;
1126
- /**
1127
- * Get total cost for entire tree.
1128
- */
1129
- getTotalCost(): number;
1130
- /**
1131
- * Get total cost for a subtree (node and all descendants).
1132
- */
1133
- getSubtreeCost(nodeId: NodeId): number;
1134
- /**
1135
- * Get token usage for entire tree.
1136
- */
1137
- getTotalTokens(): {
1138
- input: number;
1139
- output: number;
1140
- cached: number;
1141
- };
1142
- /**
1143
- * Get token usage for a subtree.
1144
- */
1145
- getSubtreeTokens(nodeId: NodeId): {
1146
- input: number;
1147
- output: number;
1148
- cached: number;
1149
- };
1393
+ interface CompactionConfig {
1150
1394
  /**
1151
- * Collect all media from a subtree.
1395
+ * Enable or disable compaction.
1396
+ * @default true
1152
1397
  */
1153
- getSubtreeMedia(nodeId: NodeId): GadgetMediaOutput[];
1398
+ enabled?: boolean;
1154
1399
  /**
1155
- * Check if a subtree is complete (all nodes finished).
1400
+ * The compaction strategy to use.
1401
+ * - 'sliding-window': Fast, drops oldest turns (no LLM call)
1402
+ * - 'summarization': LLM-based compression of old messages
1403
+ * - 'hybrid': Summarizes old messages + keeps recent turns (recommended)
1404
+ * - Or provide a custom CompactionStrategy instance
1405
+ * @default 'hybrid'
1156
1406
  */
1157
- isSubtreeComplete(nodeId: NodeId): boolean;
1407
+ strategy?: "sliding-window" | "summarization" | "hybrid" | CompactionStrategy;
1158
1408
  /**
1159
- * Get node counts.
1409
+ * Context usage percentage that triggers compaction.
1410
+ * When token count exceeds this percentage of the context window,
1411
+ * compaction is performed before the next LLM call.
1412
+ * @default 80
1160
1413
  */
1161
- getNodeCount(): {
1162
- llmCalls: number;
1163
- gadgets: number;
1164
- };
1414
+ triggerThresholdPercent?: number;
1165
1415
  /**
1166
- * Subscribe to events of a specific type.
1167
- * Returns unsubscribe function.
1168
- *
1169
- * @param type - Event type to subscribe to (use "*" for all events)
1170
- * @param listener - Callback function that receives matching events
1171
- * @returns Unsubscribe function
1172
- *
1173
- * @example
1174
- * ```typescript
1175
- * const unsubscribe = tree.on("gadget_complete", (event) => {
1176
- * if (event.type === "gadget_complete") {
1177
- * console.log(`Gadget ${event.name} completed`);
1178
- * }
1179
- * });
1180
- * ```
1416
+ * Target context usage percentage after compaction.
1417
+ * The compaction will aim to reduce tokens to this percentage.
1418
+ * @default 50
1181
1419
  */
1182
- on(type: ExecutionEventType, listener: EventListener): () => void;
1420
+ targetPercent?: number;
1183
1421
  /**
1184
- * Subscribe to all events.
1422
+ * Number of recent turns to preserve during compaction.
1423
+ * A "turn" is a user message + assistant response pair.
1424
+ * Recent turns are kept verbatim while older ones are summarized/dropped.
1425
+ * @default 5
1185
1426
  */
1186
- onAll(listener: EventListener): () => void;
1427
+ preserveRecentTurns?: number;
1187
1428
  /**
1188
- * Get async iterable of all events.
1189
- * Events are yielded as they occur.
1429
+ * Model to use for summarization.
1430
+ * If not specified, uses the agent's model.
1431
+ * @default undefined (uses agent's model)
1190
1432
  */
1191
- events(): AsyncGenerator<ExecutionEvent>;
1433
+ summarizationModel?: string;
1192
1434
  /**
1193
- * Mark the tree as complete (no more events will be emitted).
1435
+ * Custom system prompt for summarization.
1436
+ * If not specified, uses a default prompt optimized for context preservation.
1194
1437
  */
1195
- complete(): void;
1438
+ summarizationPrompt?: string;
1196
1439
  /**
1197
- * Check if the tree is complete.
1440
+ * Callback invoked when compaction occurs.
1441
+ * Useful for logging or analytics.
1198
1442
  */
1199
- isComplete(): boolean;
1443
+ onCompaction?: (event: CompactionEvent) => void;
1444
+ }
1445
+ /**
1446
+ * Default configuration values for compaction.
1447
+ * Compaction is enabled by default with the hybrid strategy.
1448
+ */
1449
+ declare const DEFAULT_COMPACTION_CONFIG: Required<Omit<CompactionConfig, "summarizationModel" | "summarizationPrompt" | "onCompaction">>;
1450
+ /**
1451
+ * Default prompt used for summarization strategy.
1452
+ */
1453
+ declare const DEFAULT_SUMMARIZATION_PROMPT = "Summarize this conversation history concisely, preserving:\n1. Key decisions made and their rationale\n2. Important facts and data discovered\n3. Errors encountered and how they were resolved\n4. Current task context and goals\n\nFormat as a brief narrative paragraph, not bullet points.\nPrevious conversation:";
1454
+ /**
1455
+ * Resolved configuration with all defaults applied.
1456
+ */
1457
+ interface ResolvedCompactionConfig {
1458
+ enabled: boolean;
1459
+ strategy: "sliding-window" | "summarization" | "hybrid";
1460
+ triggerThresholdPercent: number;
1461
+ targetPercent: number;
1462
+ preserveRecentTurns: number;
1463
+ summarizationModel?: string;
1464
+ summarizationPrompt: string;
1465
+ onCompaction?: (event: CompactionEvent) => void;
1200
1466
  }
1201
1467
 
1202
1468
  /**
@@ -1610,7 +1876,7 @@ type StreamEvent = {
1610
1876
  invocationId: string;
1611
1877
  } | {
1612
1878
  type: "compaction";
1613
- event: CompactionEvent$1;
1879
+ event: CompactionEvent;
1614
1880
  } | SubagentStreamEvent | StreamCompletionEvent;
1615
1881
  /** Event for forwarding subagent activity through the stream */
1616
1882
  interface SubagentStreamEvent {
@@ -2110,6 +2376,52 @@ interface ExecutionContext {
2110
2376
  * ```
2111
2377
  */
2112
2378
  depth?: number;
2379
+ /**
2380
+ * Host llmist exports for external gadgets.
2381
+ *
2382
+ * External gadgets MUST use these instead of importing from 'llmist'
2383
+ * to ensure they use the same version as the host CLI, enabling proper
2384
+ * tree sharing and feature compatibility.
2385
+ *
2386
+ * Use the `getHostExports(ctx)` helper function to access these exports
2387
+ * with proper error handling.
2388
+ *
2389
+ * @example
2390
+ * ```typescript
2391
+ * import { getHostExports, Gadget, z } from 'llmist';
2392
+ *
2393
+ * class BrowseWeb extends Gadget({...}) {
2394
+ * async execute(params, ctx) {
2395
+ * const { AgentBuilder } = getHostExports(ctx);
2396
+ * const agent = new AgentBuilder()
2397
+ * .withParentContext(ctx)
2398
+ * .ask(params.task);
2399
+ * }
2400
+ * }
2401
+ * ```
2402
+ */
2403
+ hostExports?: HostExports;
2404
+ }
2405
+ /**
2406
+ * Host llmist exports provided to external gadgets via ExecutionContext.
2407
+ *
2408
+ * This ensures external gadgets use the same class instances as the host CLI,
2409
+ * enabling proper tree sharing and avoiding the "dual-package problem" where
2410
+ * different versions of llmist have incompatible classes.
2411
+ */
2412
+ interface HostExports {
2413
+ /** AgentBuilder for creating subagents with proper tree sharing */
2414
+ AgentBuilder: typeof AgentBuilder;
2415
+ /** Gadget factory for defining gadgets */
2416
+ Gadget: typeof Gadget;
2417
+ /** createGadget for functional gadget definitions */
2418
+ createGadget: typeof createGadget;
2419
+ /** ExecutionTree for tree operations */
2420
+ ExecutionTree: typeof ExecutionTree;
2421
+ /** LLMist client */
2422
+ LLMist: typeof LLMist;
2423
+ /** Zod schema builder */
2424
+ z: typeof zod.z;
2113
2425
  }
2114
2426
  /**
2115
2427
  * Parent agent configuration passed to gadgets.
@@ -3929,7 +4241,7 @@ interface ObserveCompactionContext {
3929
4241
  /** Agent iteration when compaction occurred */
3930
4242
  iteration: number;
3931
4243
  /** Details of the compaction event */
3932
- event: CompactionEvent$1;
4244
+ event: CompactionEvent;
3933
4245
  /** Cumulative compaction statistics */
3934
4246
  stats: CompactionStats;
3935
4247
  /** Logger instance */
@@ -4500,7 +4812,7 @@ declare class Agent {
4500
4812
  * }
4501
4813
  * ```
4502
4814
  */
4503
- compact(): Promise<CompactionEvent$1 | null>;
4815
+ compact(): Promise<CompactionEvent | null>;
4504
4816
  /**
4505
4817
  * Get compaction statistics.
4506
4818
  *
@@ -6030,4 +6342,4 @@ declare function createTextMockStream(text: string, options?: {
6030
6342
  usage?: MockResponse["usage"];
6031
6343
  }): LLMStream;
6032
6344
 
6033
- export { type ModelDescriptor as $, AbstractGadget as A, type MessageContent as B, type CompactionConfig as C, GadgetRegistry as D, MediaStore as E, type AgentContextConfig as F, type GadgetMediaOutput as G, type HintTemplate as H, type IConversationManager as I, type SubagentConfigMap as J, type SubagentEvent as K, type LLMMessage as L, MockProviderAdapter as M, ExecutionTree as N, type NodeId as O, type ExecutionContext as P, type GadgetExecuteReturn as Q, type ResolvedCompactionConfig as R, type StreamEvent as S, type TokenUsage as T, type GadgetExample as U, type ParsedGadgetCall as V, type GadgetExecutionResult as W, type MediaKind as X, type MediaMetadata as Y, type GadgetExecuteResultWithMedia as Z, type ProviderAdapter as _, type LLMStream as a, type GadgetSkippedEvent$1 as a$, type ModelSpec as a0, type LLMGenerationOptions as a1, type ImageModelSpec as a2, type ImageGenerationOptions as a3, type ImageGenerationResult as a4, type SpeechModelSpec as a5, type SpeechGenerationOptions as a6, type SpeechGenerationResult as a7, type HistoryMessage as a8, type TrailingMessage as a9, type ObserveGadgetCompleteContext as aA, type ObserveGadgetStartContext as aB, type ObserveLLMCallContext as aC, type ObserveLLMCompleteContext as aD, type ObserveLLMErrorContext as aE, type Observers as aF, type SubagentContext as aG, DEFAULT_COMPACTION_CONFIG as aH, DEFAULT_SUMMARIZATION_PROMPT as aI, type LLMistOptions as aJ, type AddGadgetParams as aK, type AddLLMCallParams as aL, type CompleteGadgetParams as aM, type CompleteLLMCallParams as aN, type ExecutionNode as aO, type ExecutionNodeType as aP, type GadgetNode as aQ, type GadgetState as aR, type LLMCallNode as aS, type BaseExecutionEvent as aT, type CompactionEvent as aU, type ExecutionEvent as aV, type ExecutionEventType as aW, type GadgetCallEvent as aX, type GadgetCompleteEvent as aY, type GadgetErrorEvent as aZ, type GadgetEvent as a_, type TrailingMessageContext as aa, AgentBuilder as ab, type EventHandlers as ac, collectEvents as ad, collectText as ae, runWithHandlers as af, type AfterGadgetExecutionAction as ag, type AfterGadgetExecutionControllerContext as ah, type AfterLLMCallAction as ai, type AfterLLMCallControllerContext as aj, type AfterLLMErrorAction as ak, type AgentOptions as al, type BeforeGadgetExecutionAction as am, type BeforeLLMCallAction as an, type ChunkInterceptorContext as ao, type Controllers as ap, type GadgetExecutionControllerContext as aq, type GadgetParameterInterceptorContext as ar, type GadgetResultInterceptorContext as as, type Interceptors as at, type LLMCallControllerContext as au, type LLMErrorControllerContext as av, type MessageInterceptorContext as aw, type MessageTurn as ax, type ObserveChunkContext as ay, type ObserveCompactionContext as az, type LLMStreamChunk as b, complete as b$, type GadgetStartEvent as b0, type HumanInputRequiredEvent as b1, type LLMCallCompleteEvent as b2, type LLMCallErrorEvent as b3, type LLMCallStartEvent as b4, type LLMCallStreamEvent as b5, type LLMEvent as b6, type StreamCompleteEvent as b7, type TextEvent as b8, filterByDepth as b9, isImagePart as bA, isTextPart as bB, parseDataUrl as bC, text as bD, toBase64 as bE, type MessageRole as bF, extractMessageText as bG, LLMMessageBuilder as bH, normalizeMessageContent as bI, type CostEstimate as bJ, type ModelFeatures as bK, type ModelLimits as bL, type ModelPricing as bM, type VisionAnalyzeOptions as bN, type VisionAnalyzeResult as bO, type ProviderIdentifier as bP, ModelIdentifierParser as bQ, type HintContext as bR, type PromptContext as bS, type PromptTemplate as bT, type PromptTemplateConfig as bU, DEFAULT_HINTS as bV, DEFAULT_PROMPTS as bW, resolveHintTemplate as bX, resolvePromptTemplate as bY, resolveRulesTemplate as bZ, type TextGenerationOptions as b_, filterByParent as ba, filterRootEvents as bb, groupByParent as bc, isGadgetEvent as bd, isLLMEvent as be, isRootEvent as bf, isSubagentEvent as bg, type AudioContentPart as bh, type AudioMimeType as bi, type AudioSource as bj, type ContentPart as bk, type ImageBase64Source as bl, type ImageContentPart as bm, type ImageMimeType as bn, type ImageSource as bo, type ImageUrlSource as bp, type TextContentPart as bq, audioFromBase64 as br, audioFromBuffer as bs, detectAudioMimeType as bt, detectImageMimeType as bu, imageFromBase64 as bv, imageFromBuffer as bw, imageFromUrl as bx, isAudioPart as by, isDataUrl as bz, createMockAdapter as c, stream as c0, type GadgetClass as c1, type GadgetOrClass as c2, type CostReportingLLMist as c3, type GadgetExecuteResult as c4, type GadgetSkippedEvent as c5, type StoredMedia as c6, type SubagentStreamEvent as c7, type TextOnlyAction as c8, type TextOnlyContext as c9, type TextOnlyCustomHandler as ca, type TextOnlyGadgetConfig as cb, type TextOnlyHandler as cc, type TextOnlyStrategy as cd, MockBuilder as d, createMockClient as e, MockManager as f, getMockManager as g, createMockStream as h, createTextMockStream as i, type MockAudioData as j, type MockImageData as k, type MockMatcher as l, mockLLM as m, type MockMatcherContext as n, type MockOptions as o, type MockRegistration as p, type MockResponse as q, type MockStats as r, type AgentHooks as s, ModelRegistry as t, LLMist as u, type CompactionEvent$1 as v, type CompactionStats as w, type CompactionStrategy as x, type CompactionContext as y, type CompactionResult as z };
6345
+ export { type LLMGenerationOptions as $, AbstractGadget as A, type MessageContent as B, type CompactionConfig as C, GadgetRegistry as D, MediaStore as E, type AgentContextConfig as F, type GadgetMediaOutput as G, type HintTemplate as H, type IConversationManager as I, type SubagentConfigMap as J, type SubagentEvent as K, type LLMMessage as L, MockProviderAdapter as M, ExecutionTree as N, type NodeId as O, type ParsedGadgetCall as P, type GadgetExecutionResult as Q, type ResolvedCompactionConfig as R, type StreamEvent as S, type TokenUsage as T, type MediaKind as U, type MediaMetadata as V, type GadgetExecuteResultWithMedia as W, type ExecutionContext as X, type ProviderAdapter as Y, type ModelDescriptor as Z, type ModelSpec as _, type LLMStream as a, type GadgetStartEvent as a$, type ImageModelSpec as a0, type ImageGenerationOptions as a1, type ImageGenerationResult as a2, type SpeechModelSpec as a3, type SpeechGenerationOptions as a4, type SpeechGenerationResult as a5, type HostExports as a6, type HistoryMessage as a7, type TrailingMessage as a8, type TrailingMessageContext as a9, type ObserveGadgetStartContext as aA, type ObserveLLMCallContext as aB, type ObserveLLMCompleteContext as aC, type ObserveLLMErrorContext as aD, type Observers as aE, type SubagentContext as aF, DEFAULT_COMPACTION_CONFIG as aG, DEFAULT_SUMMARIZATION_PROMPT as aH, type LLMistOptions as aI, type AddGadgetParams as aJ, type AddLLMCallParams as aK, type CompleteGadgetParams as aL, type CompleteLLMCallParams as aM, type ExecutionNode as aN, type ExecutionNodeType as aO, type GadgetNode as aP, type GadgetState as aQ, type LLMCallNode as aR, type BaseExecutionEvent as aS, type CompactionEvent$1 as aT, type ExecutionEvent as aU, type ExecutionEventType as aV, type GadgetCallEvent as aW, type GadgetCompleteEvent as aX, type GadgetErrorEvent as aY, type GadgetEvent as aZ, type GadgetSkippedEvent$1 as a_, AgentBuilder as aa, type EventHandlers as ab, collectEvents as ac, collectText as ad, runWithHandlers as ae, type AfterGadgetExecutionAction as af, type AfterGadgetExecutionControllerContext as ag, type AfterLLMCallAction as ah, type AfterLLMCallControllerContext as ai, type AfterLLMErrorAction as aj, type AgentOptions as ak, type BeforeGadgetExecutionAction as al, type BeforeLLMCallAction as am, type ChunkInterceptorContext as an, type Controllers as ao, type GadgetExecutionControllerContext as ap, type GadgetParameterInterceptorContext as aq, type GadgetResultInterceptorContext as ar, type Interceptors as as, type LLMCallControllerContext as at, type LLMErrorControllerContext as au, type MessageInterceptorContext as av, type MessageTurn as aw, type ObserveChunkContext as ax, type ObserveCompactionContext as ay, type ObserveGadgetCompleteContext as az, type LLMStreamChunk as b, stream as b$, type HumanInputRequiredEvent as b0, type LLMCallCompleteEvent as b1, type LLMCallErrorEvent as b2, type LLMCallStartEvent as b3, type LLMCallStreamEvent as b4, type LLMEvent as b5, type StreamCompleteEvent as b6, type TextEvent as b7, filterByDepth as b8, filterByParent as b9, isTextPart as bA, parseDataUrl as bB, text as bC, toBase64 as bD, type MessageRole as bE, extractMessageText as bF, LLMMessageBuilder as bG, normalizeMessageContent as bH, type CostEstimate as bI, type ModelFeatures as bJ, type ModelLimits as bK, type ModelPricing as bL, type VisionAnalyzeOptions as bM, type VisionAnalyzeResult as bN, type ProviderIdentifier as bO, ModelIdentifierParser as bP, type HintContext as bQ, type PromptContext as bR, type PromptTemplate as bS, type PromptTemplateConfig as bT, DEFAULT_HINTS as bU, DEFAULT_PROMPTS as bV, resolveHintTemplate as bW, resolvePromptTemplate as bX, resolveRulesTemplate as bY, type TextGenerationOptions as bZ, complete as b_, filterRootEvents as ba, groupByParent as bb, isGadgetEvent as bc, isLLMEvent as bd, isRootEvent as be, isSubagentEvent as bf, type AudioContentPart as bg, type AudioMimeType as bh, type AudioSource as bi, type ContentPart as bj, type ImageBase64Source as bk, type ImageContentPart as bl, type ImageMimeType as bm, type ImageSource as bn, type ImageUrlSource as bo, type TextContentPart as bp, audioFromBase64 as bq, audioFromBuffer as br, detectAudioMimeType as bs, detectImageMimeType as bt, imageFromBase64 as bu, imageFromBuffer as bv, imageFromUrl as bw, isAudioPart as bx, isDataUrl as by, isImagePart as bz, createMockAdapter as c, type CreateGadgetConfig as c0, createGadget as c1, type GadgetClass as c2, type GadgetOrClass as c3, type GadgetConfig as c4, Gadget as c5, type CostReportingLLMist as c6, type GadgetExample as c7, type GadgetExecuteResult as c8, type GadgetExecuteReturn as c9, type GadgetSkippedEvent as ca, type StoredMedia as cb, type SubagentStreamEvent as cc, type TextOnlyAction as cd, type TextOnlyContext as ce, type TextOnlyCustomHandler as cf, type TextOnlyGadgetConfig as cg, type TextOnlyHandler as ch, type TextOnlyStrategy as ci, MockBuilder as d, createMockClient as e, MockManager as f, getMockManager as g, createMockStream as h, createTextMockStream as i, type MockAudioData as j, type MockImageData as k, type MockMatcher as l, mockLLM as m, type MockMatcherContext as n, type MockOptions as o, type MockRegistration as p, type MockResponse as q, type MockStats as r, type AgentHooks as s, ModelRegistry as t, LLMist as u, type CompactionEvent as v, type CompactionStats as w, type CompactionStrategy as x, type CompactionContext as y, type CompactionResult as z };