llmist 7.0.0 → 8.0.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.cjs CHANGED
@@ -45,6 +45,19 @@ var init_constants = __esm({
45
45
  }
46
46
  });
47
47
 
48
+ // src/providers/constants.ts
49
+ var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS, FALLBACK_CHARS_PER_TOKEN, OPENAI_MESSAGE_OVERHEAD_TOKENS, OPENAI_REPLY_PRIMING_TOKENS, OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
50
+ var init_constants2 = __esm({
51
+ "src/providers/constants.ts"() {
52
+ "use strict";
53
+ ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
54
+ FALLBACK_CHARS_PER_TOKEN = 4;
55
+ OPENAI_MESSAGE_OVERHEAD_TOKENS = 4;
56
+ OPENAI_REPLY_PRIMING_TOKENS = 2;
57
+ OPENAI_NAME_FIELD_OVERHEAD_TOKENS = 1;
58
+ }
59
+ });
60
+
48
61
  // src/core/input-content.ts
49
62
  function isTextPart(part) {
50
63
  return part.type === "text";
@@ -2577,7 +2590,138 @@ var AGENT_INTERNAL_KEY;
2577
2590
  var init_agent_internal_key = __esm({
2578
2591
  "src/agent/agent-internal-key.ts"() {
2579
2592
  "use strict";
2580
- AGENT_INTERNAL_KEY = Symbol("AGENT_INTERNAL_KEY");
2593
+ AGENT_INTERNAL_KEY = /* @__PURE__ */ Symbol("AGENT_INTERNAL_KEY");
2594
+ }
2595
+ });
2596
+
2597
+ // src/core/retry.ts
2598
+ function resolveRetryConfig(config) {
2599
+ if (!config) {
2600
+ return { ...DEFAULT_RETRY_CONFIG };
2601
+ }
2602
+ return {
2603
+ enabled: config.enabled ?? DEFAULT_RETRY_CONFIG.enabled,
2604
+ retries: config.retries ?? DEFAULT_RETRY_CONFIG.retries,
2605
+ minTimeout: config.minTimeout ?? DEFAULT_RETRY_CONFIG.minTimeout,
2606
+ maxTimeout: config.maxTimeout ?? DEFAULT_RETRY_CONFIG.maxTimeout,
2607
+ factor: config.factor ?? DEFAULT_RETRY_CONFIG.factor,
2608
+ randomize: config.randomize ?? DEFAULT_RETRY_CONFIG.randomize,
2609
+ onRetry: config.onRetry,
2610
+ onRetriesExhausted: config.onRetriesExhausted,
2611
+ shouldRetry: config.shouldRetry
2612
+ };
2613
+ }
2614
+ function isRetryableError(error) {
2615
+ const message = error.message.toLowerCase();
2616
+ const name = error.name;
2617
+ if (message.includes("429") || message.includes("rate limit") || message.includes("rate_limit")) {
2618
+ return true;
2619
+ }
2620
+ if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504") || message.includes("internal server error") || message.includes("bad gateway") || message.includes("service unavailable") || message.includes("gateway timeout")) {
2621
+ return true;
2622
+ }
2623
+ if (message.includes("timeout") || message.includes("etimedout") || message.includes("timed out")) {
2624
+ return true;
2625
+ }
2626
+ if (message.includes("econnreset") || message.includes("econnrefused") || message.includes("enotfound") || message.includes("connection") || message.includes("network")) {
2627
+ return true;
2628
+ }
2629
+ if (name === "APIConnectionError" || name === "RateLimitError" || name === "InternalServerError" || name === "ServiceUnavailableError" || name === "APITimeoutError") {
2630
+ return true;
2631
+ }
2632
+ if (message.includes("overloaded") || message.includes("capacity")) {
2633
+ return true;
2634
+ }
2635
+ if (message.includes("401") || message.includes("403") || message.includes("400") || message.includes("404") || message.includes("authentication") || message.includes("unauthorized") || message.includes("forbidden") || message.includes("invalid") || message.includes("content policy") || name === "AuthenticationError" || name === "BadRequestError" || name === "NotFoundError" || name === "PermissionDeniedError") {
2636
+ return false;
2637
+ }
2638
+ return false;
2639
+ }
2640
+ function formatLLMError(error) {
2641
+ const message = error.message;
2642
+ const name = error.name;
2643
+ if (message.includes("RESOURCE_EXHAUSTED") || message.includes("429")) {
2644
+ return "Rate limit exceeded (429) - retry after a few seconds";
2645
+ }
2646
+ if (message.toLowerCase().includes("rate limit") || message.toLowerCase().includes("rate_limit")) {
2647
+ return "Rate limit exceeded - retry after a few seconds";
2648
+ }
2649
+ if (message.toLowerCase().includes("overloaded") || message.toLowerCase().includes("capacity")) {
2650
+ return "API overloaded - retry later";
2651
+ }
2652
+ if (message.includes("500") || message.toLowerCase().includes("internal server error")) {
2653
+ return "Internal server error (500) - the API is experiencing issues";
2654
+ }
2655
+ if (message.includes("502") || message.toLowerCase().includes("bad gateway")) {
2656
+ return "Bad gateway (502) - the API is temporarily unavailable";
2657
+ }
2658
+ if (message.includes("503") || message.toLowerCase().includes("service unavailable")) {
2659
+ return "Service unavailable (503) - the API is temporarily down";
2660
+ }
2661
+ if (message.includes("504") || message.toLowerCase().includes("gateway timeout")) {
2662
+ return "Gateway timeout (504) - the request took too long";
2663
+ }
2664
+ if (message.toLowerCase().includes("timeout") || message.toLowerCase().includes("timed out")) {
2665
+ return "Request timed out - the API took too long to respond";
2666
+ }
2667
+ if (message.toLowerCase().includes("econnrefused")) {
2668
+ return "Connection refused - unable to reach the API";
2669
+ }
2670
+ if (message.toLowerCase().includes("econnreset")) {
2671
+ return "Connection reset - the API closed the connection";
2672
+ }
2673
+ if (message.toLowerCase().includes("enotfound")) {
2674
+ return "DNS error - unable to resolve API hostname";
2675
+ }
2676
+ if (message.includes("401") || message.toLowerCase().includes("unauthorized") || name === "AuthenticationError") {
2677
+ return "Authentication failed - check your API key";
2678
+ }
2679
+ if (message.includes("403") || message.toLowerCase().includes("forbidden") || name === "PermissionDeniedError") {
2680
+ return "Permission denied - your API key lacks required permissions";
2681
+ }
2682
+ if (message.includes("400") || name === "BadRequestError") {
2683
+ const match = message.match(/message['":\s]+['"]?([^'"}\]]+)/i);
2684
+ if (match) {
2685
+ return `Bad request: ${match[1].trim()}`;
2686
+ }
2687
+ return "Bad request - check your input parameters";
2688
+ }
2689
+ if (message.toLowerCase().includes("content policy") || message.toLowerCase().includes("safety")) {
2690
+ return "Content policy violation - the request was blocked";
2691
+ }
2692
+ try {
2693
+ const parsed = JSON.parse(message);
2694
+ const extractedMessage = parsed?.error?.message || parsed?.message;
2695
+ if (typeof extractedMessage === "string" && extractedMessage.length > 0) {
2696
+ return extractedMessage.trim();
2697
+ }
2698
+ } catch {
2699
+ }
2700
+ const jsonMatch = message.match(/["']?message["']?\s*[:=]\s*["']([^"']+)["']/i);
2701
+ if (jsonMatch) {
2702
+ return jsonMatch[1].trim();
2703
+ }
2704
+ if (message.length > 200) {
2705
+ const firstPart = message.split(/[.!?\n]/)[0];
2706
+ if (firstPart && firstPart.length > 10 && firstPart.length < 150) {
2707
+ return firstPart.trim();
2708
+ }
2709
+ return message.slice(0, 150).trim() + "...";
2710
+ }
2711
+ return message;
2712
+ }
2713
+ var DEFAULT_RETRY_CONFIG;
2714
+ var init_retry = __esm({
2715
+ "src/core/retry.ts"() {
2716
+ "use strict";
2717
+ DEFAULT_RETRY_CONFIG = {
2718
+ enabled: true,
2719
+ retries: 3,
2720
+ minTimeout: 1e3,
2721
+ maxTimeout: 3e4,
2722
+ factor: 2,
2723
+ randomize: true
2724
+ };
2581
2725
  }
2582
2726
  });
2583
2727
 
@@ -3047,6 +3191,9 @@ var init_conversation_manager = __esm({
3047
3191
  }
3048
3192
  }
3049
3193
  }
3194
+ getConversationHistory() {
3195
+ return [...this.initialMessages, ...this.historyBuilder.build()];
3196
+ }
3050
3197
  };
3051
3198
  }
3052
3199
  });
@@ -3689,19 +3836,6 @@ var init_base_provider = __esm({
3689
3836
  }
3690
3837
  });
