@probelabs/probe 0.6.0-rc295 → 0.6.0-rc296

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.
Files changed (31) hide show
  1. package/README.md +7 -0
  2. package/bin/binaries/{probe-v0.6.0-rc295-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc296-aarch64-apple-darwin.tar.gz} +0 -0
  3. package/bin/binaries/{probe-v0.6.0-rc295-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc296-aarch64-unknown-linux-musl.tar.gz} +0 -0
  4. package/bin/binaries/{probe-v0.6.0-rc295-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc296-x86_64-apple-darwin.tar.gz} +0 -0
  5. package/bin/binaries/{probe-v0.6.0-rc295-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc296-x86_64-pc-windows-msvc.zip} +0 -0
  6. package/bin/binaries/{probe-v0.6.0-rc295-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc296-x86_64-unknown-linux-musl.tar.gz} +0 -0
  7. package/build/agent/ProbeAgent.d.ts +8 -2
  8. package/build/agent/ProbeAgent.js +683 -10
  9. package/build/agent/mcp/client.js +81 -4
  10. package/build/agent/mcp/xmlBridge.js +11 -0
  11. package/build/agent/otelLogBridge.js +184 -0
  12. package/build/agent/simpleTelemetry.js +8 -0
  13. package/build/delegate.js +75 -6
  14. package/build/index.js +6 -2
  15. package/build/tools/common.js +84 -11
  16. package/build/tools/vercel.js +78 -18
  17. package/cjs/agent/ProbeAgent.cjs +858 -32
  18. package/cjs/agent/simpleTelemetry.cjs +112 -0
  19. package/cjs/index.cjs +970 -32
  20. package/index.d.ts +26 -0
  21. package/package.json +1 -1
  22. package/src/agent/ProbeAgent.d.ts +8 -2
  23. package/src/agent/ProbeAgent.js +683 -10
  24. package/src/agent/mcp/client.js +81 -4
  25. package/src/agent/mcp/xmlBridge.js +11 -0
  26. package/src/agent/otelLogBridge.js +184 -0
  27. package/src/agent/simpleTelemetry.js +8 -0
  28. package/src/delegate.js +75 -6
  29. package/src/index.js +6 -2
  30. package/src/tools/common.js +84 -11
  31. package/src/tools/vercel.js +78 -18
package/cjs/index.cjs CHANGED
@@ -27051,14 +27051,64 @@ function detectStuckResponse(response) {
27051
27051
  }
27052
27052
  return false;
27053
27053
  }
27054
+ function splitQuotedString(input) {
27055
+ const tokens = [];
27056
+ let current2 = "";
27057
+ let inQuote = null;
27058
+ let i = 0;
27059
+ while (i < input.length) {
27060
+ const ch = input[i];
27061
+ if (inQuote) {
27062
+ if (ch === "\\" && i + 1 < input.length) {
27063
+ current2 += input[i + 1];
27064
+ i += 2;
27065
+ continue;
27066
+ }
27067
+ if (ch === inQuote) {
27068
+ inQuote = null;
27069
+ i++;
27070
+ continue;
27071
+ }
27072
+ current2 += ch;
27073
+ i++;
27074
+ } else {
27075
+ if (ch === '"' || ch === "'") {
27076
+ inQuote = ch;
27077
+ i++;
27078
+ continue;
27079
+ }
27080
+ if (/[\s,]/.test(ch)) {
27081
+ if (current2.length > 0) {
27082
+ tokens.push(current2);
27083
+ current2 = "";
27084
+ }
27085
+ i++;
27086
+ continue;
27087
+ }
27088
+ current2 += ch;
27089
+ i++;
27090
+ }
27091
+ }
27092
+ if (current2.length > 0) {
27093
+ tokens.push(current2);
27094
+ }
27095
+ return tokens;
27096
+ }
27054
27097
  function parseTargets(targets) {
27055
27098
  if (!targets || typeof targets !== "string") {
27056
27099
  return [];
27057
27100
  }
27058
- return targets.split(/[\s,]+/).filter((f) => f.length > 0);
27101
+ return splitQuotedString(targets);
27059
27102
  }
