@probelabs/probe 0.6.0-rc278 → 0.6.0-rc280

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/cjs/index.cjs CHANGED
@@ -636,9 +636,7 @@ async function acquireFileLock(lockPath, version2) {
636
636
  };
637
637
  try {
638
638
  await import_fs_extra2.default.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
639
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
640
- console.log(`Acquired file lock: ${lockPath}`);
641
- }
639
+ console.log(`Acquired file lock: ${lockPath}`);
642
640
  return true;
643
641
  } catch (error2) {
644
642
  if (error2.code === "EEXIST") {
@@ -646,15 +644,11 @@ async function acquireFileLock(lockPath, version2) {
646
644
  const existingLock = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
647
645
  const lockAge = Date.now() - existingLock.timestamp;
648
646
  if (lockAge > LOCK_TIMEOUT_MS) {
649
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
650
- console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
651
- }
647
+ console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
652
648
  await import_fs_extra2.default.remove(lockPath);
653
649
  return false;
654
650
  }
655
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
656
- console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
657
- }
651
+ console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
658
652
  return false;
659
653
  } catch (readError) {
660
654
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
@@ -695,36 +689,36 @@ async function releaseFileLock(lockPath) {
695
689
  }
696
690
  async function waitForFileLock(lockPath, binaryPath) {
697
691
  const startTime = Date.now();
692
+ let lastStatusTime = startTime;
693
+ console.log(`Waiting for file lock to clear: ${lockPath}`);
698
694
  while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
699
695
  if (await import_fs_extra2.default.pathExists(binaryPath)) {
700
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
701
- console.log(`Binary now available at ${binaryPath}, download completed by another process`);
702
- }
696
+ const waitedSeconds = Math.round((Date.now() - startTime) / 1e3);
697
+ console.log(`Binary now available at ${binaryPath}, download completed by another process (waited ${waitedSeconds}s)`);
703
698
  return true;
704
699
  }
705
700
  const lockExists = await import_fs_extra2.default.pathExists(lockPath);
706
701
  if (!lockExists) {
707
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
708
- console.log(`Lock file removed but binary not found - download may have failed`);
709
- }
702
+ console.log(`Lock file removed but binary not found - download may have failed`);
710
703
  return false;
711
704
  }
712
705
  try {
713
706
  const lockData = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
714
707
  const lockAge = Date.now() - lockData.timestamp;
715
708
  if (lockAge > LOCK_TIMEOUT_MS) {
716
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
717
- console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
718
- }
709
+ console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
719
710
  return false;
720
711
  }
721
712
  } catch {
722
713
  }
714
+ if (Date.now() - lastStatusTime >= 15e3) {
715
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
716
+ console.log(`Still waiting for file lock (${elapsedSeconds}s/${MAX_LOCK_WAIT_MS / 1e3}s max)`);
717
+ lastStatusTime = Date.now();
718
+ }
723
719
  await new Promise((resolve8) => setTimeout(resolve8, LOCK_POLL_INTERVAL_MS));
724
720
  }
725
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
726
- console.log(`Timeout waiting for file lock`);
727
- }
721
+ console.log(`Timeout waiting for file lock after ${MAX_LOCK_WAIT_MS / 1e3}s`);
728
722
  return false;
729
723
  }
730
724
  async function withDownloadLock(version2, downloadFn) {
@@ -738,9 +732,7 @@ async function withDownloadLock(version2, downloadFn) {
738
732
  }
739
733
  downloadLocks.delete(lockKey);
740
734
  } else {
741
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
742
- console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
743
- }
735
+ console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
744
736
  try {
745
737
  return await lock.promise;
746
738
  } catch (error2) {
@@ -750,10 +742,16 @@ async function withDownloadLock(version2, downloadFn) {
750
742
  }
751
743
  }
752
744
  }
745
+ let timeoutId = null;
753
746
  const downloadPromise = Promise.race([
754
747
  downloadFn(),
755
748
  new Promise(
756
- (_, reject2) => setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS)
749
+ (_, reject2) => {
750
+ timeoutId = setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS);
751
+ if (timeoutId.unref) {
752
+ timeoutId.unref();
753
+ }
754
+ }
757
755
  )
758
756
  ]);
759
757
  downloadLocks.set(lockKey, {
@@ -764,6 +762,9 @@ async function withDownloadLock(version2, downloadFn) {
764
762
  const result = await downloadPromise;
765
763
  return result;
766
764
  } finally {
765
+ if (timeoutId) {
766
+ clearTimeout(timeoutId);
767
+ }
767
768
  downloadLocks.delete(lockKey);
768
769
  }
769
770
  }
@@ -36294,14 +36295,14 @@ function resolveTargetPath(target, cwd) {
36294
36295
  }
36295
36296
  return filePart + suffix;
36296
36297
  }