3691
3838
 
3692
- // src/providers/constants.ts
3693
- var ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS, FALLBACK_CHARS_PER_TOKEN, OPENAI_MESSAGE_OVERHEAD_TOKENS, OPENAI_REPLY_PRIMING_TOKENS, OPENAI_NAME_FIELD_OVERHEAD_TOKENS;
3694
- var init_constants2 = __esm({
3695
- "src/providers/constants.ts"() {
3696
- "use strict";
3697
- ANTHROPIC_DEFAULT_MAX_OUTPUT_TOKENS = 4096;
3698
- FALLBACK_CHARS_PER_TOKEN = 4;
3699
- OPENAI_MESSAGE_OVERHEAD_TOKENS = 4;
3700
- OPENAI_REPLY_PRIMING_TOKENS = 2;
3701
- OPENAI_NAME_FIELD_OVERHEAD_TOKENS = 1;
3702
- }
3703
- });
3704
-
3705
3839
  // src/providers/utils.ts
3706
3840
  function readEnvVar(key) {
3707
3841
  if (typeof process === "undefined" || typeof process.env === "undefined") {
@@ -7807,14 +7941,16 @@ var init_executor = __esm({
7807
7941
  executionTimeMs: Date.now() - startTime
7808
7942
  };
7809
7943
  }
7810
- if (error instanceof HumanInputRequiredException) {
7944
+ const isHumanInputError = error instanceof Error && error.name === "HumanInputRequiredException" && "question" in error;
7945
+ if (isHumanInputError) {
7946
+ const question = error.question;
7811
7947
  this.logger.info("Gadget requested human input", {
7812
7948
  gadgetName: call.gadgetName,
7813
- question: error.question
7949
+ question
7814
7950
  });
7815
7951
  if (this.requestHumanInput) {
7816
7952
  try {
7817
- const answer = await this.requestHumanInput(error.question);
7953
+ const answer = await this.requestHumanInput(question);
7818
7954
  this.logger.debug("Human input received", {
7819
7955
  gadgetName: call.gadgetName,
7820
7956
  answerLength: answer.length
@@ -8624,7 +8760,7 @@ var init_stream_processor = __esm({
8624
8760
  });
8625
8761
 
8626
8762
  // src/agent/agent.ts
8627
- var Agent;
8763
+ var import_p_retry, Agent;
8628
8764
  var init_agent = __esm({
8629
8765
  "src/agent/agent.ts"() {
8630
8766
  "use strict";
@@ -8636,6 +8772,8 @@ var init_agent = __esm({
8636
8772
  init_output_viewer();
8637
8773
  init_logger();
8638
8774
  init_agent_internal_key();
8775
+ init_retry();
8776
+ import_p_retry = __toESM(require("p-retry"), 1);
8639
8777
  init_manager();
8640
8778
  init_conversation_manager();
8641
8779
  init_event_handlers();
@@ -8670,6 +8808,8 @@ var init_agent = __esm({
8670
8808
  mediaStore;
8671
8809
  // Cancellation
8672
8810
  signal;
8811
+ // Retry configuration
8812
+ retryConfig;
8673
8813
  // Subagent configuration
8674
8814
  agentContextConfig;
8675
8815
  subagentConfig;
@@ -8684,6 +8824,8 @@ var init_agent = __esm({
8684
8824
  // Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
8685
8825
  completedInvocationIds = /* @__PURE__ */ new Set();
8686
8826
  failedInvocationIds = /* @__PURE__ */ new Set();
8827
+ // Queue for user messages injected during agent execution (REPL mid-session input)
8828
+ pendingUserMessages = [];
8687
8829
  // Execution Tree - first-class model for nested subagent support
8688
8830
  tree;
8689
8831
  parentNodeId;
@@ -8758,6 +8900,7 @@ var init_agent = __esm({
8758
8900
  );
8759
8901
  }
8760
8902
  this.signal = options.signal;
8903
+ this.retryConfig = resolveRetryConfig(options.retryConfig);
8761
8904
  this.agentContextConfig = {
8762
8905
  model: this.model,
8763
8906
  temperature: this.temperature
@@ -8975,6 +9118,44 @@ var init_agent = __esm({
8975
9118
  getCompactionStats() {
8976
9119
  return this.compactionManager?.getStats() ?? null;
8977
9120
  }
9121
+ /**
9122
+ * Get the conversation manager for this agent.
9123
+ * Used by REPL mode to extract session history for continuation.
9124
+ *
9125
+ * @returns The conversation manager containing all messages
9126
+ *
9127
+ * @example
9128
+ * ```typescript
9129
+ * // After running agent, extract history for next session
9130
+ * const history = agent.getConversation().getConversationHistory();
9131
+ * // Pass to next agent via builder.withHistory()
9132
+ * ```
9133
+ */
9134
+ getConversation() {
9135
+ return this.conversation;
9136
+ }
9137
+ /**
9138
+ * Inject a user message to be processed in the next iteration.
9139
+ * Used by REPL mode to allow user input during a running session.
9140
+ *
9141
+ * The message is queued and will be added to the conversation before
9142
+ * the next LLM call. This allows users to provide additional context
9143
+ * or instructions while the agent is executing.
9144
+ *
9145
+ * @param message - The user message to inject
9146
+ *
9147
+ * @example
9148
+ * ```typescript
9149
+ * // While agent is running in TUI:
9150
+ * tui.onMidSessionInput((msg) => {
9151
+ * agent.injectUserMessage(msg);
9152
+ * });
9153
+ * ```
9154
+ */
9155
+ injectUserMessage(message) {
9156
+ this.pendingUserMessages.push(message);
9157
+ this.logger.debug("User message queued for injection", { message });
9158
+ }
8978
9159
  /**
8979
9160
  * Run the agent loop.
8980
9161
  * Clean, simple orchestration - all complexity is in StreamProcessor.
@@ -8996,6 +9177,14 @@ var init_agent = __esm({
8996
9177
  if (await this.checkAbortAndNotify(currentIteration)) {
8997
9178
  return;
8998
9179
  }
9180
+ while (this.pendingUserMessages.length > 0) {
9181
+ const msg = this.pendingUserMessages.shift();
9182
+ this.conversation.addUserMessage(msg);
9183
+ this.logger.info("Injected user message into conversation", {
9184
+ iteration: currentIteration,
9185
+ messageLength: msg.length
9186
+ });
9187
+ }
8999
9188
  this.logger.debug("Starting iteration", { iteration: currentIteration });
9000
9189
  try {
9001
9190
  const compactionEvent = await this.checkAndPerformCompaction(currentIteration);
@@ -9024,7 +9213,7 @@ var init_agent = __esm({
9024
9213
  request: llmOptions.messages
9025
9214
  });
9026
9215
  const currentLLMNodeId = llmNode.id;
9027
- const stream2 = this.client.stream(llmOptions);
9216
+ const stream2 = await this.createStreamWithRetry(llmOptions, currentIteration);
9028
9217
  const processor = new StreamProcessor({
9029
9218
  iteration: currentIteration,
9030
9219
  registry: this.registry,
@@ -9149,6 +9338,53 @@ var init_agent = __esm({
9149
9338
  reason: currentIteration >= this.maxIterations ? "max_iterations" : "natural_completion"
9150
9339
  });
9151
9340
  }
9341
+ /**
9342
+ * Create LLM stream with retry logic.
9343
+ * Wraps the stream creation with exponential backoff for transient failures.
9344
+ */
9345
+ async createStreamWithRetry(llmOptions, iteration) {
9346
+ if (!this.retryConfig.enabled) {
9347
+ return this.client.stream(llmOptions);
9348
+ }
9349
+ const { retries, minTimeout, maxTimeout, factor, randomize, onRetry, onRetriesExhausted, shouldRetry } = this.retryConfig;
9350
+ try {
9351
+ return await (0, import_p_retry.default)(
9352
+ async (attemptNumber) => {
9353
+ this.logger.debug("Creating LLM stream", { attempt: attemptNumber, maxAttempts: retries + 1 });
9354
+ return this.client.stream(llmOptions);
9355
+ },
9356
+ {
9357
+ retries,
9358
+ minTimeout,
9359
+ maxTimeout,
9360
+ factor,
9361
+ randomize,
9362
+ signal: this.signal,
9363
+ onFailedAttempt: (context) => {
9364
+ const { error, attemptNumber, retriesLeft } = context;
9365
+ this.logger.warn(
9366
+ `LLM call failed (attempt ${attemptNumber}/${attemptNumber + retriesLeft}), retrying...`,
9367
+ { error: error.message, retriesLeft }
9368
+ );
9369
+ onRetry?.(error, attemptNumber);
9370
+ },
9371
+ shouldRetry: (context) => {
9372
+ if (shouldRetry) {
9373
+ return shouldRetry(context.error);
9374
+ }
9375
+ return isRetryableError(context.error);
9376
+ }
9377
+ }
9378
+ );
9379
+ } catch (error) {
9380
+ this.logger.error(`LLM call failed after ${retries + 1} attempts`, {
9381
+ error: error.message,
9382
+ iteration
9383
+ });
9384
+ onRetriesExhausted?.(error, retries + 1);
9385
+ throw error;
9386
+ }
9387
+ }
9152
9388
  /**
9153
9389
  * Handle LLM error through controller.
9154
9390
  */
@@ -9526,6 +9762,7 @@ var init_builder = __esm({
9526
9762
  gadgetOutputLimit;
9527
9763
  gadgetOutputLimitPercent;
9528
9764
  compactionConfig;
9765
+ retryConfig;
9529
9766
  signal;
9530
9767
  trailingMessage;
9531
9768
  subagentConfig;
@@ -9695,6 +9932,59 @@ var init_builder = __esm({
9695
9932
  addMessage(message) {
9696
9933
  return this.withHistory([message]);
9697
9934
  }
9935
+ /**
9936
+ * Clear any previously set conversation history.
9937
+ * Used before setting new cumulative history in REPL mode.
9938
+ *
9939
+ * @returns This builder for chaining
9940
+ *
9941
+ * @example
9942
+ * ```typescript
9943
+ * // Reset history before setting new cumulative history
9944
+ * builder.clearHistory().withHistory(cumulativeHistory);
9945
+ * ```
9946
+ */
9947
+ clearHistory() {
9948
+ this.initialMessages = [];
9949
+ return this;
9950
+ }
9951
+ /**
9952
+ * Continue conversation from a previous agent's history.
9953
+ * Extracts full conversation history and sets it as initial messages.
9954
+ *
9955
+ * This is the recommended way to implement REPL session continuation.
9956
+ * It automatically handles history extraction and format conversion.
9957
+ *
9958
+ * @param agent - The previous agent to continue from
9959
+ * @returns This builder for chaining
9960
+ *
9961
+ * @example
9962
+ * ```typescript
9963
+ * // REPL loop with session continuity
9964
+ * let previousAgent: Agent | null = null;
9965
+ *
9966
+ * while (true) {
9967
+ * if (previousAgent) {
9968
+ * builder.continueFrom(previousAgent);
9969
+ * }
9970
+ * const agent = builder.ask(prompt);
9971
+ * await runAgent(agent);
9972
+ * previousAgent = agent;
9973
+ * }
9974
+ * ```
9975
+ */
9976
+ continueFrom(agent) {
9977
+ const history = agent.getConversation().getConversationHistory();
9978
+ this.clearHistory();
9979
+ for (const msg of history) {
9980
+ if (msg.role === "user") {
9981
+ this.initialMessages.push({ role: "user", content: msg.content });
9982
+ } else if (msg.role === "assistant") {
9983
+ this.initialMessages.push({ role: "assistant", content: msg.content });
9984
+ }
9985
+ }
9986
+ return this;
9987
+ }
9698
9988
  /**
9699
9989
  * Set the human input handler for interactive conversations.
9700
9990
  *
@@ -9923,6 +10213,60 @@ var init_builder = __esm({
9923
10213
  this.compactionConfig = { enabled: false };
9924
10214
  return this;
9925
10215
  }
10216
+ /**
10217
+ * Configure retry behavior for LLM API calls.
10218
+ *
10219
+ * Retry is enabled by default with conservative settings (3 retries, exponential backoff).
10220
+ * Use this method to customize retry behavior for rate limits, timeouts, and transient errors.
10221
+ *
10222
+ * @param config - Retry configuration options
10223
+ * @returns This builder for chaining
10224
+ *
10225
+ * @example
10226
+ * ```typescript
10227
+ * // Custom retry configuration
10228
+ * .withRetry({
10229
+ * retries: 5,
10230
+ * minTimeout: 2000,
10231
+ * maxTimeout: 60000,
10232
+ * })
10233
+ *
10234
+ * // With monitoring callbacks
10235
+ * .withRetry({
10236
+ * onRetry: (error, attempt) => {
10237
+ * console.log(`Retry ${attempt}: ${error.message}`);
10238
+ * },
10239
+ * onRetriesExhausted: (error, attempts) => {
10240
+ * alerting.warn(`Failed after ${attempts} attempts`);
10241
+ * }
10242
+ * })
10243
+ *
10244
+ * // Custom retry logic
10245
+ * .withRetry({
10246
+ * shouldRetry: (error) => error.message.includes('429'),
10247
+ * })
10248
+ * ```
10249
+ */
10250
+ withRetry(config) {
10251
+ this.retryConfig = { ...config, enabled: config.enabled ?? true };
10252
+ return this;
10253
+ }
10254
+ /**
10255
+ * Disable automatic retry for LLM API calls.
10256
+ *
10257
+ * By default, retry is enabled. Use this method to explicitly disable it.
10258
+ *
10259
+ * @returns This builder for chaining
10260
+ *
10261
+ * @example
10262
+ * ```typescript
10263
+ * .withoutRetry() // Disable automatic retry
10264
+ * ```
10265
+ */
10266
+ withoutRetry() {
10267
+ this.retryConfig = { enabled: false };
10268
+ return this;
10269
+ }
9926
10270
  /**
9927
10271
  * Set an abort signal for cancelling requests mid-flight.
9928
10272
  *
@@ -10240,6 +10584,7 @@ ${endPrefix}`
10240
10584
  gadgetOutputLimit: this.gadgetOutputLimit,
10241
10585
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
10242
10586
  compactionConfig: this.compactionConfig,
10587
+ retryConfig: this.retryConfig,
10243
10588
  signal: this.signal,
10244
10589
  subagentConfig: this.subagentConfig,
10245
10590
  onSubagentEvent: this.subagentEventCallback,
@@ -10425,6 +10770,7 @@ ${endPrefix}`
10425
10770
  gadgetOutputLimit: this.gadgetOutputLimit,
10426
10771
  gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
10427
10772
  compactionConfig: this.compactionConfig,
10773
+ retryConfig: this.retryConfig,
10428
10774
  signal: this.signal,
10429
10775
  subagentConfig: this.subagentConfig,
10430
10776
  onSubagentEvent: this.subagentEventCallback,
@@ -10444,6 +10790,7 @@ var index_exports = {};
10444
10790
  __export(index_exports, {
10445
10791
  AbortException: () => AbortException,
10446
10792
  AbstractGadget: () => AbstractGadget,
10793
+ Agent: () => Agent,
10447
10794
  AgentBuilder: () => AgentBuilder,
10448
10795
  AnthropicMessagesProvider: () => AnthropicMessagesProvider,
10449
10796
  CompactionManager: () => CompactionManager,
@@ -10451,8 +10798,13 @@ __export(index_exports, {
10451
10798
  DEFAULT_COMPACTION_CONFIG: () => DEFAULT_COMPACTION_CONFIG,
10452
10799
  DEFAULT_HINTS: () => DEFAULT_HINTS,
10453
10800
  DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
10801
+ DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
10454
10802
  DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
10455
10803
  ExecutionTree: () => ExecutionTree,
10804
+ FALLBACK_CHARS_PER_TOKEN: () => FALLBACK_CHARS_PER_TOKEN,
10805
+ GADGET_ARG_PREFIX: () => GADGET_ARG_PREFIX,
10806
+ GADGET_END_PREFIX: () => GADGET_END_PREFIX,
10807
+ GADGET_START_PREFIX: () => GADGET_START_PREFIX,
10456
10808
  Gadget: () => Gadget,
10457
10809
  GadgetCallParser: () => GadgetCallParser,
10458
10810
  GadgetExecutor: () => GadgetExecutor,
@@ -10466,9 +10818,6 @@ __export(index_exports, {
10466
10818
  LLMist: () => LLMist,
10467
10819
  MODEL_ALIASES: () => MODEL_ALIASES,
10468
10820
  MediaStore: () => MediaStore,
10469
- MockBuilder: () => MockBuilder,
10470
- MockManager: () => MockManager,
10471
- MockProviderAdapter: () => MockProviderAdapter,
10472
10821
  ModelIdentifierParser: () => ModelIdentifierParser,
10473
10822
  ModelRegistry: () => ModelRegistry,
10474
10823
  OpenAIChatProvider: () => OpenAIChatProvider,
@@ -10489,11 +10838,7 @@ __export(index_exports, {
10489
10838
  createHints: () => createHints,
10490
10839
  createLogger: () => createLogger,
10491
10840
  createMediaOutput: () => createMediaOutput,
10492
- createMockAdapter: () => createMockAdapter,
10493
- createMockClient: () => createMockClient,
10494
- createMockStream: () => createMockStream,
10495
10841
  createOpenAIProviderFromEnv: () => createOpenAIProviderFromEnv,
10496
- createTextMockStream: () => createTextMockStream,
10497
10842
  defaultLogger: () => defaultLogger,
10498
10843
  detectAudioMimeType: () => detectAudioMimeType,
10499
10844
  detectImageMimeType: () => detectImageMimeType,
@@ -10502,8 +10847,8 @@ __export(index_exports, {
10502
10847
  filterByDepth: () => filterByDepth,
10503
10848
  filterByParent: () => filterByParent,
10504
10849
  filterRootEvents: () => filterRootEvents,
10850
+ formatLLMError: () => formatLLMError,
10505
10851
  getHostExports: () => getHostExports,
10506
- getMockManager: () => getMockManager,
10507
10852
  getModelId: () => getModelId,
10508
10853
  getProvider: () => getProvider,
10509
10854
  groupByParent: () => groupByParent,
@@ -10511,16 +10856,17 @@ __export(index_exports, {
10511
10856
  imageFromBase64: () => imageFromBase64,
10512
10857
  imageFromBuffer: () => imageFromBuffer,
10513
10858
  imageFromUrl: () => imageFromUrl,
10859
+ isAbortError: () => isAbortError,
10514
10860
  isAudioPart: () => isAudioPart,
10515
10861
  isDataUrl: () => isDataUrl,
10516
10862
  isGadgetEvent: () => isGadgetEvent,
10517
10863
  isImagePart: () => isImagePart,
10518
10864
  isLLMEvent: () => isLLMEvent,
10865
+ isRetryableError: () => isRetryableError,
10519
10866
  isRootEvent: () => isRootEvent,
10520
10867
  isSubagentEvent: () => isSubagentEvent,
10521
10868
  isTextPart: () => isTextPart,
10522
10869
  iterationProgressHint: () => iterationProgressHint,
10523
- mockLLM: () => mockLLM,
10524
10870
  normalizeMessageContent: () => normalizeMessageContent,
10525
10871
  parallelGadgetHint: () => parallelGadgetHint,
10526
10872
  parseDataUrl: () => parseDataUrl,
@@ -10528,6 +10874,7 @@ __export(index_exports, {
10528
10874
  resolveHintTemplate: () => resolveHintTemplate,
10529
10875
  resolveModel: () => resolveModel,
10530
10876
  resolvePromptTemplate: () => resolvePromptTemplate,
10877
+ resolveRetryConfig: () => resolveRetryConfig,
10531
10878
  resolveRulesTemplate: () => resolveRulesTemplate,
10532
10879
  resolveSubagentModel: () => resolveSubagentModel,
10533
10880
  resolveValue: () => resolveValue,
@@ -10537,15 +10884,19 @@ __export(index_exports, {
10537
10884
  resultWithImages: () => resultWithImages,
10538
10885
  resultWithMedia: () => resultWithMedia,
10539
10886
  runWithHandlers: () => runWithHandlers,
10887
+ schemaToJSONSchema: () => schemaToJSONSchema,
10540
10888
  stream: () => stream,
10541
10889
  text: () => text,
10542
10890
  toBase64: () => toBase64,
10543
10891
  validateAndApplyDefaults: () => validateAndApplyDefaults,
10544
10892
  validateGadgetParams: () => validateGadgetParams,
10893
+ validateGadgetSchema: () => validateGadgetSchema,
10545
10894
  z: () => import_zod3.z
10546
10895
  });
10547
10896
  module.exports = __toCommonJS(index_exports);
10548
10897
  var import_zod3 = require("zod");
10898
+ init_constants();
10899
+ init_constants2();
10549
10900
  init_builder();
10550
10901
  init_event_handlers();
10551
10902
 
@@ -11328,7 +11679,6 @@ var HookPresets = class _HookPresets {
11328
11679
  init_config();
11329
11680
  init_manager();
11330
11681
  init_strategies();
11331
- init_strategy();
11332
11682
 
11333
11683
  // src/agent/index.ts
11334
11684
  init_conversation_manager();
@@ -11436,6 +11786,23 @@ init_stream_processor();
11436
11786
 
11437
11787
  // src/index.ts
11438
11788
  init_client();
11789
+
11790
+ // src/core/errors.ts
11791
+ function isAbortError(error) {
11792
+ if (!(error instanceof Error)) return false;
11793
+ if (error.name === "AbortError") return true;
11794
+ if (error.name === "APIConnectionAbortedError") return true;
11795
+ if (error.name === "APIUserAbortError") return true;
11796
+ const message = error.message.toLowerCase();
11797
+ if (message.includes("abort")) return true;
11798
+ if (message.includes("cancelled")) return true;
11799
+ if (message.includes("canceled")) return true;
11800
+ return false;
11801
+ }
11802
+
11803
+ // src/index.ts
11804
+ init_agent();
11805
+ init_retry();
11439
11806
  init_execution_tree();
11440
11807
 
11441
11808
  // src/core/execution-events.ts
@@ -11633,6 +12000,8 @@ function validateGadgetParams(gadget, params) {
11633
12000
  }
11634
12001
 
11635
12002
  // src/index.ts
12003
+ init_schema_to_json();
12004
+ init_schema_validator();
11636
12005
  init_logger();
11637
12006
 
11638
12007
  // src/utils/config-resolver.ts
@@ -11684,1006 +12053,6 @@ init_anthropic();
11684
12053
  init_discovery();
11685
12054
  init_gemini();
11686
12055
  init_openai();
11687
-
11688
- // src/testing/cli-helpers.ts
11689
- var import_node_stream = require("stream");
11690
-
11691
- // src/testing/mock-manager.ts
11692
- init_logger();
11693
- var MockManager = class _MockManager {
11694
- static instance = null;
11695
- mocks = /* @__PURE__ */ new Map();
11696
- stats = /* @__PURE__ */ new Map();
11697
- options;
11698
- logger;
11699
- nextId = 1;
11700
- constructor(options = {}) {
11701
- this.options = {
11702
- strictMode: options.strictMode ?? false,
11703
- debug: options.debug ?? false,
11704
- recordStats: options.recordStats ?? true
11705
- };
11706
- this.logger = createLogger({ name: "MockManager", minLevel: this.options.debug ? 2 : 3 });
11707
- }
11708
- /**
11709
- * Get the global MockManager instance.
11710
- * Creates one if it doesn't exist.
11711
- */
11712
- static getInstance(options) {
11713
- if (!_MockManager.instance) {
11714
- _MockManager.instance = new _MockManager(options);
11715
- } else if (options) {
11716
- console.warn(
11717
- "MockManager.getInstance() called with options, but instance already exists. Options are ignored. Use setOptions() to update options or reset() to reinitialize."
11718
- );
11719
- }
11720
- return _MockManager.instance;
11721
- }
11722
- /**
11723
- * Reset the global instance (useful for testing).
11724
- */
11725
- static reset() {
11726
- _MockManager.instance = null;
11727
- }
11728
- /**
11729
- * Register a new mock.
11730
- *
11731
- * @param registration - The mock registration configuration
11732
- * @returns The ID of the registered mock
11733
- *
11734
- * @example
11735
- * const manager = MockManager.getInstance();
11736
- * const mockId = manager.register({
11737
- * label: 'GPT-4 mock',
11738
- * matcher: (ctx) => ctx.modelName.includes('gpt-4'),
11739
- * response: { text: 'Mocked response' }
11740
- * });
11741
- */
11742
- register(registration) {
11743
- const id = registration.id ?? `mock-${this.nextId++}`;
11744
- const mock = {
11745
- id,
11746
- matcher: registration.matcher,
11747
- response: registration.response,
11748
- label: registration.label,
11749
- once: registration.once
11750
- };
11751
- this.mocks.set(id, mock);
11752
- if (this.options.recordStats) {
11753
- this.stats.set(id, { matchCount: 0 });
11754
- }
11755
- this.logger.debug(
11756
- `Registered mock: ${id}${mock.label ? ` (${mock.label})` : ""}${mock.once ? " [once]" : ""}`
11757
- );
11758
- return id;
11759
- }
11760
- /**
11761
- * Unregister a mock by ID.
11762
- */
11763
- unregister(id) {
11764
- const deleted = this.mocks.delete(id);
11765
- if (deleted) {
11766
- this.stats.delete(id);
11767
- this.logger.debug(`Unregistered mock: ${id}`);
11768
- }
11769
- return deleted;
11770
- }
11771
- /**
11772
- * Clear all registered mocks.
11773
- */
11774
- clear() {
11775
- this.mocks.clear();
11776
- this.stats.clear();
11777
- this.logger.debug("Cleared all mocks");
11778
- }
11779
- /**
11780
- * Find and return a matching mock for the given context.
11781
- * Returns the mock response if found, null otherwise.
11782
- */
11783
- async findMatch(context) {
11784
- this.logger.debug(
11785
- `Finding match for: ${context.provider}:${context.modelName} (${this.mocks.size} mocks registered)`
11786
- );
11787
- for (const [id, mock] of this.mocks.entries()) {
11788
- let matches = false;
11789
- try {
11790
- matches = await Promise.resolve(mock.matcher(context));
11791
- } catch (error) {
11792
- this.logger.warn(`Error in matcher ${id}:`, error);
11793
- if (this.options.strictMode) {
11794
- throw new Error(`Matcher error in mock ${id}: ${error}`);
11795
- }
11796
- continue;
11797
- }
11798
- if (matches) {
11799
- this.logger.debug(`Mock matched: ${id}${mock.label ? ` (${mock.label})` : ""}`);
11800
- if (this.options.recordStats) {
11801
- const stats = this.stats.get(id);
11802
- if (stats) {
11803
- stats.matchCount++;
11804
- stats.lastUsed = /* @__PURE__ */ new Date();
11805
- }
11806
- }
11807
- if (mock.once) {
11808
- this.mocks.delete(id);
11809
- this.stats.delete(id);
11810
- this.logger.debug(`Removed one-time mock: ${id}`);
11811
- }
11812
- const response = typeof mock.response === "function" ? await Promise.resolve(mock.response(context)) : mock.response;
11813
- return response;
11814
- }
11815
- }
11816
- this.logger.debug("No mock matched");
11817
- if (this.options.strictMode) {
11818
- throw new Error(
11819
- `No mock registered for ${context.provider}:${context.modelName}. Register a mock using MockManager.getInstance().register() or disable strictMode.`
11820
- );
11821
- }
11822
- return {
11823
- text: "",
11824
- usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
11825
- finishReason: "stop"
11826
- };
11827
- }
11828
- /**
11829
- * Get statistics for a specific mock.
11830
- */
11831
- getStats(id) {
11832
- return this.stats.get(id);
11833
- }
11834
- /**
11835
- * Get all registered mock IDs.
11836
- */
11837
- getMockIds() {
11838
- return Array.from(this.mocks.keys());
11839
- }
11840
- /**
11841
- * Get the number of registered mocks.
11842
- */
11843
- getCount() {
11844
- return this.mocks.size;
11845
- }
11846
- /**
11847
- * Update the mock manager options.
11848
- */
11849
- setOptions(options) {
11850
- this.options = { ...this.options, ...options };
11851
- this.logger = createLogger({ name: "MockManager", minLevel: this.options.debug ? 2 : 3 });
11852
- }
11853
- };
11854
- function getMockManager(options) {
11855
- return MockManager.getInstance(options);
11856
- }
11857
-
11858
- // src/testing/mock-stream.ts
11859
- init_constants();
11860
- function sleep(ms) {
11861
- return new Promise((resolve) => setTimeout(resolve, ms));
11862
- }
11863
- function generateInvocationId() {
11864
- return `inv-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
11865
- }
11866
- function splitIntoChunks(text3, minChunkSize = 5, maxChunkSize = 30) {
11867
- const chunks = [];
11868
- let remaining = text3;
11869
- while (remaining.length > 0) {
11870
- const chunkSize = Math.min(
11871
- Math.floor(Math.random() * (maxChunkSize - minChunkSize + 1)) + minChunkSize,
11872
- remaining.length
11873
- );
11874
- let chunk;
11875
- if (chunkSize < remaining.length) {
11876
- const substr = remaining.substring(0, chunkSize);
11877
- const lastSpace = substr.lastIndexOf(" ");
11878
- if (lastSpace > minChunkSize / 2) {
11879
- chunk = substr.substring(0, lastSpace + 1);
11880
- } else {
11881
- chunk = substr;
11882
- }
11883
- } else {
11884
- chunk = remaining;
11885
- }
11886
- chunks.push(chunk);
11887
- remaining = remaining.substring(chunk.length);
11888
- }
11889
- return chunks;
11890
- }
11891
- function serializeToBlockFormat(obj, prefix = "") {
11892
- let result = "";
11893
- for (const [key, value] of Object.entries(obj)) {
11894
- const pointer = prefix ? `${prefix}/${key}` : key;
11895
- if (value === null || value === void 0) {
11896
- continue;
11897
- }
11898
- if (Array.isArray(value)) {
11899
- for (let i = 0; i < value.length; i++) {
11900
- const item = value[i];
11901
- const itemPointer = `${pointer}/${i}`;
11902
- if (typeof item === "object" && item !== null && !Array.isArray(item)) {
11903
- result += serializeToBlockFormat(item, itemPointer);
11904
- } else if (Array.isArray(item)) {
11905
- for (let j = 0; j < item.length; j++) {
11906
- result += `${GADGET_ARG_PREFIX}${itemPointer}/${j}
11907
- ${String(item[j])}
11908
- `;
11909
- }
11910
- } else {
11911
- result += `${GADGET_ARG_PREFIX}${itemPointer}
11912
- ${String(item)}
11913
- `;
11914
- }
11915
- }
11916
- } else if (typeof value === "object") {
11917
- result += serializeToBlockFormat(value, pointer);
11918
- } else {
11919
- result += `${GADGET_ARG_PREFIX}${pointer}
11920
- ${String(value)}
11921
- `;
11922
- }
11923
- }
11924
- return result;
11925
- }
11926
- function formatGadgetCalls(gadgetCalls) {
11927
- let text3 = "";
11928
- const calls = [];
11929
- for (const call of gadgetCalls) {
11930
- const invocationId = call.invocationId ?? generateInvocationId();
11931
- calls.push({ name: call.gadgetName, invocationId });
11932
- const blockParams = serializeToBlockFormat(call.parameters);
11933
- text3 += `
11934
- ${GADGET_START_PREFIX}${call.gadgetName}
11935
- ${blockParams}${GADGET_END_PREFIX}`;
11936
- }
11937
- return { text: text3, calls };
11938
- }
11939
- async function* createMockStream(response) {
11940
- if (response.delayMs) {
11941
- await sleep(response.delayMs);
11942
- }
11943
- const streamDelay = response.streamDelayMs ?? 0;
11944
- let fullText = response.text ?? "";
11945
- if (response.gadgetCalls && response.gadgetCalls.length > 0) {
11946
- const { text: gadgetText } = formatGadgetCalls(response.gadgetCalls);
11947
- fullText += gadgetText;
11948
- }
11949
- if (fullText.length > 0) {
11950
- const chunks = streamDelay > 0 ? splitIntoChunks(fullText) : [fullText];
11951
- for (let i = 0; i < chunks.length; i++) {
11952
- const isLast = i === chunks.length - 1;
11953
- const chunk = {
11954
- text: chunks[i]
11955
- };
11956
- if (isLast) {
11957
- if (response.finishReason !== void 0) {
11958
- chunk.finishReason = response.finishReason;
11959
- }
11960
- if (response.usage) {
11961
- chunk.usage = response.usage;
11962
- }
11963
- }
11964
- yield chunk;
11965
- if (streamDelay > 0 && !isLast) {
11966
- await sleep(streamDelay);
11967
- }
11968
- }
11969
- } else {
11970
- yield {
11971
- text: "",
11972
- finishReason: response.finishReason ?? "stop",
11973
- usage: response.usage ?? { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
11974
- };
11975
- }
11976
- }
11977
- function createTextMockStream(text3, options) {
11978
- return createMockStream({
11979
- text: text3,
11980
- delayMs: options?.delayMs,
11981
- streamDelayMs: options?.streamDelayMs,
11982
- usage: options?.usage,
11983
- finishReason: "stop"
11984
- });
11985
- }
11986
-
11987
- // src/testing/mock-adapter.ts
11988
- var MockProviderAdapter = class {
11989
- providerId = "mock";
11990
- priority = 100;
11991
- // High priority: check mocks before real providers
11992
- mockManager;
11993
- constructor(options) {
11994
- this.mockManager = getMockManager(options);
11995
- }
11996
- supports(_descriptor) {
11997
- return true;
11998
- }
11999
- stream(options, descriptor, _spec) {
12000
- const context = {
12001
- model: options.model,
12002
- provider: descriptor.provider,
12003
- modelName: descriptor.name,
12004
- options,
12005
- messages: options.messages
12006
- };
12007
- return this.createMockStreamFromContext(context);
12008
- }
12009
- async *createMockStreamFromContext(context) {
12010
- const mockResponse = await this.mockManager.findMatch(context);
12011
- if (!mockResponse) {
12012
- yield {
12013
- text: "",
12014
- finishReason: "stop",
12015
- usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 }
12016
- };
12017
- return;
12018
- }
12019
- yield* createMockStream(mockResponse);
12020
- }
12021
- // ==========================================================================
12022
- // Image Generation Support
12023
- // ==========================================================================
12024
- /**
12025
- * Check if this adapter supports image generation for a given model.
12026
- * Returns true if there's a registered mock with images for this model.
12027
- */
12028
- supportsImageGeneration(_modelId) {
12029
- return true;
12030
- }
12031
- /**
12032
- * Generate mock images based on registered mocks.
12033
- *
12034
- * @param options - Image generation options
12035
- * @returns Mock image generation result
12036
- */
12037
- async generateImage(options) {
12038
- const context = {
12039
- model: options.model,
12040
- provider: "mock",
12041
- modelName: options.model,
12042
- options: {
12043
- model: options.model,
12044
- messages: [{ role: "user", content: options.prompt }]
12045
- },
12046
- messages: [{ role: "user", content: options.prompt }]
12047
- };
12048
- const mockResponse = await this.mockManager.findMatch(context);
12049
- if (!mockResponse?.images || mockResponse.images.length === 0) {
12050
- throw new Error(
12051
- `No mock registered for image generation with model "${options.model}". Use mockLLM().forModel("${options.model}").returnsImage(...).register() to add one.`
12052
- );
12053
- }
12054
- return this.createImageResult(options, mockResponse);
12055
- }
12056
- /**
12057
- * Transform mock response into ImageGenerationResult format.
12058
- *
12059
- * @param options - Original image generation options
12060
- * @param mockResponse - Mock response containing image data
12061
- * @returns ImageGenerationResult with mock data and zero cost
12062
- */
12063
- createImageResult(options, mockResponse) {
12064
- const images = mockResponse.images ?? [];
12065
- return {
12066
- images: images.map((img) => ({
12067
- b64Json: img.data,
12068
- revisedPrompt: img.revisedPrompt
12069
- })),
12070
- model: options.model,
12071
- usage: {
12072
- imagesGenerated: images.length,
12073
- size: options.size ?? "1024x1024",
12074
- quality: options.quality ?? "standard"
12075
- },
12076
- cost: 0
12077
- // Mock cost is always 0
12078
- };
12079
- }
12080
- // ==========================================================================
12081
- // Speech Generation Support
12082
- // ==========================================================================
12083
- /**
12084
- * Check if this adapter supports speech generation for a given model.
12085
- * Returns true if there's a registered mock with audio for this model.
12086
- */
12087
- supportsSpeechGeneration(_modelId) {
12088
- return true;
12089
- }
12090
- /**
12091
- * Generate mock speech based on registered mocks.
12092
- *
12093
- * @param options - Speech generation options
12094
- * @returns Mock speech generation result
12095
- */
12096
- async generateSpeech(options) {
12097
- const context = {
12098
- model: options.model,
12099
- provider: "mock",
12100
- modelName: options.model,
12101
- options: {
12102
- model: options.model,
12103
- messages: [{ role: "user", content: options.input }]
12104
- },
12105
- messages: [{ role: "user", content: options.input }]
12106
- };
12107
- const mockResponse = await this.mockManager.findMatch(context);
12108
- if (!mockResponse?.audio) {
12109
- throw new Error(
12110
- `No mock registered for speech generation with model "${options.model}". Use mockLLM().forModel("${options.model}").returnsAudio(...).register() to add one.`
12111
- );
12112
- }
12113
- return this.createSpeechResult(options, mockResponse);
12114
- }
12115
- /**
12116
- * Transform mock response into SpeechGenerationResult format.
12117
- * Converts base64 audio data to ArrayBuffer.
12118
- *
12119
- * @param options - Original speech generation options
12120
- * @param mockResponse - Mock response containing audio data
12121
- * @returns SpeechGenerationResult with mock data and zero cost
12122
- */
12123
- createSpeechResult(options, mockResponse) {
12124
- const audio = mockResponse.audio;
12125
- const binaryString = atob(audio.data);
12126
- const bytes = new Uint8Array(binaryString.length);
12127
- for (let i = 0; i < binaryString.length; i++) {
12128
- bytes[i] = binaryString.charCodeAt(i);
12129
- }
12130
- const format = this.mimeTypeToAudioFormat(audio.mimeType);
12131
- return {
12132
- audio: bytes.buffer,
12133
- model: options.model,
12134
- usage: {
12135
- characterCount: options.input.length
12136
- },
12137
- cost: 0,
12138
- // Mock cost is always 0
12139
- format
12140
- };
12141
- }
12142
- /**
12143
- * Map MIME type to audio format for SpeechGenerationResult.
12144
- * Defaults to "mp3" for unknown MIME types.
12145
- *
12146
- * @param mimeType - Audio MIME type string
12147
- * @returns Audio format identifier
12148
- */
12149
- mimeTypeToAudioFormat(mimeType) {
12150
- const mapping = {
12151
- "audio/mp3": "mp3",
12152
- "audio/mpeg": "mp3",
12153
- "audio/wav": "wav",
12154
- "audio/webm": "opus",
12155
- "audio/ogg": "opus"
12156
- };
12157
- return mapping[mimeType] ?? "mp3";
12158
- }
12159
- };
12160
- function createMockAdapter(options) {
12161
- return new MockProviderAdapter(options);
12162
- }
12163
-
12164
- // src/testing/mock-builder.ts
12165
- init_input_content();
12166
- init_messages();
12167
- function hasImageContent(content) {
12168
- if (typeof content === "string") return false;
12169
- return content.some((part) => isImagePart(part));
12170
- }
12171
- function hasAudioContent(content) {
12172
- if (typeof content === "string") return false;
12173
- return content.some((part) => isAudioPart(part));
12174
- }
12175
- function countImages(content) {
12176
- if (typeof content === "string") return 0;
12177
- return content.filter((part) => isImagePart(part)).length;
12178
- }
12179
- var MockBuilder = class {
12180
- matchers = [];
12181
- response = {};
12182
- label;
12183
- isOnce = false;
12184
- id;
12185
- /**
12186
- * Match calls to a specific model (by name, supports partial matching).
12187
- *
12188
- * @example
12189
- * mockLLM().forModel('gpt-5')
12190
- * mockLLM().forModel('claude') // matches any Claude model
12191
- */
12192
- forModel(modelName) {
12193
- if (!modelName || modelName.trim() === "") {
12194
- throw new Error("Model name cannot be empty");
12195
- }
12196
- this.matchers.push((ctx) => ctx.modelName.includes(modelName));
12197
- return this;
12198
- }
12199
- /**
12200
- * Match calls to any model.
12201
- * Useful when you want to mock responses regardless of the model used.
12202
- *
12203
- * @example
12204
- * mockLLM().forAnyModel()
12205
- */
12206
- forAnyModel() {
12207
- this.matchers.push(() => true);
12208
- return this;
12209
- }
12210
- /**
12211
- * Match calls to a specific provider.
12212
- *
12213
- * @example
12214
- * mockLLM().forProvider('openai')
12215
- * mockLLM().forProvider('anthropic')
12216
- */
12217
- forProvider(provider) {
12218
- if (!provider || provider.trim() === "") {
12219
- throw new Error("Provider name cannot be empty");
12220
- }
12221
- this.matchers.push((ctx) => ctx.provider === provider);
12222
- return this;
12223
- }
12224
- /**
12225
- * Match calls to any provider.
12226
- * Useful when you want to mock responses regardless of the provider used.
12227
- *
12228
- * @example
12229
- * mockLLM().forAnyProvider()
12230
- */
12231
- forAnyProvider() {
12232
- this.matchers.push(() => true);
12233
- return this;
12234
- }
12235
- /**
12236
- * Match when any message contains the given text (case-insensitive).
12237
- *
12238
- * @example
12239
- * mockLLM().whenMessageContains('hello')
12240
- */
12241
- whenMessageContains(text3) {
12242
- this.matchers.push(
12243
- (ctx) => ctx.messages.some(
12244
- (msg) => extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
12245
- )
12246
- );
12247
- return this;
12248
- }
12249
- /**
12250
- * Match when the last message contains the given text (case-insensitive).
12251
- *
12252
- * @example
12253
- * mockLLM().whenLastMessageContains('goodbye')
12254
- */
12255
- whenLastMessageContains(text3) {
12256
- this.matchers.push((ctx) => {
12257
- const lastMsg = ctx.messages[ctx.messages.length - 1];
12258
- if (!lastMsg) return false;
12259
- return extractMessageText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
12260
- });
12261
- return this;
12262
- }
12263
- /**
12264
- * Match when any message matches the given regex.
12265
- *
12266
- * @example
12267
- * mockLLM().whenMessageMatches(/calculate \d+/)
12268
- */
12269
- whenMessageMatches(regex) {
12270
- this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractMessageText(msg.content))));
12271
- return this;
12272
- }
12273
- /**
12274
- * Match when a message with a specific role contains text.
12275
- *
12276
- * @example
12277
- * mockLLM().whenRoleContains('system', 'You are a helpful assistant')
12278
- */
12279
- whenRoleContains(role, text3) {
12280
- this.matchers.push(
12281
- (ctx) => ctx.messages.some(
12282
- (msg) => msg.role === role && extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
12283
- )
12284
- );
12285
- return this;
12286
- }
12287
- /**
12288
- * Match based on the number of messages in the conversation.
12289
- *
12290
- * @example
12291
- * mockLLM().whenMessageCount((count) => count > 10)
12292
- */
12293
- whenMessageCount(predicate) {
12294
- this.matchers.push((ctx) => predicate(ctx.messages.length));
12295
- return this;
12296
- }
12297
- /**
12298
- * Add a custom matcher function.
12299
- * This provides full control over matching logic.
12300
- *
12301
- * @example
12302
- * mockLLM().when((ctx) => {
12303
- * return ctx.options.temperature > 0.8;
12304
- * })
12305
- */
12306
- when(matcher) {
12307
- this.matchers.push(matcher);
12308
- return this;
12309
- }
12310
- // ==========================================================================
12311
- // Multimodal Matchers
12312
- // ==========================================================================
12313
- /**
12314
- * Match when any message contains an image.
12315
- *
12316
- * @example
12317
- * mockLLM().whenMessageHasImage().returns("I see an image of a sunset.")
12318
- */
12319
- whenMessageHasImage() {
12320
- this.matchers.push((ctx) => ctx.messages.some((msg) => hasImageContent(msg.content)));
12321
- return this;
12322
- }
12323
- /**
12324
- * Match when any message contains audio.
12325
- *
12326
- * @example
12327
- * mockLLM().whenMessageHasAudio().returns("I hear music playing.")
12328
- */
12329
- whenMessageHasAudio() {
12330
- this.matchers.push((ctx) => ctx.messages.some((msg) => hasAudioContent(msg.content)));
12331
- return this;
12332
- }
12333
- /**
12334
- * Match based on the number of images in the last message.
12335
- *
12336
- * @example
12337
- * mockLLM().whenImageCount((n) => n >= 2).returns("Comparing multiple images...")
12338
- */
12339
- whenImageCount(predicate) {
12340
- this.matchers.push((ctx) => {
12341
- const lastMsg = ctx.messages[ctx.messages.length - 1];
12342
- if (!lastMsg) return false;
12343
- return predicate(countImages(lastMsg.content));
12344
- });
12345
- return this;
12346
- }
12347
- /**
12348
- * Set the text response to return.
12349
- * Can be a static string or a function that returns a string dynamically.
12350
- *
12351
- * @example
12352
- * mockLLM().returns('Hello, world!')
12353
- * mockLLM().returns(() => `Response at ${Date.now()}`)
12354
- * mockLLM().returns((ctx) => `You said: ${ctx.messages[0]?.content}`)
12355
- */
12356
- returns(text3) {
12357
- if (typeof text3 === "function") {
12358
- this.response = async (ctx) => {
12359
- const resolvedText = await Promise.resolve().then(() => text3(ctx));
12360
- return { text: resolvedText };
12361
- };
12362
- } else {
12363
- if (typeof this.response === "function") {
12364
- throw new Error("Cannot use returns() after withResponse() with a function");
12365
- }
12366
- this.response.text = text3;
12367
- }
12368
- return this;
12369
- }
12370
- /**
12371
- * Set gadget calls to include in the response.
12372
- *
12373
- * @example
12374
- * mockLLM().returnsGadgetCalls([
12375
- * { gadgetName: 'calculator', parameters: { op: 'add', a: 1, b: 2 } }
12376
- * ])
12377
- */
12378
- returnsGadgetCalls(calls) {
12379
- if (typeof this.response === "function") {
12380
- throw new Error("Cannot use returnsGadgetCalls() after withResponse() with a function");
12381
- }
12382
- this.response.gadgetCalls = calls;
12383
- return this;
12384
- }
12385
- /**
12386
- * Add a single gadget call to the response.
12387
- *
12388
- * @example
12389
- * mockLLM()
12390
- * .returnsGadgetCall('calculator', { op: 'add', a: 1, b: 2 })
12391
- * .returnsGadgetCall('logger', { message: 'Done!' })
12392
- */
12393
- returnsGadgetCall(gadgetName, parameters) {
12394
- if (typeof this.response === "function") {
12395
- throw new Error("Cannot use returnsGadgetCall() after withResponse() with a function");
12396
- }
12397
- if (!this.response.gadgetCalls) {
12398
- this.response.gadgetCalls = [];
12399
- }
12400
- this.response.gadgetCalls.push({ gadgetName, parameters });
12401
- return this;
12402
- }
12403
- // ==========================================================================
12404
- // Multimodal Response Helpers
12405
- // ==========================================================================
12406
- /**
12407
- * Return a single image in the response.
12408
- * Useful for mocking image generation endpoints.
12409
- *
12410
- * @param data - Image data (base64 string or Buffer)
12411
- * @param mimeType - MIME type (auto-detected if Buffer provided without type)
12412
- *
12413
- * @example
12414
- * mockLLM()
12415
- * .forModel('dall-e-3')
12416
- * .returnsImage(pngBuffer)
12417
- * .register();
12418
- */
12419
- returnsImage(data, mimeType) {
12420
- if (typeof this.response === "function") {
12421
- throw new Error("Cannot use returnsImage() after withResponse() with a function");
12422
- }
12423
- let imageData;
12424
- let imageMime;
12425
- if (typeof data === "string") {
12426
- imageData = data;
12427
- if (!mimeType) {
12428
- throw new Error("MIME type is required when providing base64 string data");
12429
- }
12430
- imageMime = mimeType;
12431
- } else {
12432
- imageData = toBase64(data);
12433
- const detected = mimeType ?? detectImageMimeType(data);
12434
- if (!detected) {
12435
- throw new Error(
12436
- "Could not detect image MIME type. Please provide the mimeType parameter explicitly."
12437
- );
12438
- }
12439
- imageMime = detected;
12440
- }
12441
- if (!this.response.images) {
12442
- this.response.images = [];
12443
- }
12444
- this.response.images.push({ data: imageData, mimeType: imageMime });
12445
- return this;
12446
- }
12447
- /**
12448
- * Return multiple images in the response.
12449
- *
12450
- * @example
12451
- * mockLLM()
12452
- * .forModel('dall-e-3')
12453
- * .returnsImages([
12454
- * { data: pngBuffer1 },
12455
- * { data: pngBuffer2 },
12456
- * ])
12457
- * .register();
12458
- */
12459
- returnsImages(images) {
12460
- for (const img of images) {
12461
- this.returnsImage(img.data, img.mimeType);
12462
- if (img.revisedPrompt && this.response && typeof this.response !== "function") {
12463
- const lastImage = this.response.images?.[this.response.images.length - 1];
12464
- if (lastImage) {
12465
- lastImage.revisedPrompt = img.revisedPrompt;
12466
- }
12467
- }
12468
- }
12469
- return this;
12470
- }
12471
- /**
12472
- * Return audio data in the response.
12473
- * Useful for mocking speech synthesis endpoints.
12474
- *
12475
- * @param data - Audio data (base64 string or Buffer)
12476
- * @param mimeType - MIME type (auto-detected if Buffer provided without type)
12477
- *
12478
- * @example
12479
- * mockLLM()
12480
- * .forModel('tts-1')
12481
- * .returnsAudio(mp3Buffer)
12482
- * .register();
12483
- */
12484
- returnsAudio(data, mimeType) {
12485
- if (typeof this.response === "function") {
12486
- throw new Error("Cannot use returnsAudio() after withResponse() with a function");
12487
- }
12488
- let audioData;
12489
- let audioMime;
12490
- if (typeof data === "string") {
12491
- audioData = data;
12492
- if (!mimeType) {
12493
- throw new Error("MIME type is required when providing base64 string data");
12494
- }
12495
- audioMime = mimeType;
12496
- } else {
12497
- audioData = toBase64(data);
12498
- const detected = mimeType ?? detectAudioMimeType(data);
12499
- if (!detected) {
12500
- throw new Error(
12501
- "Could not detect audio MIME type. Please provide the mimeType parameter explicitly."
12502
- );
12503
- }
12504
- audioMime = detected;
12505
- }
12506
- this.response.audio = { data: audioData, mimeType: audioMime };
12507
- return this;
12508
- }
12509
- /**
12510
- * Set the complete mock response object.
12511
- * This allows full control over all response properties.
12512
- * Can also be a function that generates the response dynamically based on context.
12513
- *
12514
- * @example
12515
- * // Static response
12516
- * mockLLM().withResponse({
12517
- * text: 'Hello',
12518
- * usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 },
12519
- * finishReason: 'stop'
12520
- * })
12521
- *
12522
- * @example
12523
- * // Dynamic response
12524
- * mockLLM().withResponse((ctx) => ({
12525
- * text: `You said: ${ctx.messages[ctx.messages.length - 1]?.content}`,
12526
- * usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 }
12527
- * }))
12528
- */
12529
- withResponse(response) {
12530
- this.response = response;
12531
- return this;
12532
- }
12533
- /**
12534
- * Set simulated token usage.
12535
- *
12536
- * @example
12537
- * mockLLM().withUsage({ inputTokens: 100, outputTokens: 50, totalTokens: 150 })
12538
- */
12539
- withUsage(usage) {
12540
- if (typeof this.response === "function") {
12541
- throw new Error("Cannot use withUsage() after withResponse() with a function");
12542
- }
12543
- if (usage.inputTokens < 0 || usage.outputTokens < 0 || usage.totalTokens < 0) {
12544
- throw new Error("Token counts cannot be negative");
12545
- }
12546
- if (usage.totalTokens !== usage.inputTokens + usage.outputTokens) {
12547
- throw new Error("totalTokens must equal inputTokens + outputTokens");
12548
- }
12549
- this.response.usage = usage;
12550
- return this;
12551
- }
12552
- /**
12553
- * Set the finish reason.
12554
- *
12555
- * @example
12556
- * mockLLM().withFinishReason('stop')
12557
- * mockLLM().withFinishReason('length')
12558
- */
12559
- withFinishReason(reason) {
12560
- if (typeof this.response === "function") {
12561
- throw new Error("Cannot use withFinishReason() after withResponse() with a function");
12562
- }
12563
- this.response.finishReason = reason;
12564
- return this;
12565
- }
12566
- /**
12567
- * Set initial delay before streaming starts (simulates network latency).
12568
- *
12569
- * @example
12570
- * mockLLM().withDelay(100) // 100ms delay
12571
- */
12572
- withDelay(ms) {
12573
- if (typeof this.response === "function") {
12574
- throw new Error("Cannot use withDelay() after withResponse() with a function");
12575
- }
12576
- if (ms < 0) {
12577
- throw new Error("Delay must be non-negative");
12578
- }
12579
- this.response.delayMs = ms;
12580
- return this;
12581
- }
12582
- /**
12583
- * Set delay between stream chunks (simulates realistic streaming).
12584
- *
12585
- * @example
12586
- * mockLLM().withStreamDelay(10) // 10ms between chunks
12587
- */
12588
- withStreamDelay(ms) {
12589
- if (typeof this.response === "function") {
12590
- throw new Error("Cannot use withStreamDelay() after withResponse() with a function");
12591
- }
12592
- if (ms < 0) {
12593
- throw new Error("Stream delay must be non-negative");
12594
- }
12595
- this.response.streamDelayMs = ms;
12596
- return this;
12597
- }
12598
- /**
12599
- * Set a label for this mock (useful for debugging).
12600
- *
12601
- * @example
12602
- * mockLLM().withLabel('greeting mock')
12603
- */
12604
- withLabel(label) {
12605
- this.label = label;
12606
- return this;
12607
- }
12608
- /**
12609
- * Set a specific ID for this mock.
12610
- *
12611
- * @example
12612
- * mockLLM().withId('my-custom-mock-id')
12613
- */
12614
- withId(id) {
12615
- this.id = id;
12616
- return this;
12617
- }
12618
- /**
12619
- * Mark this mock as one-time use (will be removed after first match).
12620
- *
12621
- * @example
12622
- * mockLLM().once()
12623
- */
12624
- once() {
12625
- this.isOnce = true;
12626
- return this;
12627
- }
12628
- /**
12629
- * Build the mock registration without registering it.
12630
- * Useful if you want to register it manually later.
12631
- *
12632
- * @returns The built MockRegistration object (without id if not specified)
12633
- */
12634
- build() {
12635
- if (this.matchers.length === 0) {
12636
- throw new Error(
12637
- "Mock must have at least one matcher. Use .when(), .forModel(), .forProvider(), etc."
12638
- );
12639
- }
12640
- const combinedMatcher = async (ctx) => {
12641
- for (const matcher of this.matchers) {
12642
- const matches = await Promise.resolve(matcher(ctx));
12643
- if (!matches) return false;
12644
- }
12645
- return true;
12646
- };
12647
- return {
12648
- id: this.id,
12649
- matcher: combinedMatcher,
12650
- response: this.response,
12651
- label: this.label,
12652
- once: this.isOnce
12653
- };
12654
- }
12655
- /**
12656
- * Register this mock with the global MockManager.
12657
- * Returns the ID of the registered mock.
12658
- *
12659
- * @example
12660
- * const mockId = mockLLM().forModel('gpt-5').returns('Hello!').register();
12661
- * // Later: getMockManager().unregister(mockId);
12662
- */
12663
- register() {
12664
- const mockManager = getMockManager();
12665
- const registration = this.build();
12666
- return mockManager.register(registration);
12667
- }
12668
- };
12669
- function mockLLM() {
12670
- return new MockBuilder();
12671
- }
12672
-
12673
- // src/testing/mock-client.ts
12674
- init_client();
12675
- function createMockClient(options) {
12676
- return new LLMist({
12677
- adapters: [new MockProviderAdapter(options)],
12678
- autoDiscoverProviders: false,
12679
- defaultProvider: "mock"
12680
- });
12681
- }
12682
-
12683
- // src/testing/mock-gadget.ts
12684
- init_gadget();
12685
-
12686
- // src/index.ts
12687
12056
  function getHostExports(ctx) {
12688
12057
  if (!ctx?.hostExports) {
12689
12058
  throw new Error(
@@ -12696,6 +12065,7 @@ function getHostExports(ctx) {
12696
12065
  0 && (module.exports = {
12697
12066
  AbortException,
12698
12067
  AbstractGadget,
12068
+ Agent,
12699
12069
  AgentBuilder,
12700
12070
  AnthropicMessagesProvider,
12701
12071
  CompactionManager,
@@ -12703,8 +12073,13 @@ function getHostExports(ctx) {
12703
12073
  DEFAULT_COMPACTION_CONFIG,
12704
12074
  DEFAULT_HINTS,
12705
12075
  DEFAULT_PROMPTS,
12076
+ DEFAULT_RETRY_CONFIG,
12706
12077
  DEFAULT_SUMMARIZATION_PROMPT,
12707
12078
  ExecutionTree,
12079
+ FALLBACK_CHARS_PER_TOKEN,
12080
+ GADGET_ARG_PREFIX,
12081
+ GADGET_END_PREFIX,
12082
+ GADGET_START_PREFIX,
12708
12083
  Gadget,
12709
12084
  GadgetCallParser,
12710
12085
  GadgetExecutor,
@@ -12718,9 +12093,6 @@ function getHostExports(ctx) {
12718
12093
  LLMist,
12719
12094
  MODEL_ALIASES,
12720
12095
  MediaStore,
12721
- MockBuilder,
12722
- MockManager,
12723
- MockProviderAdapter,
12724
12096
  ModelIdentifierParser,
12725
12097
  ModelRegistry,
12726
12098
  OpenAIChatProvider,
@@ -12741,11 +12113,7 @@ function getHostExports(ctx) {
12741
12113
  createHints,
12742
12114
  createLogger,
12743
12115
  createMediaOutput,
12744
- createMockAdapter,
12745
- createMockClient,
12746
- createMockStream,
12747
12116
  createOpenAIProviderFromEnv,
12748
- createTextMockStream,
12749
12117
  defaultLogger,
12750
12118
  detectAudioMimeType,
12751
12119
  detectImageMimeType,
@@ -12754,8 +12122,8 @@ function getHostExports(ctx) {
12754
12122
  filterByDepth,
12755
12123
  filterByParent,
12756
12124
  filterRootEvents,
12125
+ formatLLMError,
12757
12126
  getHostExports,
12758
- getMockManager,
12759
12127
  getModelId,
12760
12128
  getProvider,
12761
12129
  groupByParent,
@@ -12763,16 +12131,17 @@ function getHostExports(ctx) {
12763
12131
  imageFromBase64,
12764
12132
  imageFromBuffer,
12765
12133
  imageFromUrl,
12134
+ isAbortError,
12766
12135
  isAudioPart,
12767
12136
  isDataUrl,
12768
12137
  isGadgetEvent,
12769
12138
  isImagePart,
12770
12139
  isLLMEvent,
12140
+ isRetryableError,
12771
12141
  isRootEvent,
12772
12142
  isSubagentEvent,
12773
12143
  isTextPart,
12774
12144
  iterationProgressHint,
12775
- mockLLM,
12776
12145
  normalizeMessageContent,
12777
12146
  parallelGadgetHint,
12778
12147
  parseDataUrl,
@@ -12780,6 +12149,7 @@ function getHostExports(ctx) {
12780
12149
  resolveHintTemplate,
12781
12150
  resolveModel,
12782
12151
  resolvePromptTemplate,
12152
+ resolveRetryConfig,
12783
12153
  resolveRulesTemplate,
12784
12154
  resolveSubagentModel,
12785
12155
  resolveValue,
@@ -12789,11 +12159,13 @@ function getHostExports(ctx) {
12789
12159
  resultWithImages,
12790
12160
  resultWithMedia,
12791
12161
  runWithHandlers,
12162
+ schemaToJSONSchema,
12792
12163
  stream,
12793
12164
  text,
12794
12165
  toBase64,
12795
12166
  validateAndApplyDefaults,
12796
12167
  validateGadgetParams,
12168
+ validateGadgetSchema,
12797
12169
  z
12798
12170
  });
12799
12171
  //# sourceMappingURL=index.cjs.map