27060
27103
  function parseAndResolvePaths(pathStr, cwd) {
27061
27104
  if (!pathStr) return [];
27105
+ if (/["']/.test(pathStr)) {
27106
+ const paths2 = splitQuotedString(pathStr);
27107
+ return paths2.map((p) => {
27108
+ if ((0, import_path5.isAbsolute)(p)) return p;
27109
+ return cwd ? (0, import_path5.resolve)(cwd, p) : p;
27110
+ });
27111
+ }
27062
27112
  let paths = pathStr.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
27063
27113
  paths = paths.flatMap((p) => {
27064
27114
  if (!/\s/.test(p)) return [p];
@@ -27068,9 +27118,7 @@ function parseAndResolvePaths(pathStr, cwd) {
27068
27118
  return allLookLikePaths ? parts : [p];
27069
27119
  });
27070
27120
  return paths.map((p) => {
27071
- if ((0, import_path5.isAbsolute)(p)) {
27072
- return p;
27073
- }
27121
+ if ((0, import_path5.isAbsolute)(p)) return p;
27074
27122
  return cwd ? (0, import_path5.resolve)(cwd, p) : p;
27075
27123
  });
27076
27124
  }
@@ -27103,7 +27151,7 @@ var init_common = __esm({
27103
27151
  searchSchema = external_exports2.object({
27104
27152
  query: external_exports2.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."),
27105
27153
  path: external_exports2.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
27106
- exact: external_exports2.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.'),
27154
+ exact: external_exports2.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 OR when searching for strings with punctuation/quotes/empty values (e.g. 'description: ""' \u2014 BM25 strips punctuation so exact=true is required for literal matching). Use true when you know the exact symbol name or need literal string matching.`),
27107
27155
  maxTokens: external_exports2.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
27108
27156
  session: external_exports2.string().optional().describe("Session ID for result caching and pagination. Pass the session ID from a previous search to get additional results (next page). Results already shown in a session are automatically excluded. Omit for a fresh search."),
27109
27157
  nextPage: external_exports2.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.")
@@ -73542,6 +73590,9 @@ function isMethodAllowed(methodName, allowedMethods, blockedMethods) {
73542
73590
  }
73543
73591
  function createTransport(serverConfig) {
73544
73592
  const { transport, command, args, url: url2, env } = serverConfig;
73593
+ if (serverConfig.transportInstance) {
73594
+ return serverConfig.transportInstance;
73595
+ }
73545
73596
  switch (transport) {
73546
73597
  case "stdio":
73547
73598
  return new import_stdio.StdioClientTransport({
@@ -73904,6 +73955,53 @@ var init_client = __esm({
73904
73955
  throw error40;
73905
73956
  }
73906
73957
  }
73958
+ /**
73959
+ * Call graceful_stop on all MCP servers that expose it.
73960
+ * This signals agent-type MCP servers to wrap up their work.
73961
+ * @returns {Promise<Array<{server: string, success: boolean, error?: string}>>}
73962
+ */
73963
+ async callGracefulStopAll() {
73964
+ const results = [];
73965
+ for (const [serverName, clientInfo] of this.clients) {
73966
+ const qualifiedName = `${serverName}_graceful_stop`;
73967
+ if (this.tools.has(qualifiedName)) {
73968
+ if (this.debug) {
73969
+ console.log(`[DEBUG] MCP callGracefulStopAll: calling graceful_stop on server "${serverName}"`);
73970
+ }
73971
+ try {
73972
+ const timeoutMs = 5e3;
73973
+ const timeoutPromise = new Promise(
73974
+ (_, reject2) => setTimeout(() => reject2(new Error("graceful_stop timeout")), timeoutMs)
73975
+ );
73976
+ await Promise.race([
73977
+ clientInfo.client.callTool({ name: "graceful_stop", arguments: {} }, void 0, { timeout: timeoutMs }),
73978
+ timeoutPromise
73979
+ ]);
73980
+ results.push({ server: serverName, success: true });
73981
+ if (this.debug) {
73982
+ console.log(`[DEBUG] MCP callGracefulStopAll: server "${serverName}" acknowledged graceful_stop`);
73983
+ }
73984
+ } catch (e) {
73985
+ results.push({ server: serverName, success: false, error: e.message });
73986
+ if (this.debug) {
73987
+ console.log(`[DEBUG] MCP callGracefulStopAll: server "${serverName}" graceful_stop failed: ${e.message}`);
73988
+ }
73989
+ }
73990
+ }
73991
+ }
73992
+ if (this.debug) {
73993
+ const withStop = results.length;
73994
+ const total = this.clients.size;
73995
+ console.log(`[DEBUG] MCP callGracefulStopAll: ${withStop}/${total} servers had graceful_stop tool`);
73996
+ }
73997
+ this.recordMcpEvent("graceful_stop.sweep_completed", {
73998
+ servers_total: this.clients.size,
73999
+ servers_with_graceful_stop: results.length,
74000
+ servers_acknowledged: results.filter((r) => r.success).length,
74001
+ servers_failed: results.filter((r) => !r.success).length
74002
+ });
74003
+ return results;
74004
+ }
73907
74005
  /**
73908
74006
  * Get all available tools with their schemas
73909
74007
  * @returns {Object} Map of tool name to tool definition
@@ -73931,10 +74029,29 @@ var init_client = __esm({
73931
74029
  inputSchema: tool6.inputSchema,
73932
74030
  execute: async (args) => {
73933
74031
  const result = await this.callTool(name15, args);
73934
- if (result.content && result.content[0]) {
73935
- return result.content[0].text;
74032
+ if (!result.content || !result.content[0]) {
74033
+ return JSON.stringify(result);
73936
74034
  }
73937
- return JSON.stringify(result);
74035
+ const hasImage = result.content.some((block) => block.type === "image");
74036
+ if (hasImage) {
74037
+ return { _mcpContent: result.content };
74038
+ }
74039
+ return result.content[0].text;
74040
+ },
74041
+ // Convert MCP content blocks (including images) to Vercel AI SDK format
74042
+ toModelOutput: ({ output }) => {
74043
+ if (output && typeof output === "object" && output._mcpContent) {
74044
+ const parts = [];
74045
+ for (const block of output._mcpContent) {
74046
+ if (block.type === "text") {
74047
+ parts.push({ type: "text", text: block.text });
74048
+ } else if (block.type === "image") {
74049
+ parts.push({ type: "image-data", data: block.data, mediaType: block.mimeType });
74050
+ }
74051
+ }
74052
+ return { type: "content", value: parts };
74053
+ }
74054
+ return { type: "text", value: typeof output === "string" ? output : JSON.stringify(output) };
73938
74055
  }
73939
74056
  };
73940
74057
  }
@@ -74114,6 +74231,16 @@ var init_xmlBridge = __esm({
74114
74231
  isMcpTool(toolName) {
74115
74232
  return toolName in this.mcpTools;
74116
74233
  }
74234
+ /**
74235
+ * Call graceful_stop on all MCP servers that expose it.
74236
+ * @returns {Promise<Array>}
74237
+ */
74238
+ async callGracefulStopAll() {
74239
+ if (this.mcpManager) {
74240
+ return this.mcpManager.callGracefulStopAll();
74241
+ }
74242
+ return [];
74243
+ }
74117
74244
  /**
74118
74245
  * Clean up MCP connections
74119
74246
  */
@@ -96673,6 +96800,7 @@ var init_ProbeAgent = __esm({
96673
96800
  this.debug = options.debug || process.env.DEBUG === "1";
96674
96801
  this.cancelled = false;
96675
96802
  this._abortController = new AbortController();
96803
+ this._activeSubagents = /* @__PURE__ */ new Map();
96676
96804
  this.tracer = options.tracer || null;
96677
96805
  this.outline = !!options.outline;
96678
96806
  this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
@@ -96784,14 +96912,31 @@ var init_ProbeAgent = __esm({
96784
96912
  this.timeoutBehavior = options.timeoutBehavior ?? (() => {
96785
96913
  const val = process.env.TIMEOUT_BEHAVIOR;
96786
96914
  if (val === "hard") return "hard";
96915
+ if (val === "negotiated") return "negotiated";
96787
96916
  return "graceful";
96788
96917
  })();
96789
96918
  this.gracefulTimeoutBonusSteps = options.gracefulTimeoutBonusSteps ?? (() => {
96790
96919
  const parsed = parseInt(process.env.GRACEFUL_TIMEOUT_BONUS_STEPS, 10);
96791
96920
  return isNaN(parsed) || parsed < 1 || parsed > 20 ? 4 : parsed;
96792
96921
  })();
96922
+ this.negotiatedTimeoutBudget = options.negotiatedTimeoutBudget ?? (() => {
96923
+ const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_BUDGET, 10);
96924
+ return isNaN(parsed) || parsed < 6e4 || parsed > 72e5 ? 18e5 : parsed;
96925
+ })();
96926
+ this.negotiatedTimeoutMaxRequests = options.negotiatedTimeoutMaxRequests ?? (() => {
96927
+ const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_MAX_REQUESTS, 10);
96928
+ return isNaN(parsed) || parsed < 1 || parsed > 10 ? 3 : parsed;
96929
+ })();
96930
+ this.negotiatedTimeoutMaxPerRequest = options.negotiatedTimeoutMaxPerRequest ?? (() => {
96931
+ const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_MAX_PER_REQUEST, 10);
96932
+ return isNaN(parsed) || parsed < 6e4 || parsed > 36e5 ? 6e5 : parsed;
96933
+ })();
96934
+ this.gracefulStopDeadline = options.gracefulStopDeadline ?? (() => {
96935
+ const parsed = parseInt(process.env.GRACEFUL_STOP_DEADLINE, 10);
96936
+ return isNaN(parsed) || parsed < 5e3 || parsed > 3e5 ? 45e3 : parsed;
96937
+ })();
96793
96938
  if (this.debug) {
96794
- console.log(`[DEBUG] Timeout behavior: ${this.timeoutBehavior}, bonus steps: ${this.gracefulTimeoutBonusSteps}`);
96939
+ console.log(`[DEBUG] Timeout behavior: ${this.timeoutBehavior}, bonus steps: ${this.gracefulTimeoutBonusSteps}, graceful stop deadline: ${this.gracefulStopDeadline}ms`);
96795
96940
  }
96796
96941
  this.retryConfig = options.retry || {};
96797
96942
  this.retryManager = null;
@@ -97143,6 +97288,18 @@ var init_ProbeAgent = __esm({
97143
97288
  // Per-instance delegation limits
97144
97289
  parentAbortSignal: this._abortController.signal,
97145
97290
  // Propagate cancellation to delegations
97291
+ // Timeout settings for delegate subagents to inherit
97292
+ timeoutBehavior: this.timeoutBehavior,
97293
+ maxOperationTimeout: this.maxOperationTimeout,
97294
+ requestTimeout: this.requestTimeout,
97295
+ gracefulTimeoutBonusSteps: this.gracefulTimeoutBonusSteps,
97296
+ negotiatedTimeoutBudget: this.negotiatedTimeoutBudget,
97297
+ negotiatedTimeoutMaxRequests: this.negotiatedTimeoutMaxRequests,
97298
+ negotiatedTimeoutMaxPerRequest: this.negotiatedTimeoutMaxPerRequest,
97299
+ parentOperationStartTime: this._operationStartTime,
97300
+ // For remaining budget calculation
97301
+ onSubagentCreated: (sid, subagent) => this._registerSubagent(sid, subagent),
97302
+ onSubagentCompleted: (sid) => this._unregisterSubagent(sid),
97146
97303
  outputBuffer: this._outputBuffer,
97147
97304
  concurrencyLimiter: this.concurrencyLimiter,
97148
97305
  // Global AI concurrency limiter
@@ -97727,7 +97884,7 @@ var init_ProbeAgent = __esm({
97727
97884
  }
97728
97885
  if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
97729
97886
  const gts = this._gracefulTimeoutState;
97730
- if (this.timeoutBehavior === "graceful" && gts) {
97887
+ if ((this.timeoutBehavior === "graceful" || this.timeoutBehavior === "negotiated") && gts) {
97731
97888
  } else {
97732
97889
  timeoutState.timeoutId = setTimeout(() => {
97733
97890
  controller.abort();
@@ -99114,6 +99271,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
99114
99271
  } else {
99115
99272
  options = schemaOrOptions || {};
99116
99273
  }
99274
+ this._operationStartTime = Date.now();
99117
99275
  try {
99118
99276
  const oldHistoryLength = this.history.length;
99119
99277
  if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
@@ -99194,7 +99352,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
99194
99352
  }
99195
99353
  }
99196
99354
  let currentIteration = 0;
99197
- let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
99355
+ let finalResult = null;
99356
+ const DEFAULT_MAX_ITER_MSG = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
99357
+ const _toolCallLog = [];
99358
+ let abortSummaryTaken = false;
99198
99359
  const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
99199
99360
  const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
99200
99361
  const isClaudeCode = this.clientApiProvider === "claude-code" || process.env.USE_CLAUDE_CODE === "true";
@@ -99334,6 +99495,220 @@ You are working with a workspace. Available paths: ${workspaceDesc}
99334
99495
  bonusStepsMax: this.gracefulTimeoutBonusSteps
99335
99496
  };
99336
99497
  this._gracefulTimeoutState = gracefulTimeoutState;
99498
+ const negotiatedTimeoutState = {
99499
+ extensionsUsed: 0,
99500
+ totalExtraTimeMs: 0,
99501
+ softTimeoutId: null,
99502
+ hardAbortTimeoutId: null,
99503
+ maxRequests: this.negotiatedTimeoutMaxRequests,
99504
+ maxPerRequestMs: this.negotiatedTimeoutMaxPerRequest,
99505
+ budgetMs: this.negotiatedTimeoutBudget,
99506
+ observerRunning: false,
99507
+ // true while observer LLM call is in flight
99508
+ extensionMessage: null,
99509
+ // message to show in prepareStep after extension granted
99510
+ startTime: Date.now()
99511
+ };
99512
+ this._negotiatedTimeoutState = negotiatedTimeoutState;
99513
+ const activeTools = /* @__PURE__ */ new Map();
99514
+ this._activeTools = activeTools;
99515
+ const onToolCall = (event) => {
99516
+ const key = event.toolCallId || `${event.name}:${JSON.stringify(event.args || {}).slice(0, 100)}`;
99517
+ if (event.status === "started") {
99518
+ activeTools.set(key, {
99519
+ name: event.name,
99520
+ args: event.args,
99521
+ startedAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString()
99522
+ });
99523
+ } else if (event.status === "completed" || event.status === "error") {
99524
+ activeTools.delete(key);
99525
+ }
99526
+ };
99527
+ this.events.on("toolCall", onToolCall);
99528
+ const runTimeoutObserver = async () => {
99529
+ if (negotiatedTimeoutState.observerRunning) return;
99530
+ negotiatedTimeoutState.observerRunning = true;
99531
+ const remainingRequests = negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed;
99532
+ const remainingBudgetMs = negotiatedTimeoutState.budgetMs - negotiatedTimeoutState.totalExtraTimeMs;
99533
+ const maxPerReqMin = Math.round(negotiatedTimeoutState.maxPerRequestMs / 6e4);
99534
+ const elapsedMin = Math.round((Date.now() - negotiatedTimeoutState.startTime) / 6e4);
99535
+ if (remainingRequests <= 0 || remainingBudgetMs <= 0) {
99536
+ if (this.debug) {
99537
+ console.log(`[DEBUG] Timeout observer: no extensions/budget remaining \u2014 aborting in-flight tools and triggering graceful wind-down`);
99538
+ }
99539
+ if (this.tracer) {
99540
+ this.tracer.addEvent("negotiated_timeout.observer_exhausted", {
99541
+ extensions_used: negotiatedTimeoutState.extensionsUsed,
99542
+ max_requests: negotiatedTimeoutState.maxRequests,
99543
+ total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
99544
+ budget_ms: negotiatedTimeoutState.budgetMs,
99545
+ elapsed_min: elapsedMin,
99546
+ active_tools: Array.from(activeTools.values()).map((t) => t.name)
99547
+ });
99548
+ }
99549
+ await this._initiateGracefulStop(gracefulTimeoutState, "budget/extensions exhausted");
99550
+ negotiatedTimeoutState.observerRunning = false;
99551
+ return;
99552
+ }
99553
+ const activeToolsList = Array.from(activeTools.values());
99554
+ const now = Date.now();
99555
+ const formatDuration = (ms) => {
99556
+ const totalSec = Math.round(ms / 1e3);
99557
+ if (totalSec < 60) return `${totalSec}s`;
99558
+ const min = Math.floor(totalSec / 60);
99559
+ const sec = totalSec % 60;
99560
+ if (min < 60) return `${min}m ${sec}s`;
99561
+ const hr = Math.floor(min / 60);
99562
+ const remainMin = min % 60;
99563
+ return `${hr}h ${remainMin}m`;
99564
+ };
99565
+ const activeToolsDesc = activeToolsList.length > 0 ? activeToolsList.map((t) => {
99566
+ const runningForMs = now - new Date(t.startedAt).getTime();
99567
+ return `- ${t.name}(${JSON.stringify(t.args || {}).slice(0, 200)}) \u2014 running for ${formatDuration(runningForMs)}`;
99568
+ }).join("\n") : "(none currently running)";
99569
+ const recentHistory = this.history.slice(-6).map((msg) => {
99570
+ const content = typeof msg.content === "string" ? msg.content.slice(0, 300) : JSON.stringify(msg.content).slice(0, 300);
99571
+ return `[${msg.role}]: ${content}`;
99572
+ }).join("\n");
99573
+ const observerPrompt = `You are a timeout observer for an AI coding agent. The agent has been working for ${elapsedMin} minute(s) and has reached its time limit.
99574
+
99575
+ ## Recent Conversation
99576
+ ${recentHistory || "(no history yet)"}
99577
+
99578
+ ## Currently Running Tools
99579
+ ${activeToolsDesc}
99580
+
99581
+ ## Budget
99582
+ - Extensions used: ${negotiatedTimeoutState.extensionsUsed}/${negotiatedTimeoutState.maxRequests}
99583
+ - Time budget remaining: ${Math.round(remainingBudgetMs / 6e4)} minutes
99584
+ - Max per extension: ${maxPerReqMin} minutes
99585
+
99586
+ Decide whether the agent should get more time. EXTEND if:
99587
+ - Tools are actively running (especially delegates or complex analysis) \u2014 they need time to finish
99588
+ - The agent is making clear progress on a complex task
99589
+ - New information is being gathered that will improve the final answer
99590
+
99591
+ DO NOT EXTEND if:
99592
+ - The agent appears stuck in a loop (repeating the same tool calls or getting the same errors)
99593
+ - The conversation shows the agent retrying failed operations without changing approach
99594
+ - The agent has enough information to answer but keeps searching for more
99595
+ - Tool calls are returning empty or error results repeatedly
99596
+ - The agent is doing redundant work (searching for things it already found)
99597
+
99598
+ A stuck agent will not recover with more time \u2014 it will just burn the budget. Better to force it to answer with what it has.
99599
+
99600
+ Respond with ONLY valid JSON (no markdown, no explanation):
99601
+ {"extend": true, "minutes": <1-${maxPerReqMin}>, "reason": "your reason here"}
99602
+ or
99603
+ {"extend": false, "reason": "your reason here"}`;
99604
+ const observerFn = async () => {
99605
+ const modelInstance = this.provider ? this.provider(this.model) : this.model;
99606
+ if (this.debug) {
99607
+ console.log(`[DEBUG] Timeout observer: making LLM call (${activeToolsList.length} active tools, ${elapsedMin} min elapsed)`);
99608
+ }
99609
+ if (this.tracer) {
99610
+ this.tracer.addEvent("negotiated_timeout.observer_invoked", {
99611
+ elapsed_min: elapsedMin,
99612
+ active_tools: activeToolsList.map((t) => t.name),
99613
+ active_tools_detail: activeToolsList.map((t) => ({
99614
+ name: t.name,
99615
+ running_for_ms: now - new Date(t.startedAt).getTime(),
99616
+ args_preview: JSON.stringify(t.args || {}).slice(0, 100)
99617
+ })),
99618
+ active_tools_count: activeToolsList.length,
99619
+ extensions_used: negotiatedTimeoutState.extensionsUsed,
99620
+ remaining_requests: remainingRequests,
99621
+ remaining_budget_ms: remainingBudgetMs,
99622
+ history_length: this.history.length
99623
+ });
99624
+ }
99625
+ const observerResult = await (0, import_ai4.generateText)({
99626
+ model: modelInstance,
99627
+ messages: [{ role: "user", content: observerPrompt }],
99628
+ maxTokens: 500
99629
+ });
99630
+ const responseText = observerResult.text.trim();
99631
+ if (this.tracer) {
99632
+ this.tracer.addEvent("negotiated_timeout.observer_response", {
99633
+ response_text: responseText,
99634
+ usage_prompt_tokens: observerResult.usage?.promptTokens,
99635
+ usage_completion_tokens: observerResult.usage?.completionTokens
99636
+ });
99637
+ }
99638
+ const jsonStr = responseText.replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
99639
+ const decision = JSON.parse(jsonStr);
99640
+ if (decision.extend && decision.minutes > 0) {
99641
+ const requestedMs = Math.min(decision.minutes, maxPerReqMin) * 6e4;
99642
+ const grantedMs = Math.min(requestedMs, remainingBudgetMs, negotiatedTimeoutState.maxPerRequestMs);
99643
+ const grantedMin = Math.round(grantedMs / 6e4 * 10) / 10;
99644
+ negotiatedTimeoutState.extensionsUsed++;
99645
+ negotiatedTimeoutState.totalExtraTimeMs += grantedMs;
99646
+ negotiatedTimeoutState.extensionMessage = `\u23F0 Time limit was reached. The timeout observer granted ${grantedMin} more minute(s) (reason: ${decision.reason || "work in progress"}). Extensions remaining: ${negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed}. Continue your work efficiently.`;
99647
+ negotiatedTimeoutState.softTimeoutId = setTimeout(() => {
99648
+ runTimeoutObserver();
99649
+ }, grantedMs);
99650
+ if (this.debug) {
99651
+ console.log(`[DEBUG] Timeout observer: granted ${grantedMin} min (reason: ${decision.reason}). Extensions: ${negotiatedTimeoutState.extensionsUsed}/${negotiatedTimeoutState.maxRequests}`);
99652
+ }
99653
+ if (this.tracer) {
99654
+ this.tracer.addEvent("negotiated_timeout.observer_extended", {
99655
+ decision_reason: decision.reason,
99656
+ requested_minutes: decision.minutes,
99657
+ granted_ms: grantedMs,
99658
+ granted_min: grantedMin,
99659
+ extensions_used: negotiatedTimeoutState.extensionsUsed,
99660
+ max_requests: negotiatedTimeoutState.maxRequests,
99661
+ total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
99662
+ budget_remaining_ms: remainingBudgetMs - grantedMs,
99663
+ active_tools: activeToolsList.map((t) => t.name),
99664
+ active_tools_count: activeToolsList.length
99665
+ });
99666
+ }
99667
+ } else {
99668
+ if (this.debug) {
99669
+ console.log(`[DEBUG] Timeout observer: declined extension (reason: ${decision.reason}). Initiating graceful stop.`);
99670
+ }
99671
+ if (this.tracer) {
99672
+ this.tracer.addEvent("negotiated_timeout.observer_declined", {
99673
+ decision_reason: decision.reason,
99674
+ extensions_used: negotiatedTimeoutState.extensionsUsed,
99675
+ total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
99676
+ elapsed_min: elapsedMin,
99677
+ active_tools: activeToolsList.map((t) => t.name)
99678
+ });
99679
+ }
99680
+ await this._initiateGracefulStop(gracefulTimeoutState, `observer declined: ${decision.reason}`);
99681
+ }
99682
+ };
99683
+ try {
99684
+ if (this.tracer) {
99685
+ await this.tracer.withSpan("negotiated_timeout.observer", observerFn, {
99686
+ "timeout.elapsed_min": elapsedMin,
99687
+ "timeout.extensions_used": negotiatedTimeoutState.extensionsUsed,
99688
+ "timeout.active_tools_count": activeToolsList.length,
99689
+ "timeout.remaining_budget_ms": remainingBudgetMs
99690
+ });
99691
+ } else {
99692
+ await observerFn();
99693
+ }
99694
+ } catch (err) {
99695
+ if (this.debug) {
99696
+ console.log(`[DEBUG] Timeout observer: LLM call failed (${err.message}). Initiating graceful stop.`);
99697
+ }
99698
+ if (this.tracer) {
99699
+ this.tracer.addEvent("negotiated_timeout.observer_error", {
99700
+ error_message: err.message,
99701
+ error_name: err.name,
99702
+ extensions_used: negotiatedTimeoutState.extensionsUsed,
99703
+ elapsed_min: elapsedMin
99704
+ });
99705
+ }
99706
+ await this._initiateGracefulStop(gracefulTimeoutState, `observer error: ${err.message}`);
99707
+ } finally {
99708
+ negotiatedTimeoutState.observerRunning = false;
99709
+ }
99710
+ };
99711
+ negotiatedTimeoutState.runObserver = runTimeoutObserver;
99337
99712
  let compactionAttempted = false;
99338
99713
  while (true) {
99339
99714
  try {
@@ -99395,6 +99770,14 @@ You are working with a workspace. Available paths: ${workspaceDesc}
99395
99770
  return false;
99396
99771
  },
99397
99772
  prepareStep: ({ steps, stepNumber }) => {
99773
+ if (negotiatedTimeoutState.extensionMessage && !gracefulTimeoutState.triggered) {
99774
+ const msg = negotiatedTimeoutState.extensionMessage;
99775
+ negotiatedTimeoutState.extensionMessage = null;
99776
+ if (this.debug) {
99777
+ console.log(`[DEBUG] prepareStep: delivering timeout observer extension message`);
99778
+ }
99779
+ return { userMessage: msg };
99780
+ }
99398
99781
  if (gracefulTimeoutState.triggered) {
99399
99782
  gracefulTimeoutState.bonusStepsUsed++;
99400
99783
  const remaining = gracefulTimeoutState.bonusStepsMax - gracefulTimeoutState.bonusStepsUsed;
@@ -99420,8 +99803,12 @@ You are working with a workspace. Available paths: ${workspaceDesc}
99420
99803
  return { toolChoice: "none" };
99421
99804
  }
99422
99805
  if (stepNumber === maxIterations - 1) {
99806
+ const searchesTried = _toolCallLog.filter((tc) => tc.name === "search").map((tc) => `"${tc.args.query || ""}"${tc.args.exact ? " (exact)" : ""}`).filter((v, i, a) => a.indexOf(v) === i);
99807
+ const searchSummary = searchesTried.length > 0 ? `
99808
+ Searches attempted: ${searchesTried.join(", ")}` : "";
99423
99809
  return {
99424
- toolChoice: "none"
99810
+ toolChoice: "none",
99811
+ userMessage: `\u26A0\uFE0F LAST ITERATION \u2014 you are out of tool calls. Provide your BEST answer NOW with the information gathered so far. If you could not find what was requested, explain exactly what you searched for and why it did not work, so the caller can try a different approach.${searchSummary}`
99425
99812
  };
99426
99813
  }
99427
99814
  if (steps.length >= 2) {
@@ -99500,6 +99887,11 @@ Double-check your response based on the criteria above. If everything looks good
99500
99887
  const { toolResults, toolCalls, text, reasoningText, finishReason, usage } = stepResult;
99501
99888
  currentIteration++;
99502
99889
  toolContext.currentIteration = currentIteration;
99890
+ if (toolCalls?.length > 0) {
99891
+ for (const tc of toolCalls) {
99892
+ _toolCallLog.push({ name: tc.toolName, args: tc.args || {} });
99893
+ }
99894
+ }
99503
99895
  if (this.tracer) {
99504
99896
  const stepEvent = {
99505
99897
  "iteration": currentIteration,
@@ -99591,6 +99983,14 @@ Double-check your response based on the criteria above. If everything looks good
99591
99983
  }, 6e4);
99592
99984
  }, this.maxOperationTimeout);
99593
99985
  }
99986
+ if (this.timeoutBehavior === "negotiated" && this.maxOperationTimeout > 0) {
99987
+ negotiatedTimeoutState.softTimeoutId = setTimeout(() => {
99988
+ if (this.debug) {
99989
+ console.log(`[DEBUG] Soft timeout after ${this.maxOperationTimeout}ms \u2014 invoking timeout observer`);
99990
+ }
99991
+ runTimeoutObserver();
99992
+ }, this.maxOperationTimeout);
99993
+ }
99594
99994
  try {
99595
99995
  const steps = await result.steps;
99596
99996
  let finalText;
@@ -99611,6 +100011,12 @@ Double-check your response based on the criteria above. If everything looks good
99611
100011
  } finally {
99612
100012
  if (gracefulTimeoutId) clearTimeout(gracefulTimeoutId);
99613
100013
  if (hardAbortTimeoutId) clearTimeout(hardAbortTimeoutId);
100014
+ if (negotiatedTimeoutState.softTimeoutId) clearTimeout(negotiatedTimeoutState.softTimeoutId);
100015
+ if (this._gracefulStopHardAbortId) {
100016
+ clearTimeout(this._gracefulStopHardAbortId);
100017
+ this._gracefulStopHardAbortId = null;
100018
+ }
100019
+ this.events.removeListener("toolCall", onToolCall);
99614
100020
  }
99615
100021
  };
99616
100022
  let aiResult;
@@ -99650,7 +100056,7 @@ Double-check your response based on the criteria above. If everything looks good
99650
100056
  }
99651
100057
  if (gracefulTimeoutState.triggered) {
99652
100058
  const timeoutNotice = "**Note: This response was generated under a time constraint. The research may be incomplete, and some planned searches or analysis steps were not completed.**\n\n";
99653
- if (!finalResult || finalResult === "I was unable to complete your request due to reaching the maximum number of tool iterations.") {
100059
+ if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG || finalResult.startsWith("I was unable to complete your request after")) {
99654
100060
  try {
99655
100061
  const allText = await aiResult.result.text;
99656
100062
  if (allText && allText.trim()) {
@@ -99698,7 +100104,7 @@ ${toolSummaries.join("\n\n---\n\n")}`;
99698
100104
  currentMessages.push(msg);
99699
100105
  }
99700
100106
  }
99701
- if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && finalResult) {
100107
+ if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && !abortSummaryTaken && finalResult) {
99702
100108
  completionPromptInjected = true;
99703
100109
  preCompletionResult = finalResult;
99704
100110
  if (this.debug) {
@@ -99770,6 +100176,118 @@ Double-check your response based on the criteria above. If everything looks good
99770
100176
  }
99771
100177
  break;
99772
100178
  } catch (error40) {
100179
+ if (gracefulTimeoutState.triggered && error40?.name === "AbortError") {
100180
+ if (this.debug) {
100181
+ console.log(`[DEBUG] Negotiated timeout: abort caught \u2014 making summary LLM call with conversation context`);
100182
+ }
100183
+ if (this.tracer) {
100184
+ this.tracer.addEvent("negotiated_timeout.abort_summary_started", {
100185
+ conversation_messages: currentMessages.length,
100186
+ has_schema: !!options.schema,
100187
+ has_tasks: !!(this.enableTasks && this.taskManager)
100188
+ });
100189
+ }
100190
+ try {
100191
+ let taskContext = "";
100192
+ if (this.enableTasks && this.taskManager) {
100193
+ const taskSummary = this.taskManager.getTaskSummary?.();
100194
+ if (taskSummary) {
100195
+ taskContext = `
100196
+
100197
+ ## Task Status
100198
+ ${taskSummary}
100199
+
100200
+ Acknowledge which tasks were completed and which were not.`;
100201
+ }
100202
+ }
100203
+ let schemaContext = "";
100204
+ if (options.schema) {
100205
+ try {
100206
+ const parsedSchema = typeof options.schema === "string" ? JSON.parse(options.schema) : options.schema;
100207
+ schemaContext = `
100208
+
100209
+ IMPORTANT: Your response MUST be valid JSON matching this schema:
100210
+ ${JSON.stringify(parsedSchema, null, 2)}
100211
+
100212
+ Respond with ONLY valid JSON \u2014 no markdown, no explanation, no text outside the JSON object. Include all findings and partial results within the JSON structure. If fields cannot be fully populated due to the interruption, use partial data or null values as appropriate.`;
100213
+ } catch {
100214
+ }
100215
+ }
100216
+ const summaryPrompt = `Your operation was interrupted by a timeout observer because the time limit was reached. Some of your tool calls were cancelled mid-execution.
100217
+
100218
+ Please provide a DETAILED summary of:
100219
+ 1. What you were asked to do (the original task)
100220
+ 2. What you accomplished \u2014 include ALL findings, code snippets, data, and conclusions you gathered
100221
+ 3. What was still in progress or not yet started
100222
+ 4. Any partial results or recommendations you can offer based on what you found so far${taskContext}${schemaContext}
100223
+
100224
+ Be thorough \u2014 this is the user's only response. Include all useful information you collected.`;
100225
+ const summaryMessages = [
100226
+ ...currentMessages,
100227
+ { role: "user", content: summaryPrompt }
100228
+ ];
100229
+ const modelInstance = this.provider ? this.provider(this.model) : this.model;
100230
+ const summaryFn = async () => {
100231
+ const summaryResult = await (0, import_ai4.generateText)({
100232
+ model: modelInstance,
100233
+ messages: this.prepareMessagesWithImages(summaryMessages),
100234
+ maxTokens: 4e3
100235
+ });
100236
+ if (this.tracer) {
100237
+ this.tracer.addEvent("negotiated_timeout.abort_summary_completed", {
100238
+ summary_length: summaryResult.text?.length || 0,
100239
+ usage_prompt_tokens: summaryResult.usage?.promptTokens,
100240
+ usage_completion_tokens: summaryResult.usage?.completionTokens
100241
+ });
100242
+ }
100243
+ if (summaryResult.usage) {
100244
+ this.tokenCounter.recordUsage(summaryResult.usage);
100245
+ }
100246
+ return summaryResult.text;
100247
+ };
100248
+ let summaryText;
100249
+ if (this.tracer) {
100250
+ summaryText = await this.tracer.withSpan("negotiated_timeout.abort_summary", summaryFn, {
100251
+ "summary.conversation_messages": currentMessages.length
100252
+ });
100253
+ } else {
100254
+ summaryText = await summaryFn();
100255
+ }
100256
+ if (options.schema) {
100257
+ finalResult = summaryText || "{}";
100258
+ } else {
100259
+ const timeoutNotice = "**Note: This response was generated under a time constraint. The timeout observer interrupted the operation because the time budget was exhausted.**\n\n";
100260
+ finalResult = timeoutNotice + (summaryText || "The operation was interrupted before a response could be generated.");
100261
+ }
100262
+ if (options.onStream && finalResult) {
100263
+ options.onStream(finalResult);
100264
+ }
100265
+ if (this.debug) {
100266
+ console.log(`[DEBUG] Negotiated timeout: summary produced ${summaryText?.length || 0} chars`);
100267
+ }
100268
+ } catch (summaryErr) {
100269
+ if (this.debug) {
100270
+ console.log(`[DEBUG] Negotiated timeout: summary call failed (${summaryErr.message}), falling back to partial text`);
100271
+ }
100272
+ if (this.tracer) {
100273
+ this.tracer.addEvent("negotiated_timeout.abort_summary_error", {
100274
+ error_message: summaryErr.message
100275
+ });
100276
+ }
100277
+ const partialTexts = currentMessages.filter((m) => m.role === "assistant" && typeof m.content === "string" && m.content.trim()).map((m) => m.content);
100278
+ if (options.schema) {
100279
+ finalResult = partialTexts.length > 0 ? partialTexts[partialTexts.length - 1] : "{}";
100280
+ } else {
100281
+ const timeoutNotice = "**Note: This response was generated under a time constraint. The operation was interrupted and some work was not completed.**\n\n";
100282
+ finalResult = partialTexts.length > 0 ? timeoutNotice + partialTexts[partialTexts.length - 1] : timeoutNotice + "The operation was interrupted before enough information could be gathered. Please try again with a simpler query or increase the timeout.";
100283
+ }
100284
+ if (options.onStream && finalResult) {
100285
+ options.onStream(finalResult);
100286
+ }
100287
+ }
100288
+ abortSummaryTaken = true;
100289
+ break;
100290
+ }
99773
100291
  if (!compactionAttempted && handleContextLimitError) {
99774
100292
  const compactionResult = handleContextLimitError(error40, currentMessages, {
99775
100293
  keepLastSegment: true,
@@ -99804,6 +100322,36 @@ Double-check your response based on the criteria above. If everything looks good
99804
100322
  }
99805
100323
  if (currentIteration >= maxIterations) {
99806
100324
  console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}.`);
100325
+ if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG) {
100326
+ try {
100327
+ const searchQueries = [];
100328
+ const toolCounts = {};
100329
+ for (const tc of _toolCallLog) {
100330
+ toolCounts[tc.name] = (toolCounts[tc.name] || 0) + 1;
100331
+ if (tc.name === "search") {
100332
+ const q = tc.args.query || "";
100333
+ const exact = tc.args.exact ? " (exact)" : "";
100334
+ searchQueries.push(`"${q}"${exact}`);
100335
+ }
100336
+ }
100337
+ const toolBreakdown = Object.entries(toolCounts).map(([name15, count]) => `${name15}: ${count}x`).join(", ");
100338
+ const uniqueSearches = [...new Set(searchQueries)];
100339
+ let summary = `I was unable to complete your request after ${currentIteration} tool iterations.
100340
+
100341
+ `;
100342
+ summary += `Tool calls made: ${toolBreakdown || "none"}
100343
+ `;
100344
+ if (uniqueSearches.length > 0) {
100345
+ summary += `Search queries tried: ${uniqueSearches.join(", ")}
100346
+ `;
100347
+ }
100348
+ summary += `
100349
+ The search approach may be fundamentally wrong for this query. Consider: using exact=true for literal string matching, using bash/grep for pattern-based file searches, or trying a completely different strategy instead of repeating similar searches.`;
100350
+ finalResult = summary;
100351
+ } catch {
100352
+ finalResult = DEFAULT_MAX_ITER_MSG;
100353
+ }
100354
+ }
99807
100355
  }
99808
100356
  this.history = currentMessages.map((msg) => ({ ...msg }));
99809
100357
  if (this.history.length > MAX_HISTORY_MESSAGES) {
@@ -100338,6 +100886,134 @@ Double-check your response based on the criteria above. If everything looks good
100338
100886
  console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
100339
100887
  }
100340
100888
  }
100889
+ /**
100890
+ * Trigger graceful wind-down from outside (e.g., parent agent).
100891
+ * Unlike cancel(), this does NOT abort — it sets the graceful timeout flag
100892
+ * so the agent finishes its current step and then winds down naturally.
100893
+ */
100894
+ triggerGracefulWindDown() {
100895
+ if (this._gracefulTimeoutState && !this._gracefulTimeoutState.triggered) {
100896
+ this._gracefulTimeoutState.triggered = true;
100897
+ if (this.debug) {
100898
+ console.log(`[DEBUG] Graceful wind-down triggered externally for session ${this.sessionId}`);
100899
+ }
100900
+ if (this.tracer) {
100901
+ this.tracer.addEvent("graceful_stop.external_trigger", {
100902
+ "session.id": this.sessionId
100903
+ });
100904
+ }
100905
+ } else if (this.debug) {
100906
+ console.log(`[DEBUG] Graceful wind-down already active for session ${this.sessionId}, skipping`);
100907
+ }
100908
+ }
100909
+ /**
100910
+ * Initiate two-phase graceful stop: signal subagents and MCP servers to wind down,
100911
+ * then hard-abort after a deadline if they haven't finished.
100912
+ * @param {Object} gracefulTimeoutState - The graceful timeout state object from run()
100913
+ * @param {string} reason - Why the graceful stop was initiated
100914
+ */
100915
+ async _initiateGracefulStop(gracefulTimeoutState, reason) {
100916
+ if (gracefulTimeoutState.triggered) return;
100917
+ if (this.debug) {
100918
+ console.log(`[DEBUG] Initiating graceful stop: ${reason} (subagents: ${this._activeSubagents.size}, hasMcpBridge: ${!!this.mcpBridge}, deadline: ${this.gracefulStopDeadline}ms)`);
100919
+ }
100920
+ gracefulTimeoutState.triggered = true;
100921
+ if (this.tracer) {
100922
+ this.tracer.addEvent("graceful_stop.initiated", {
100923
+ "session.id": this.sessionId,
100924
+ "graceful_stop.reason": reason,
100925
+ "graceful_stop.active_subagents": this._activeSubagents.size,
100926
+ "graceful_stop.has_mcp_bridge": !!this.mcpBridge,
100927
+ "graceful_stop.deadline_ms": this.gracefulStopDeadline
100928
+ });
100929
+ }
100930
+ let subagentsSignalled = 0;
100931
+ let subagentErrors = 0;
100932
+ for (const [sid, subagent] of this._activeSubagents) {
100933
+ try {
100934
+ subagent.triggerGracefulWindDown();
100935
+ subagentsSignalled++;
100936
+ if (this.debug) {
100937
+ console.log(`[DEBUG] Triggered graceful wind-down on subagent ${sid}`);
100938
+ }
100939
+ } catch (e) {
100940
+ subagentErrors++;
100941
+ if (this.debug) {
100942
+ console.log(`[DEBUG] Failed to trigger wind-down on subagent ${sid}: ${e.message}`);
100943
+ }
100944
+ }
100945
+ }
100946
+ let mcpResults = [];
100947
+ if (this.mcpBridge) {
100948
+ try {
100949
+ mcpResults = await this.mcpBridge.callGracefulStopAll();
100950
+ if (this.debug && mcpResults.length > 0) {
100951
+ console.log(`[DEBUG] MCP graceful_stop results: ${JSON.stringify(mcpResults)}`);
100952
+ }
100953
+ } catch (e) {
100954
+ if (this.debug) {
100955
+ console.log(`[DEBUG] MCP graceful_stop failed: ${e.message}`);
100956
+ }
100957
+ }
100958
+ }
100959
+ if (this.tracer) {
100960
+ this.tracer.addEvent("graceful_stop.signals_sent", {
100961
+ "session.id": this.sessionId,
100962
+ "graceful_stop.subagents_signalled": subagentsSignalled,
100963
+ "graceful_stop.subagent_errors": subagentErrors,
100964
+ "graceful_stop.mcp_servers_called": mcpResults.filter((r) => r.success).length,
100965
+ "graceful_stop.mcp_servers_failed": mcpResults.filter((r) => !r.success).length,
100966
+ "graceful_stop.mcp_servers_total": mcpResults.length
100967
+ });
100968
+ }
100969
+ this._gracefulStopHardAbortId = setTimeout(() => {
100970
+ if (this.debug) {
100971
+ console.log(`[DEBUG] Graceful stop deadline (${this.gracefulStopDeadline}ms) expired \u2014 hard aborting`);
100972
+ }
100973
+ if (this.tracer) {
100974
+ this.tracer.addEvent("graceful_stop.deadline_expired", {
100975
+ "session.id": this.sessionId,
100976
+ "graceful_stop.deadline_ms": this.gracefulStopDeadline
100977
+ });
100978
+ }
100979
+ if (this._abortController) this._abortController.abort();
100980
+ }, this.gracefulStopDeadline);
100981
+ }
100982
+ /**
100983
+ * Register an active subagent for graceful stop coordination.
100984
+ * @param {string} sessionId
100985
+ * @param {ProbeAgent} subagent
100986
+ */
100987
+ _registerSubagent(sessionId, subagent) {
100988
+ this._activeSubagents.set(sessionId, subagent);
100989
+ if (this.debug) {
100990
+ console.log(`[DEBUG] Registered subagent ${sessionId} (active: ${this._activeSubagents.size})`);
100991
+ }
100992
+ if (this.tracer) {
100993
+ this.tracer.addEvent("subagent.registered", {
100994
+ "session.id": this.sessionId,
100995
+ "subagent.session_id": sessionId,
100996
+ "subagent.active_count": this._activeSubagents.size
100997
+ });
100998
+ }
100999
+ }
101000
+ /**
101001
+ * Unregister a completed subagent.
101002
+ * @param {string} sessionId
101003
+ */
101004
+ _unregisterSubagent(sessionId) {
101005
+ this._activeSubagents.delete(sessionId);
101006
+ if (this.debug) {
101007
+ console.log(`[DEBUG] Unregistered subagent ${sessionId} (active: ${this._activeSubagents.size})`);
101008
+ }
101009
+ if (this.tracer) {
101010
+ this.tracer.addEvent("subagent.unregistered", {
101011
+ "session.id": this.sessionId,
101012
+ "subagent.session_id": sessionId,
101013
+ "subagent.active_count": this._activeSubagents.size
101014
+ });
101015
+ }
101016
+ }
100341
101017
  /**
100342
101018
  * Get the abort signal for this agent.
100343
101019
  * Delegations and subagents should check this signal.
@@ -100380,8 +101056,15 @@ async function delegate({
100380
101056
  // Optional per-instance manager, falls back to default singleton
100381
101057
  concurrencyLimiter = null,
100382
101058
  // Optional global AI concurrency limiter
100383
- parentAbortSignal = null
101059
+ parentAbortSignal = null,
100384
101060
  // Optional AbortSignal from parent to cancel this delegation
101061
+ // Timeout settings inherited from parent agent
101062
+ timeoutBehavior = void 0,
101063
+ requestTimeout = void 0,
101064
+ gracefulTimeoutBonusSteps = void 0,
101065
+ // Subagent lifecycle callbacks for graceful stop coordination
101066
+ onSubagentCreated = null,
101067
+ onSubagentCompleted = null
100385
101068
  }) {
100386
101069
  if (!task || typeof task !== "string") {
100387
101070
  throw new Error("Task parameter is required and must be a string");
@@ -100464,12 +101147,38 @@ async function delegate({
100464
101147
  // Inherit from parent
100465
101148
  mcpConfigPath,
100466
101149
  // Inherit from parent
100467
- concurrencyLimiter
101150
+ concurrencyLimiter,
100468
101151
  // Inherit global AI concurrency limiter
101152
+ // Inherit timeout behavior from parent — subagent gets its own graceful wind-down
101153
+ // so it can produce partial results instead of being hard-killed by the external timer.
101154
+ // The external delegate timeout (capped to parent's remaining budget) is the hard limit;
101155
+ // maxOperationTimeout on the subagent is set slightly shorter so its own wind-down
101156
+ // fires before the external kill.
101157
+ maxOperationTimeout: Math.max(1e4, timeout * 1e3 - 15e3),
101158
+ // 15s before external kill
101159
+ timeoutBehavior: timeoutBehavior || "graceful",
101160
+ requestTimeout,
101161
+ gracefulTimeoutBonusSteps: gracefulTimeoutBonusSteps ?? 2
101162
+ // fewer steps for subagents
100469
101163
  });
101164
+ if (onSubagentCreated) {
101165
+ onSubagentCreated(sessionId, subagent);
101166
+ }
100470
101167
  if (debug) {
100471
101168
  console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
100472
101169
  console.error(`[DELEGATE] Subagent config: promptType=${promptType}, enableDelegate=false, maxIterations=${remainingIterations}`);
101170
+ console.error(`[DELEGATE] Timeout inheritance: externalTimeout=${timeout}s, maxOperationTimeout=${Math.max(1e4, timeout * 1e3 - 15e3)}ms, behavior=${timeoutBehavior || "graceful"}, bonusSteps=${gracefulTimeoutBonusSteps ?? 2}`);
101171
+ }
101172
+ if (tracer) {
101173
+ tracer.addEvent("delegation.subagent_created", {
101174
+ "delegation.session_id": sessionId,
101175
+ "delegation.parent_session_id": parentSessionId,
101176
+ "delegation.external_timeout_s": timeout,
101177
+ "delegation.internal_timeout_ms": Math.max(1e4, timeout * 1e3 - 15e3),
101178
+ "delegation.timeout_behavior": timeoutBehavior || "graceful",
101179
+ "delegation.bonus_steps": gracefulTimeoutBonusSteps ?? 2,
101180
+ "delegation.max_iterations": remainingIterations
101181
+ });
100473
101182
  }
100474
101183
  const timeoutPromise = new Promise((_, reject2) => {
100475
101184
  timeoutId = setTimeout(() => {
@@ -100478,6 +101187,7 @@ async function delegate({
100478
101187
  }, timeout * 1e3);
100479
101188
  });
100480
101189
  let parentAbortHandler;
101190
+ let parentAbortHardCancelId = null;
100481
101191
  const parentAbortPromise = new Promise((_, reject2) => {
100482
101192
  if (parentAbortSignal) {
100483
101193
  if (parentAbortSignal.aborted) {
@@ -100486,8 +101196,31 @@ async function delegate({
100486
101196
  return;
100487
101197
  }
100488
101198
  parentAbortHandler = () => {
100489
- subagent.cancel();
100490
- reject2(new Error("Delegation cancelled: parent operation was aborted"));
101199
+ subagent.triggerGracefulWindDown();
101200
+ if (debug) {
101201
+ console.error(`[DELEGATE] Parent abort signal received \u2014 triggered graceful wind-down on subagent ${sessionId}`);
101202
+ }
101203
+ if (tracer) {
101204
+ tracer.addEvent("delegation.parent_abort_phase1", {
101205
+ "delegation.session_id": sessionId,
101206
+ "delegation.parent_session_id": parentSessionId,
101207
+ "delegation.action": "graceful_wind_down"
101208
+ });
101209
+ }
101210
+ parentAbortHardCancelId = setTimeout(() => {
101211
+ if (debug) {
101212
+ console.error(`[DELEGATE] Graceful wind-down deadline expired \u2014 hard cancelling subagent ${sessionId}`);
101213
+ }
101214
+ if (tracer) {
101215
+ tracer.addEvent("delegation.parent_abort_phase2", {
101216
+ "delegation.session_id": sessionId,
101217
+ "delegation.parent_session_id": parentSessionId,
101218
+ "delegation.action": "hard_cancel"
101219
+ });
101220
+ }
101221
+ subagent.cancel();
101222
+ reject2(new Error("Delegation cancelled: parent operation was aborted (graceful wind-down deadline expired)"));
101223
+ }, 3e4);
100491
101224
  };
100492
101225
  parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
100493
101226
  }
@@ -100503,6 +101236,13 @@ async function delegate({
100503
101236
  if (parentAbortHandler && parentAbortSignal) {
100504
101237
  parentAbortSignal.removeEventListener("abort", parentAbortHandler);
100505
101238
  }
101239
+ if (parentAbortHardCancelId) {
101240
+ clearTimeout(parentAbortHardCancelId);
101241
+ parentAbortHardCancelId = null;
101242
+ }
101243
+ if (onSubagentCompleted) {
101244
+ onSubagentCompleted(sessionId);
101245
+ }
100506
101246
  }
100507
101247
  if (timeoutId !== null) {
100508
101248
  clearTimeout(timeoutId);
@@ -101527,6 +102267,10 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
101527
102267
  "- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
101528
102268
  "- exact=true matches the literal string only \u2014 no stemming, no splitting.",
101529
102269
  '- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
102270
+ "- IMPORTANT: Use exact=true when searching for strings containing punctuation, quotes, or empty values.",
102271
+ " Default BM25 search strips punctuation and treats quoted empty strings as noise.",
102272
+ ` Example: searching for 'description: ""' with exact=false will NOT find empty description fields \u2014 it just matches "description".`,
102273
+ ` Use exact=true for literal patterns like 'description: ""', 'value: \\'\\'', or any YAML/config field with specific punctuation.`,
101530
102274
  "- Do NOT use exact=true for exploratory/conceptual queries \u2014 use the default for those.",
101531
102275
  "",
101532
102276
  "Combining searches with OR:",
@@ -101586,7 +102330,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
101586
102330
  "WHEN TO STOP:",
101587
102331
  "- After you have explored the main concept AND related subsystems.",
101588
102332
  "- Once you have 5-15 targets covering different aspects of the query.",
101589
- '- If you get a "DUPLICATE SEARCH BLOCKED" message, move on.',
102333
+ '- If you get a "DUPLICATE SEARCH BLOCKED" message, do NOT rephrase the same query \u2014 try a FUNDAMENTALLY different approach:',
102334
+ " * Switch between exact=true and exact=false",
102335
+ " * Search for a broader term and filter results manually",
102336
+ " * Use listFiles to browse the directory structure directly",
102337
+ " * Look for related/surrounding patterns instead of the exact string",
102338
+ "- If 2-3 genuinely different search approaches fail, STOP and report what you tried and why it failed.",
102339
+ " Do NOT keep trying variations of the same failing concept.",
101590
102340
  "",
101591
102341
  "Strategy:",
101592
102342
  "1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
@@ -101658,8 +102408,8 @@ var init_vercel = __esm({
101658
102408
  }
101659
102409
  return result;
101660
102410
  };
101661
- const previousSearches = /* @__PURE__ */ new Set();
101662
- let consecutiveDupBlocks = 0;
102411
+ const previousSearches = /* @__PURE__ */ new Map();
102412
+ const dupBlockCounts = /* @__PURE__ */ new Map();
101663
102413
  const paginationCounts = /* @__PURE__ */ new Map();
101664
102414
  const MAX_PAGES_PER_QUERY = 3;
101665
102415
  return (0, import_ai5.tool)({
@@ -101710,20 +102460,25 @@ var init_vercel = __esm({
101710
102460
  return await search(searchOptions);
101711
102461
  };
101712
102462
  if (!searchDelegate) {
101713
- const searchKey = `${searchQuery}::${exact || false}`;
102463
+ const searchKey = `${searchPath}::${searchQuery}::${exact || false}`;
101714
102464
  if (!nextPage) {
101715
102465
  if (previousSearches.has(searchKey)) {
101716
- consecutiveDupBlocks++;
102466
+ const blockCount = (dupBlockCounts.get(searchKey) || 0) + 1;
102467
+ dupBlockCounts.set(searchKey, blockCount);
101717
102468
  if (debug) {
101718
- console.error(`[DEDUP] Blocked duplicate search (${consecutiveDupBlocks}x): "${searchQuery}" (path: "${searchPath}")`);
102469
+ console.error(`[DEDUP] Blocked duplicate search (${blockCount}x): "${searchQuery}" (path: "${searchPath}")`);
101719
102470
  }
101720
- if (consecutiveDupBlocks >= 3) {
101721
- return "STOP. You have been blocked " + consecutiveDupBlocks + " times for repeating searches. You MUST output your final JSON answer NOW with whatever targets you have found. Do NOT call any more tools.";
102471
+ if (blockCount >= 3) {
102472
+ return "STOP. You have been blocked " + blockCount + " times for repeating the same search. You MUST provide your final answer NOW with whatever information you have. Do NOT call any more tools.";
101722
102473
  }
101723
- return "DUPLICATE SEARCH BLOCKED (" + consecutiveDupBlocks + "x). You already searched for this. Do NOT repeat \u2014 probe searches recursively across all paths. Either: (1) use extract on results you already found, (2) try a COMPLETELY different keyword, or (3) output your final answer NOW.";
102474
+ const prev = previousSearches.get(searchKey);
102475
+ if (prev.hadResults) {
102476
+ return `DUPLICATE SEARCH BLOCKED (${blockCount}x). You already searched for "${searchQuery}" in this path and found results. Do NOT repeat. Use extract to examine the files you already found, try a COMPLETELY different keyword, or provide your final answer.`;
102477
+ }
102478
+ const exactHint = exact ? "You used exact=true. Try a broader search with exact=false, or use listFiles to browse the directory structure." : `Try exact=true if you need literal/punctuation matching (e.g. 'description: ""'), or use listFiles to explore directories, or search for a broader/related term and filter manually.`;
102479
+ return `DUPLICATE SEARCH BLOCKED (${blockCount}x). You already searched for "${searchQuery}" in this path and got NO results. This term does not appear in the codebase. Do NOT repeat or rephrase \u2014 try a FUNDAMENTALLY different approach: ${exactHint} If multiple approaches have failed, provide your final answer with what you know.`;
101724
102480
  }
101725
- previousSearches.add(searchKey);
101726
- consecutiveDupBlocks = 0;
102481
+ previousSearches.set(searchKey, { hadResults: false });
101727
102482
  paginationCounts.set(searchKey, 0);
101728
102483
  } else {
101729
102484
  const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
@@ -101737,6 +102492,14 @@ var init_vercel = __esm({
101737
102492
  }
101738
102493
  try {
101739
102494
  const result = maybeAnnotate(await runRawSearch());
102495
+ if (typeof result === "string" && result.includes("No results found")) {
102496
+ if (/^[A-Z]+-\d+$/.test(searchQuery.trim()) || /^[A-Z]+-\d+$/.test(searchQuery.replace(/"/g, "").trim())) {
102497
+ return result + "\n\n\u26A0\uFE0F Your query looks like a ticket/issue ID (e.g., JIRA-1234). Ticket IDs are rarely present in source code. Search for the technical concepts described in the ticket instead (e.g., function names, error messages, variable names).";
102498
+ }
102499
+ } else if (typeof result === "string") {
102500
+ const entry = previousSearches.get(searchKey);
102501
+ if (entry) entry.hadResults = true;
102502
+ }
101740
102503
  if (options.fileTracker && typeof result === "string") {
101741
102504
  options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
101742
102505
  });
@@ -102019,7 +102782,31 @@ var init_vercel = __esm({
102019
102782
  });
102020
102783
  };
102021
102784
  delegateTool = (options = {}) => {
102022
- const { debug = false, timeout = 300, cwd, allowedFolders, workspaceRoot, enableBash = false, bashConfig, architectureFileName, enableMcp = false, mcpConfig = null, mcpConfigPath = null, delegationManager = null } = options;
102785
+ const {
102786
+ debug = false,
102787
+ timeout = 300,
102788
+ cwd,
102789
+ allowedFolders,
102790
+ workspaceRoot,
102791
+ enableBash = false,
102792
+ bashConfig,
102793
+ architectureFileName,
102794
+ enableMcp = false,
102795
+ mcpConfig = null,
102796
+ mcpConfigPath = null,
102797
+ delegationManager = null,
102798
+ // Timeout settings inherited from parent agent
102799
+ timeoutBehavior,
102800
+ maxOperationTimeout,
102801
+ requestTimeout,
102802
+ gracefulTimeoutBonusSteps,
102803
+ negotiatedTimeoutBudget,
102804
+ negotiatedTimeoutMaxRequests,
102805
+ negotiatedTimeoutMaxPerRequest,
102806
+ parentOperationStartTime,
102807
+ onSubagentCreated,
102808
+ onSubagentCompleted
102809
+ } = options;
102023
102810
  return (0, import_ai5.tool)({
102024
102811
  name: "delegate",
102025
102812
  description: delegateDescription,
@@ -102063,9 +102850,30 @@ var init_vercel = __esm({
102063
102850
  console.error(`Using workspace root: ${effectivePath} (cwd was: ${cwd || "not set"})`);
102064
102851
  }
102065
102852
  }
102853
+ let effectiveTimeout = timeout;
102854
+ if (parentOperationStartTime && maxOperationTimeout) {
102855
+ const elapsed = Date.now() - parentOperationStartTime;
102856
+ const remaining = maxOperationTimeout - elapsed;
102857
+ const budgetCap = Math.max(30, Math.floor(remaining * 0.9 / 1e3));
102858
+ if (budgetCap < effectiveTimeout) {
102859
+ effectiveTimeout = budgetCap;
102860
+ if (debug) {
102861
+ console.error(`[DELEGATE] Capping timeout from ${timeout}s to ${effectiveTimeout}s (remaining parent budget: ${Math.floor(remaining / 1e3)}s)`);
102862
+ }
102863
+ if (tracer) {
102864
+ tracer.addEvent("delegation.budget_capped", {
102865
+ "delegation.original_timeout_s": timeout,
102866
+ "delegation.effective_timeout_s": effectiveTimeout,
102867
+ "delegation.parent_elapsed_ms": elapsed,
102868
+ "delegation.parent_remaining_ms": remaining,
102869
+ "delegation.parent_session_id": parentSessionId
102870
+ });
102871
+ }
102872
+ }
102873
+ }
102066
102874
  const result = await delegate({
102067
102875
  task,
102068
- timeout,
102876
+ timeout: effectiveTimeout,
102069
102877
  debug,
102070
102878
  currentIteration: currentIteration || 0,
102071
102879
  maxIterations: maxIterations || 30,
@@ -102084,7 +102892,14 @@ var init_vercel = __esm({
102084
102892
  mcpConfigPath,
102085
102893
  delegationManager,
102086
102894
  // Per-instance delegation limits
102087
- parentAbortSignal
102895
+ parentAbortSignal,
102896
+ // Inherit timeout settings for subagent
102897
+ timeoutBehavior,
102898
+ requestTimeout,
102899
+ gracefulTimeoutBonusSteps,
102900
+ // Subagent lifecycle callbacks for graceful stop coordination
102901
+ onSubagentCreated,
102902
+ onSubagentCompleted
102088
102903
  });
102089
102904
  return result;
102090
102905
  }
@@ -103563,6 +104378,121 @@ var init_file_lister = __esm({
103563
104378
  }
103564
104379
  });
103565
104380
 
104381
+ // src/agent/otelLogBridge.js
104382
+ function getOtelApi() {
104383
+ if (otelApiAttempted) return otelApi;
104384
+ otelApiAttempted = true;
104385
+ try {
104386
+ otelApi = (function(name15) {
104387
+ return _require(name15);
104388
+ })("@opentelemetry/api");
104389
+ } catch {
104390
+ }
104391
+ return otelApi;
104392
+ }
104393
+ function getOtelLogger() {
104394
+ if (otelLoggerAttempted) return otelLogger;
104395
+ otelLoggerAttempted = true;
104396
+ try {
104397
+ const { logs } = (function(name15) {
104398
+ return _require(name15);
104399
+ })("@opentelemetry/api-logs");
104400
+ otelLogger = logs.getLogger("probe-agent");
104401
+ } catch {
104402
+ }
104403
+ return otelLogger;
104404
+ }
104405
+ function getTraceSuffix() {
104406
+ try {
104407
+ const api2 = getOtelApi();
104408
+ if (!api2) return "";
104409
+ const span = api2.trace.getSpan(api2.context.active());
104410
+ const ctx = span?.spanContext?.();
104411
+ if (!ctx?.traceId) return "";
104412
+ return ` [trace_id=${ctx.traceId} span_id=${ctx.spanId}]`;
104413
+ } catch {
104414
+ return "";
104415
+ }
104416
+ }
104417
+ function emitOtelLog(msg, level) {
104418
+ try {
104419
+ const logger = getOtelLogger();
104420
+ if (!logger) return;
104421
+ const api2 = getOtelApi();
104422
+ let traceId, spanId;
104423
+ if (api2) {
104424
+ const span = api2.trace.getSpan(api2.context.active());
104425
+ const ctx = span?.spanContext?.();
104426
+ if (ctx?.traceId) {
104427
+ traceId = ctx.traceId;
104428
+ spanId = ctx.spanId;
104429
+ }
104430
+ }
104431
+ logger.emit({
104432
+ severityNumber: OTEL_SEVERITY[level] || 9,
104433
+ severityText: level.toUpperCase(),
104434
+ body: msg,
104435
+ attributes: {
104436
+ "probe.logger": true,
104437
+ ...traceId ? { trace_id: traceId, span_id: spanId } : {}
104438
+ }
104439
+ });
104440
+ } catch {
104441
+ }
104442
+ }
104443
+ function patchConsole() {
104444
+ if (patched) return;
104445
+ const methods = ["log", "info", "warn", "error"];
104446
+ const c = globalThis.console;
104447
+ for (const m of methods) {
104448
+ const orig = c[m].bind(c);
104449
+ originals[m] = orig;
104450
+ c[m] = (...args) => {
104451
+ const msgParts = args.map(
104452
+ (a) => typeof a === "string" ? a : a instanceof Error ? a.message : JSON.stringify(a)
104453
+ );
104454
+ const msg = msgParts.join(" ");
104455
+ emitOtelLog(msg, m === "log" ? "log" : m);
104456
+ const suffix = getTraceSuffix();
104457
+ if (suffix) {
104458
+ if (typeof args[0] === "string") {
104459
+ args[0] = args[0] + suffix;
104460
+ } else {
104461
+ args.push(suffix);
104462
+ }
104463
+ }
104464
+ return orig(...args);
104465
+ };
104466
+ }
104467
+ patched = true;
104468
+ }
104469
+ var import_module, _require, OTEL_SEVERITY, patched, originals, otelApi, otelApiAttempted, otelLogger, otelLoggerAttempted;
104470
+ var init_otelLogBridge = __esm({
104471
+ "src/agent/otelLogBridge.js"() {
104472
+ "use strict";
104473
+ import_module = require("module");
104474
+ _require = (0, import_module.createRequire)("file:///");
104475
+ OTEL_SEVERITY = {
104476
+ log: 9,
104477
+ // INFO
104478
+ info: 9,
104479
+ // INFO
104480
+ warn: 13,
104481
+ // WARN
104482
+ error: 17,
104483
+ // ERROR
104484
+ debug: 5
104485
+ // DEBUG
104486
+ };
104487
+ patched = false;
104488
+ originals = {};
104489
+ otelApi = null;
104490
+ otelApiAttempted = false;
104491
+ otelLogger = null;
104492
+ otelLoggerAttempted = false;
104493
+ }
104494
+ });
104495
+
103566
104496
  // src/agent/simpleTelemetry.js
103567
104497
  function initializeSimpleTelemetryFromOptions(options) {
103568
104498
  const telemetry = new SimpleTelemetry({
@@ -103571,6 +104501,7 @@ function initializeSimpleTelemetryFromOptions(options) {
103571
104501
  enableConsole: options.traceConsole,
103572
104502
  filePath: options.traceFile || "./traces.jsonl"
103573
104503
  });
104504
+ patchConsole();
103574
104505
  return telemetry;
103575
104506
  }
103576
104507
  var import_fs15, import_path18, SimpleTelemetry, SimpleAppTracer;
@@ -103579,6 +104510,7 @@ var init_simpleTelemetry = __esm({
103579
104510
  "use strict";
103580
104511
  import_fs15 = require("fs");
103581
104512
  import_path18 = require("path");
104513
+ init_otelLogBridge();
103582
104514
  SimpleTelemetry = class {
103583
104515
  constructor(options = {}) {
103584
104516
  this.serviceName = options.serviceName || "probe-agent";
@@ -104029,6 +104961,9 @@ var init_hooks = __esm({
104029
104961
  var index_exports = {};
104030
104962
  __export(index_exports, {
104031
104963
  DEFAULT_SYSTEM_MESSAGE: () => DEFAULT_SYSTEM_MESSAGE,
104964
+ ENGINE_ACTIVITY_TIMEOUT_DEFAULT: () => ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
104965
+ ENGINE_ACTIVITY_TIMEOUT_MAX: () => ENGINE_ACTIVITY_TIMEOUT_MAX,
104966
+ ENGINE_ACTIVITY_TIMEOUT_MIN: () => ENGINE_ACTIVITY_TIMEOUT_MIN,
104032
104967
  FileTracker: () => FileTracker,
104033
104968
  HOOK_TYPES: () => HOOK_TYPES,
104034
104969
  HookManager: () => HookManager,
@@ -104115,6 +105050,9 @@ init_index();
104115
105050
  // Annotate the CommonJS export names for ESM import in node:
104116
105051
  0 && (module.exports = {
104117
105052
  DEFAULT_SYSTEM_MESSAGE,
105053
+ ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
105054
+ ENGINE_ACTIVITY_TIMEOUT_MAX,
105055
+ ENGINE_ACTIVITY_TIMEOUT_MIN,
104118
105056
  FileTracker,
104119
105057
  HOOK_TYPES,
104120
105058
  HookManager,