36297
- var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
36298
+ var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
36298
36299
  var init_common2 = __esm({
36299
36300
  "src/tools/common.js"() {
36300
36301
  "use strict";
36301
36302
  init_zod();
36302
36303
  import_path5 = require("path");
36303
36304
  searchSchema = external_exports.object({
36304
- query: external_exports.string().describe("Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
36305
+ query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
36305
36306
  path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
36306
36307
  exact: external_exports.boolean().optional().default(false).describe('Default (false) enables stemming and keyword splitting for exploratory search - "getUserData" matches "get", "user", "data", etc. Set true for precise symbol lookup where "getUserData" matches only "getUserData". Use true when you know the exact symbol name.'),
36307
36308
  maxTokens: external_exports.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
@@ -36309,7 +36310,7 @@ var init_common2 = __esm({
36309
36310
  nextPage: external_exports.boolean().optional().default(false).describe("Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.")
36310
36311
  });
36311
36312
  searchAllSchema = external_exports.object({
36312
- query: external_exports.string().describe("Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
36313
+ query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
36313
36314
  path: external_exports.string().optional().default(".").describe("Path to search in."),
36314
36315
  exact: external_exports.boolean().optional().default(false).describe("Use exact matching instead of stemming."),
36315
36316
  maxTokensPerPage: external_exports.number().optional().default(2e4).describe("Tokens per page when paginating. Default 20000."),
@@ -36416,7 +36417,8 @@ var init_common2 = __esm({
36416
36417
  };
36417
36418
  }
36418
36419
  };
36419
- searchDescription = "Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.";
36420
+ searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions. NOTE: By default, search handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT manually try keyword variations like "getAllUsers" then "get_all_users" then "GetAllUsers". One search covers all variations.';
36421
+ searchDelegateDescription = 'Search code in the repository by asking a question. Accepts natural language questions (e.g., "How does authentication work?", "Where is the user validation logic?"). A specialized subagent breaks down your question into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself \u2014 just ask the question naturally.';
36420
36422
  queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
36421
36423
  extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.";
36422
36424
  delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
@@ -101736,6 +101738,7 @@ function generateSandboxGlobals(options) {
101736
101738
  executing.add(p5);
101737
101739
  results.push(p5);
101738
101740
  if (executing.size >= mapConcurrency) {
101741
+ console.error(`[map] Concurrency limit reached (${executing.size}/${mapConcurrency}), waiting for a slot...`);
101739
101742
  await Promise.race(executing);
101740
101743
  }
101741
101744
  }
@@ -105965,8 +105968,23 @@ __export(ProbeAgent_exports, {
105965
105968
  ENGINE_ACTIVITY_TIMEOUT_DEFAULT: () => ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
105966
105969
  ENGINE_ACTIVITY_TIMEOUT_MAX: () => ENGINE_ACTIVITY_TIMEOUT_MAX,
105967
105970
  ENGINE_ACTIVITY_TIMEOUT_MIN: () => ENGINE_ACTIVITY_TIMEOUT_MIN,
105968
- ProbeAgent: () => ProbeAgent
105971
+ ProbeAgent: () => ProbeAgent,
105972
+ debugLogToolResults: () => debugLogToolResults,
105973
+ debugTruncate: () => debugTruncate
105969
105974
  });
105975
+ function debugTruncate(s5, limit = 200) {
105976
+ if (s5.length <= limit) return s5;
105977
+ const half = Math.floor(limit / 2);
105978
+ return s5.substring(0, half) + ` ... [${s5.length} chars] ... ` + s5.substring(s5.length - half);
105979
+ }
105980
+ function debugLogToolResults(toolResults) {
105981
+ if (!toolResults || toolResults.length === 0) return;
105982
+ for (const tr of toolResults) {
105983
+ const argsStr = JSON.stringify(tr.args || {});
105984
+ const resultStr = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result || "");
105985
+ console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
105986
+ }
105987
+ }
105970
105988
  var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai4, import_crypto8, import_events4, import_fs10, import_promises6, import_path15, ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
105971
105989
  var init_ProbeAgent = __esm({
105972
105990
  "src/agent/ProbeAgent.js"() {
@@ -106086,6 +106104,7 @@ var init_ProbeAgent = __esm({
106086
106104
  this.enableExecutePlan = !!options.enableExecutePlan;
106087
106105
  this.debug = options.debug || process.env.DEBUG === "1";
106088
106106
  this.cancelled = false;
106107
+ this._abortController = new AbortController();
106089
106108
  this.tracer = options.tracer || null;
106090
106109
  this.outline = !!options.outline;
106091
106110
  this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
@@ -106114,6 +106133,7 @@ var init_ProbeAgent = __esm({
106114
106133
  this.completionPrompt = options.completionPrompt || null;
106115
106134
  this.thinkingEffort = options.thinkingEffort || null;
106116
106135
  const effectiveAllowedTools = options.disableTools ? [] : options.allowedTools;
106136
+ this._rawAllowedTools = options.allowedTools;
106117
106137
  this.allowedTools = this._parseAllowedTools(effectiveAllowedTools);
106118
106138
  this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
106119
106139
  this.hooks = new HookManager();
@@ -106256,6 +106276,16 @@ var init_ProbeAgent = __esm({
106256
106276
  _filterMcpTools(mcpToolNames) {
106257
106277
  return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
106258
106278
  }
106279
+ /**
106280
+ * Check if query tool was explicitly listed in allowedTools (not via wildcard).
106281
+ * Query (ast-grep) is excluded by default because models struggle with AST pattern syntax.
106282
+ * @returns {boolean}
106283
+ * @private
106284
+ */
106285
+ _isQueryExplicitlyAllowed() {
106286
+ if (!this._rawAllowedTools) return false;
106287
+ return Array.isArray(this._rawAllowedTools) && this._rawAllowedTools.includes("query");
106288
+ }
106259
106289
  /**
106260
106290
  * Check if tracer is AppTracer (expects sessionId as first param) vs SimpleAppTracer
106261
106291
  * @returns {boolean} - True if tracer is AppTracer style (requires sessionId)
@@ -106531,6 +106561,8 @@ var init_ProbeAgent = __esm({
106531
106561
  searchDelegateModel: this.searchDelegateModel,
106532
106562
  delegationManager: this.delegationManager,
106533
106563
  // Per-instance delegation limits
106564
+ parentAbortSignal: this._abortController.signal,
106565
+ // Propagate cancellation to delegations
106534
106566
  outputBuffer: this._outputBuffer,
106535
106567
  concurrencyLimiter: this.concurrencyLimiter,
106536
106568
  // Global AI concurrency limiter
@@ -106547,7 +106579,7 @@ var init_ProbeAgent = __esm({
106547
106579
  if (wrappedTools.searchToolInstance && isToolAllowed("search")) {
106548
106580
  this.toolImplementations.search = wrappedTools.searchToolInstance;
106549
106581
  }
106550
- if (wrappedTools.queryToolInstance && isToolAllowed("query")) {
106582
+ if (wrappedTools.queryToolInstance && isToolAllowed("query") && this._isQueryExplicitlyAllowed()) {
106551
106583
  this.toolImplementations.query = wrappedTools.queryToolInstance;
106552
106584
  }
106553
106585
  if (wrappedTools.extractToolInstance && isToolAllowed("extract")) {
@@ -106978,6 +107010,15 @@ var init_ProbeAgent = __esm({
106978
107010
  }
106979
107011
  const controller = new AbortController();
106980
107012
  const timeoutState = { timeoutId: null };
107013
+ if (this._abortController.signal.aborted) {
107014
+ controller.abort();
107015
+ } else {
107016
+ const onAgentAbort = () => controller.abort();
107017
+ this._abortController.signal.addEventListener("abort", onAgentAbort, { once: true });
107018
+ controller.signal.addEventListener("abort", () => {
107019
+ this._abortController.signal.removeEventListener("abort", onAgentAbort);
107020
+ }, { once: true });
107021
+ }
106981
107022
  if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
106982
107023
  timeoutState.timeoutId = setTimeout(() => {
106983
107024
  controller.abort();
@@ -107281,7 +107322,8 @@ var init_ProbeAgent = __esm({
107281
107322
  allowEdit: this.allowEdit,
107282
107323
  allowedTools: allowedToolsForDelegate,
107283
107324
  debug: this.debug,
107284
- tracer: this.tracer
107325
+ tracer: this.tracer,
107326
+ parentAbortSignal: this._abortController.signal
107285
107327
  };
107286
107328
  if (this.debug) {
107287
107329
  console.log(`[DEBUG] Executing delegate tool`);
@@ -107476,12 +107518,13 @@ var init_ProbeAgent = __esm({
107476
107518
  const toolMap = {
107477
107519
  search: {
107478
107520
  schema: searchSchema,
107479
- description: "Search code in the repository using keyword queries with Elasticsearch syntax."
107480
- },
107481
- query: {
107482
- schema: querySchema,
107483
- description: "Search code using ast-grep structural pattern matching."
107521
+ description: this.searchDelegate ? "Search code in the repository by asking a question. Accepts natural language questions \u2014 a subagent breaks them into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself." : "Search code in the repository using keyword queries with Elasticsearch syntax. Handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT try keyword variations manually."
107484
107522
  },
107523
+ // query tool (ast-grep) removed from AI-facing tools — models struggle with pattern syntax
107524
+ // query: {
107525
+ // schema: querySchema,
107526
+ // description: 'Search code using ast-grep structural pattern matching.'
107527
+ // },
107485
107528
  extract: {
107486
107529
  schema: extractSchema,
107487
107530
  description: "Extract code blocks from files based on file paths and optional line numbers."
@@ -108149,25 +108192,27 @@ ${this.architectureContext.content}
108149
108192
  } else {
108150
108193
  systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
108151
108194
  }
108195
+ const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
108152
108196
  systemPrompt += `You have access to powerful code search and analysis tools through MCP:
108153
- - search: Find code patterns using semantic search
108197
+ ${searchToolDesc1}
108154
108198
  - extract: Extract specific code sections with context
108155
- - query: Use AST patterns for structural code matching
108156
108199
  - listFiles: Browse directory contents
108157
108200
  - searchFiles: Find files by name patterns`;
108158
108201
  if (this.enableBash) {
108159
108202
  systemPrompt += `
108160
108203
  - bash: Execute bash commands for system operations`;
108161
108204
  }
108162
- const searchGuidance = this.searchDelegate ? "1. Start with search to retrieve extracted code blocks" : "1. Start with search to find relevant code patterns";
108163
- const extractGuidance = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
108205
+ const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
108206
+ const extractGuidance1 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
108164
108207
  systemPrompt += `
108165
108208
 
108166
108209
  When exploring code:
108167
- ${searchGuidance}
108168
- ${extractGuidance}
108210
+ ${searchGuidance1}
108211
+ ${extractGuidance1}
108169
108212
  3. Prefer focused, specific searches over broad queries
108170
- 4. Combine multiple tools to build complete understanding`;
108213
+ 4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
108214
+ 5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
108215
+ 6. Combine multiple tools to build complete understanding`;
108171
108216
  if (this.allowedFolders && this.allowedFolders.length > 0) {
108172
108217
  systemPrompt += `
108173
108218
 
@@ -108202,25 +108247,27 @@ Workspace: ${this.allowedFolders.join(", ")}`;
108202
108247
  } else {
108203
108248
  systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
108204
108249
  }
108250
+ const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
108205
108251
  systemPrompt += `You have access to powerful code search and analysis tools through MCP:
108206
- - search: Find code patterns using semantic search
108252
+ ${searchToolDesc2}
108207
108253
  - extract: Extract specific code sections with context
108208
- - query: Use AST patterns for structural code matching
108209
108254
  - listFiles: Browse directory contents
108210
108255
  - searchFiles: Find files by name patterns`;
108211
108256
  if (this.enableBash) {
108212
108257
  systemPrompt += `
108213
108258
  - bash: Execute bash commands for system operations`;
108214
108259
  }
108215
- const searchGuidance = this.searchDelegate ? "1. Start with search to retrieve extracted code blocks" : "1. Start with search to find relevant code patterns";
108216
- const extractGuidance = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
108260
+ const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
108261
+ const extractGuidance2 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
108217
108262
  systemPrompt += `
108218
108263
 
108219
108264
  When exploring code:
108220
- ${searchGuidance}
108221
- ${extractGuidance}
108265
+ ${searchGuidance2}
108266
+ ${extractGuidance2}
108222
108267
  3. Prefer focused, specific searches over broad queries
108223
- 4. Combine multiple tools to build complete understanding`;
108268
+ 4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
108269
+ 5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
108270
+ 6. Combine multiple tools to build complete understanding`;
108224
108271
  if (this.allowedFolders && this.allowedFolders.length > 0) {
108225
108272
  systemPrompt += `
108226
108273
 
@@ -108271,10 +108318,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
108271
108318
  Follow these instructions carefully:
108272
108319
  1. Analyze the user's request.
108273
108320
  2. Use the available tools step-by-step to fulfill the request.
108274
- 3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " It already returns extracted code blocks; use extract only to expand context or read full files." : " Read full files only if really necessary."}
108321
+ 3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
108275
108322
  4. Ensure to get really deep and understand the full picture before answering.
108276
108323
  5. Once the task is fully completed, use the attempt_completion tool to provide the final result.
108277
- 6. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.${this.allowEdit ? `
108324
+ 6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}${this.allowEdit ? `
108278
108325
  7. When modifying files, choose the appropriate tool:
108279
108326
  - Use 'edit' for all code modifications:
108280
108327
  * PREFERRED: Use start_line (and optionally end_line) for line-targeted editing \u2014 this is the safest and most precise approach.${this.hashLines ? ' Use the line:hash references from extract/search output (e.g. "42:ab") for integrity verification.' : ""} Always use extract first to see line numbers${this.hashLines ? " and hashes" : ""}, then edit by line reference.
@@ -108598,6 +108645,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
108598
108645
  completionResult = result;
108599
108646
  completionAttempted = true;
108600
108647
  }, toolContext);
108648
+ if (this.debug) {
108649
+ const toolNames = Object.keys(tools2);
108650
+ console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
108651
+ }
108601
108652
  let maxResponseTokens = this.maxResponseTokens;
108602
108653
  if (!maxResponseTokens) {
108603
108654
  maxResponseTokens = 4e3;
@@ -108637,6 +108688,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
108637
108688
  }
108638
108689
  if (this.debug) {
108639
108690
  console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
108691
+ debugLogToolResults(toolResults);
108640
108692
  }
108641
108693
  }
108642
108694
  };
@@ -108793,6 +108845,7 @@ Double-check your response based on the criteria above. If everything looks good
108793
108845
  }
108794
108846
  if (this.debug) {
108795
108847
  console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
108848
+ debugLogToolResults(toolResults);
108796
108849
  }
108797
108850
  }
108798
108851
  };
@@ -109503,6 +109556,9 @@ Convert your previous response content into actual JSON data that follows this s
109503
109556
  * Clean up resources (including MCP connections)
109504
109557
  */
109505
109558
  async cleanup() {
109559
+ if (!this._abortController.signal.aborted) {
109560
+ this._abortController.abort();
109561
+ }
109506
109562
  if (this.mcpBridge) {
109507
109563
  try {
109508
109564
  await this.mcpBridge.cleanup();
@@ -109526,14 +109582,25 @@ Convert your previous response content into actual JSON data that follows this s
109526
109582
  this.clearHistory();
109527
109583
  }
109528
109584
  /**
109529
- * Cancel the current request
109585
+ * Cancel the current request and all in-flight delegations.
109586
+ * Aborts the internal AbortController so streamText, subagents,
109587
+ * and any code checking the signal will stop.
109530
109588
  */
109531
109589
  cancel() {
109532
109590
  this.cancelled = true;
109591
+ this._abortController.abort();
109533
109592
  if (this.debug) {
109534
109593
  console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
109535
109594
  }
109536
109595
  }
109596
+ /**
109597
+ * Get the abort signal for this agent.
109598
+ * Delegations and subagents should check this signal.
109599
+ * @returns {AbortSignal}
109600
+ */
109601
+ get abortSignal() {
109602
+ return this._abortController.signal;
109603
+ }
109537
109604
  };
109538
109605
  }
109539
109606
  });
@@ -109566,12 +109633,17 @@ async function delegate({
109566
109633
  mcpConfigPath = null,
109567
109634
  delegationManager = null,
109568
109635
  // Optional per-instance manager, falls back to default singleton
109569
- concurrencyLimiter = null
109636
+ concurrencyLimiter = null,
109570
109637
  // Optional global AI concurrency limiter
109638
+ parentAbortSignal = null
109639
+ // Optional AbortSignal from parent to cancel this delegation
109571
109640
  }) {
109572
109641
  if (!task || typeof task !== "string") {
109573
109642
  throw new Error("Task parameter is required and must be a string");
109574
109643
  }
109644
+ if (parentAbortSignal?.aborted) {
109645
+ throw new Error("Delegation cancelled: parent operation was aborted");
109646
+ }
109575
109647
  const hasExplicitTimeout = Object.prototype.hasOwnProperty.call(arguments?.[0] ?? {}, "timeout");
109576
109648
  if (!hasExplicitTimeout) {
109577
109649
  const envTimeoutMs = parseInt(process.env.DELEGATION_TIMEOUT_MS || "", 10);
@@ -109656,12 +109728,37 @@ async function delegate({
109656
109728
  }
109657
109729
  const timeoutPromise = new Promise((_, reject2) => {
109658
109730
  timeoutId = setTimeout(() => {
109731
+ subagent.cancel();
109659
109732
  reject2(new Error(`Delegation timed out after ${timeout} seconds`));
109660
109733
  }, timeout * 1e3);
109661
109734
  });
109735
+ let parentAbortHandler;
109736
+ const parentAbortPromise = new Promise((_, reject2) => {
109737
+ if (parentAbortSignal) {
109738
+ if (parentAbortSignal.aborted) {
109739
+ subagent.cancel();
109740
+ reject2(new Error("Delegation cancelled: parent operation was aborted"));
109741
+ return;
109742
+ }
109743
+ parentAbortHandler = () => {
109744
+ subagent.cancel();
109745
+ reject2(new Error("Delegation cancelled: parent operation was aborted"));
109746
+ };
109747
+ parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
109748
+ }
109749
+ });
109662
109750
  const answerOptions = schema ? { schema } : void 0;
109663
109751
  const answerPromise = answerOptions ? subagent.answer(task, [], answerOptions) : subagent.answer(task);
109664
- const response = await Promise.race([answerPromise, timeoutPromise]);
109752
+ const racers = [answerPromise, timeoutPromise];
109753
+ if (parentAbortSignal) racers.push(parentAbortPromise);
109754
+ let response;
109755
+ try {
109756
+ response = await Promise.race(racers);
109757
+ } finally {
109758
+ if (parentAbortHandler && parentAbortSignal) {
109759
+ parentAbortSignal.removeEventListener("abort", parentAbortHandler);
109760
+ }
109761
+ }
109665
109762
  if (timeoutId !== null) {
109666
109763
  clearTimeout(timeoutId);
109667
109764
  timeoutId = null;
@@ -109817,10 +109914,9 @@ var init_delegate = __esm({
109817
109914
  if (this.tryAcquire(parentSessionId)) {
109818
109915
  return true;
109819
109916
  }
109820
- if (debug) {
109821
- console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length}, timeout: ${effectiveTimeout}ms)`);
109822
- }
109917
+ console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length + 1}, timeout: ${effectiveTimeout}ms)`);
109823
109918
  return new Promise((resolve8, reject2) => {
109919
+ const queuedAt = Date.now();
109824
109920
  const entry = {
109825
109921
  resolve: null,
109826
109922
  // Will be wrapped below
@@ -109828,20 +109924,23 @@ var init_delegate = __esm({
109828
109924
  // Will be wrapped below
109829
109925
  parentSessionId,
109830
109926
  debug,
109831
- queuedAt: Date.now(),
109832
- timeoutId: null
109927
+ queuedAt,
109928
+ timeoutId: null,
109929
+ reminderId: null
109833
109930
  };
109834
109931
  let settled = false;
109835
109932
  entry.resolve = (value) => {
109836
109933
  if (settled) return;
109837
109934
  settled = true;
109838
109935
  if (entry.timeoutId) clearTimeout(entry.timeoutId);
109936
+ if (entry.reminderId) clearInterval(entry.reminderId);
109839
109937
  resolve8(value);
109840
109938
  };
109841
109939
  entry.reject = (error2) => {
109842
109940
  if (settled) return;
109843
109941
  settled = true;
109844
109942
  if (entry.timeoutId) clearTimeout(entry.timeoutId);
109943
+ if (entry.reminderId) clearInterval(entry.reminderId);
109845
109944
  reject2(error2);
109846
109945
  };
109847
109946
  if (effectiveTimeout > 0) {
@@ -109853,6 +109952,13 @@ var init_delegate = __esm({
109853
109952
  entry.reject(new Error(`Delegation queue timeout: waited ${effectiveTimeout}ms for an available slot`));
109854
109953
  }, effectiveTimeout);
109855
109954
  }
109955
+ entry.reminderId = setInterval(() => {
109956
+ const waitedSeconds = Math.round((Date.now() - queuedAt) / 1e3);
109957
+ console.error(`[DelegationManager] Still waiting for slot (${waitedSeconds}s). ${this.globalActive}/${this.maxConcurrent} active, ${this.waitQueue.length} queued.`);
109958
+ }, 15e3);
109959
+ if (entry.reminderId.unref) {
109960
+ entry.reminderId.unref();
109961
+ }
109856
109962
  this.waitQueue.push(entry);
109857
109963
  });
109858
109964
  }
@@ -109891,18 +109997,14 @@ var init_delegate = __esm({
109891
109997
  const sessionData = this.sessionDelegations.get(parentSessionId);
109892
109998
  const sessionCount = sessionData?.count || 0;
109893
109999
  if (sessionCount >= this.maxPerSession) {
109894
- if (debug) {
109895
- console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
109896
- }
110000
+ console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
109897
110001
  toReject.push({ reject: reject2, error: new Error(`Maximum delegations per session (${this.maxPerSession}) reached for session ${parentSessionId}`) });
109898
110002
  continue;
109899
110003
  }
109900
110004
  }
109901
110005
  this._incrementCounters(parentSessionId);
109902
- if (debug) {
109903
- const waitTime = Date.now() - queuedAt;
109904
- console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
109905
- }
110006
+ const waitTime = Date.now() - queuedAt;
110007
+ console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
109906
110008
  toResolve.push(resolve8);
109907
110009
  }
109908
110010
  if (toResolve.length > 0 || toReject.length > 0) {
@@ -109952,6 +110054,9 @@ var init_delegate = __esm({
109952
110054
  if (entry.timeoutId) {
109953
110055
  clearTimeout(entry.timeoutId);
109954
110056
  }
110057
+ if (entry.reminderId) {
110058
+ clearInterval(entry.reminderId);
110059
+ }
109955
110060
  if (entry.reject) {
109956
110061
  entry.reject(new Error("DelegationManager was cleaned up"));
109957
110062
  }
@@ -110074,8 +110179,9 @@ Instructions:
110074
110179
  promptType: "code-researcher",
110075
110180
  allowedTools: ["extract"],
110076
110181
  maxIterations: 5,
110077
- delegationManager: options.delegationManager
110182
+ delegationManager: options.delegationManager,
110078
110183
  // Per-instance delegation limits
110184
+ parentAbortSignal: options.parentAbortSignal || null
110079
110185
  // timeout removed - inherit default from delegate (300s)
110080
110186
  });
110081
110187
  return { chunk, result };
@@ -110095,16 +110201,12 @@ async function processChunksParallel(chunks, extractionPrompt, maxWorkers, optio
110095
110201
  return result;
110096
110202
  });
110097
110203
  active.add(promise);
110098
- if (options.debug) {
110099
- console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
110100
- }
110204
+ console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
110101
110205
  }
110102
110206
  if (active.size > 0) {
110103
110207
  const result = await Promise.race(active);
110104
110208
  results.push(result);
110105
- if (options.debug) {
110106
- console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
110107
- }
110209
+ console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
110108
110210
  }
110109
110211
  }
110110
110212
  results.sort((a5, b5) => a5.chunk.id - b5.chunk.id);
@@ -110174,8 +110276,9 @@ Organize all findings into clear categories with items listed under each.${compl
110174
110276
  promptType: "code-researcher",
110175
110277
  allowedTools: [],
110176
110278
  maxIterations: 5,
110177
- delegationManager: options.delegationManager
110279
+ delegationManager: options.delegationManager,
110178
110280
  // Per-instance delegation limits
110281
+ parentAbortSignal: options.parentAbortSignal || null
110179
110282
  // timeout removed - inherit default from delegate (300s)
110180
110283
  });
110181
110284
  return result;
@@ -110239,8 +110342,9 @@ CRITICAL: Do NOT guess keywords. Actually run searches and see what returns resu
110239
110342
  promptType: "code-researcher",
110240
110343
  // Full tool access for exploration and experimentation
110241
110344
  maxIterations: 15,
110242
- delegationManager: options.delegationManager
110345
+ delegationManager: options.delegationManager,
110243
110346
  // Per-instance delegation limits
110347
+ parentAbortSignal: options.parentAbortSignal || null
110244
110348
  // timeout removed - inherit default from delegate (300s)
110245
110349
  });
110246
110350
  const plan = parsePlanningResult(stripResultTags(result));
@@ -110297,8 +110401,9 @@ When done, use the attempt_completion tool with your answer as the result.`;
110297
110401
  promptType: "code-researcher",
110298
110402
  allowedTools: [],
110299
110403
  maxIterations: 5,
110300
- delegationManager: options.delegationManager
110404
+ delegationManager: options.delegationManager,
110301
110405
  // Per-instance delegation limits
110406
+ parentAbortSignal: options.parentAbortSignal || null
110302
110407
  // timeout removed - inherit default from delegate (300s)
110303
110408
  });
110304
110409
  return stripResultTags(result);
@@ -110611,11 +110716,41 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
110611
110716
  "- extract: Verify code snippets to ensure targets are actually relevant before including them.",
110612
110717
  "- listFiles: Understand directory structure to find where relevant code might live.",
110613
110718
  "",
110614
- "Strategy for complex queries:",
110719
+ "CRITICAL - How probe search works (do NOT ignore):",
110720
+ "- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
110721
+ '- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
110722
+ '- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
110723
+ "- NEVER repeat the same search query \u2014 you will get the same results.",
110724
+ "- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
110725
+ "- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
110726
+ "- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
110727
+ "",
110728
+ "GOOD search strategy (do this):",
110729
+ ' Query: "How does authentication work and how are sessions managed?"',
110730
+ ' \u2192 search "authentication" \u2192 search "session management" (two different concepts)',
110731
+ ' Query: "Find the IP allowlist middleware"',
110732
+ ' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
110733
+ ' Query: "How does BM25 scoring work with SIMD optimization?"',
110734
+ ' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
110735
+ "",
110736
+ "BAD search strategy (never do this):",
110737
+ ' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
110738
+ ' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
110739
+ ' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
110740
+ "",
110741
+ "Keyword tips:",
110742
+ "- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
110743
+ '- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
110744
+ '- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
110745
+ "- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
110746
+ '- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
110747
+ "",
110748
+ "Strategy:",
110615
110749
  "1. Analyze the query - identify key concepts, entities, and relationships",
110616
- '2. Run focused searches for each independent concept (e.g., for "how do payments work and how are emails sent", search "payments" and "emails" separately since they are unrelated)',
110617
- "3. Use extract to verify relevance of promising results",
110618
- "4. Combine all relevant targets in your final response",
110750
+ "2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
110751
+ "3. If a search returns results, use extract to verify relevance",
110752
+ "4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
110753
+ "5. Combine all relevant targets in your final response",
110619
110754
  "",
110620
110755
  `Query: ${searchQuery}`,
110621
110756
  `Search path(s): ${searchPath}`,
@@ -110668,9 +110803,12 @@ var init_vercel = __esm({
110668
110803
  }
110669
110804
  return result;
110670
110805
  };
110806
+ const previousSearches = /* @__PURE__ */ new Set();
110807
+ const paginationCounts = /* @__PURE__ */ new Map();
110808
+ const MAX_PAGES_PER_QUERY = 3;
110671
110809
  return (0, import_ai5.tool)({
110672
110810
  name: "search",
110673
- description: searchDelegate ? `${searchDescription} (delegates code search to a subagent and returns extracted code blocks)` : searchDescription,
110811
+ description: searchDelegate ? searchDelegateDescription : searchDescription,
110674
110812
  inputSchema: searchSchema,
110675
110813
  execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
110676
110814
  const effectiveMaxTokens = paramMaxTokens || maxTokens;
@@ -110708,6 +110846,26 @@ var init_vercel = __esm({
110708
110846
  return await search(searchOptions);
110709
110847
  };
110710
110848
  if (!searchDelegate) {
110849
+ const searchKey = `${searchQuery}::${searchPath}::${exact || false}`;
110850
+ if (!nextPage) {
110851
+ if (previousSearches.has(searchKey)) {
110852
+ if (debug) {
110853
+ console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" in "${searchPath}"`);
110854
+ }
110855
+ return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query in this path. Do NOT repeat the same search. If you need more results, set nextPage=true with the session ID from the previous search. Otherwise, try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.";
110856
+ }
110857
+ previousSearches.add(searchKey);
110858
+ paginationCounts.set(searchKey, 0);
110859
+ } else {
110860
+ const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
110861
+ paginationCounts.set(searchKey, pageCount);
110862
+ if (pageCount > MAX_PAGES_PER_QUERY) {
110863
+ if (debug) {
110864
+ console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
110865
+ }
110866
+ return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or use attempt_completion to return your findings.`;
110867
+ }
110868
+ }
110711
110869
  try {
110712
110870
  const result = maybeAnnotate(await runRawSearch());
110713
110871
  if (options.fileTracker && typeof result === "string") {
@@ -110746,7 +110904,8 @@ var init_vercel = __esm({
110746
110904
  promptType: "code-searcher",
110747
110905
  allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
110748
110906
  searchDelegate: false,
110749
- schema: CODE_SEARCH_SCHEMA
110907
+ schema: CODE_SEARCH_SCHEMA,
110908
+ parentAbortSignal: options.parentAbortSignal || null
110750
110909
  });
110751
110910
  const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
110752
110911
  "search.query": searchQuery,
@@ -110960,7 +111119,7 @@ var init_vercel = __esm({
110960
111119
  name: "delegate",
110961
111120
  description: delegateDescription,
110962
111121
  inputSchema: delegateSchema,
110963
- execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate }) => {
111122
+ execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate, parentAbortSignal }) => {
110964
111123
  if (!task || typeof task !== "string") {
110965
111124
  throw new Error("Task parameter is required and must be a non-empty string");
110966
111125
  }
@@ -111018,8 +111177,9 @@ var init_vercel = __esm({
111018
111177
  enableMcp,
111019
111178
  mcpConfig,
111020
111179
  mcpConfigPath,
111021
- delegationManager
111180
+ delegationManager,
111022
111181
  // Per-instance delegation limits
111182
+ parentAbortSignal
111023
111183
  });
111024
111184
  return result;
111025
111185
  }
@@ -111057,8 +111217,9 @@ var init_vercel = __esm({
111057
111217
  provider: options.provider,
111058
111218
  model: options.model,
111059
111219
  tracer: options.tracer,
111060
- delegationManager
111220
+ delegationManager,
111061
111221
  // Per-instance delegation limits
111222
+ parentAbortSignal: options.parentAbortSignal || null
111062
111223
  });
111063
111224
  return result;
111064
111225
  } catch (error2) {