@probelabs/probe 0.6.0-rc295 → 0.6.0-rc297
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/README.md +7 -0
- package/bin/binaries/{probe-v0.6.0-rc295-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc297-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc295-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc297-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc295-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc297-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc295-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc297-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc295-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc297-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.d.ts +40 -2
- package/build/agent/ProbeAgent.js +703 -11
- package/build/agent/mcp/client.js +115 -4
- package/build/agent/mcp/xmlBridge.js +13 -1
- package/build/agent/otelLogBridge.js +184 -0
- package/build/agent/simpleTelemetry.js +8 -0
- package/build/delegate.js +75 -6
- package/build/index.js +6 -2
- package/build/tools/common.js +84 -11
- package/build/tools/vercel.js +78 -18
- package/cjs/agent/ProbeAgent.cjs +1095 -185
- package/cjs/agent/simpleTelemetry.cjs +112 -0
- package/cjs/index.cjs +1207 -185
- package/index.d.ts +26 -0
- package/package.json +2 -2
- package/src/agent/ProbeAgent.d.ts +40 -2
- package/src/agent/ProbeAgent.js +703 -11
- package/src/agent/mcp/client.js +115 -4
- package/src/agent/mcp/xmlBridge.js +13 -1
- package/src/agent/otelLogBridge.js +184 -0
- package/src/agent/simpleTelemetry.js +8 -0
- package/src/delegate.js +75 -6
- package/src/index.js +6 -2
- package/src/tools/common.js +84 -11
- package/src/tools/vercel.js +78 -18
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -26106,8 +26106,15 @@ async function delegate({
|
|
|
26106
26106
|
// Optional per-instance manager, falls back to default singleton
|
|
26107
26107
|
concurrencyLimiter = null,
|
|
26108
26108
|
// Optional global AI concurrency limiter
|
|
26109
|
-
parentAbortSignal = null
|
|
26109
|
+
parentAbortSignal = null,
|
|
26110
26110
|
// Optional AbortSignal from parent to cancel this delegation
|
|
26111
|
+
// Timeout settings inherited from parent agent
|
|
26112
|
+
timeoutBehavior = void 0,
|
|
26113
|
+
requestTimeout = void 0,
|
|
26114
|
+
gracefulTimeoutBonusSteps = void 0,
|
|
26115
|
+
// Subagent lifecycle callbacks for graceful stop coordination
|
|
26116
|
+
onSubagentCreated = null,
|
|
26117
|
+
onSubagentCompleted = null
|
|
26111
26118
|
}) {
|
|
26112
26119
|
if (!task || typeof task !== "string") {
|
|
26113
26120
|
throw new Error("Task parameter is required and must be a string");
|
|
@@ -26190,12 +26197,38 @@ async function delegate({
|
|
|
26190
26197
|
// Inherit from parent
|
|
26191
26198
|
mcpConfigPath,
|
|
26192
26199
|
// Inherit from parent
|
|
26193
|
-
concurrencyLimiter
|
|
26200
|
+
concurrencyLimiter,
|
|
26194
26201
|
// Inherit global AI concurrency limiter
|
|
26202
|
+
// Inherit timeout behavior from parent — subagent gets its own graceful wind-down
|
|
26203
|
+
// so it can produce partial results instead of being hard-killed by the external timer.
|
|
26204
|
+
// The external delegate timeout (capped to parent's remaining budget) is the hard limit;
|
|
26205
|
+
// maxOperationTimeout on the subagent is set slightly shorter so its own wind-down
|
|
26206
|
+
// fires before the external kill.
|
|
26207
|
+
maxOperationTimeout: Math.max(1e4, timeout * 1e3 - 15e3),
|
|
26208
|
+
// 15s before external kill
|
|
26209
|
+
timeoutBehavior: timeoutBehavior || "graceful",
|
|
26210
|
+
requestTimeout,
|
|
26211
|
+
gracefulTimeoutBonusSteps: gracefulTimeoutBonusSteps ?? 2
|
|
26212
|
+
// fewer steps for subagents
|
|
26195
26213
|
});
|
|
26214
|
+
if (onSubagentCreated) {
|
|
26215
|
+
onSubagentCreated(sessionId, subagent);
|
|
26216
|
+
}
|
|
26196
26217
|
if (debug) {
|
|
26197
26218
|
console.error(`[DELEGATE] Created subagent with session ${sessionId}`);
|
|
26198
26219
|
console.error(`[DELEGATE] Subagent config: promptType=${promptType}, enableDelegate=false, maxIterations=${remainingIterations}`);
|
|
26220
|
+
console.error(`[DELEGATE] Timeout inheritance: externalTimeout=${timeout}s, maxOperationTimeout=${Math.max(1e4, timeout * 1e3 - 15e3)}ms, behavior=${timeoutBehavior || "graceful"}, bonusSteps=${gracefulTimeoutBonusSteps ?? 2}`);
|
|
26221
|
+
}
|
|
26222
|
+
if (tracer) {
|
|
26223
|
+
tracer.addEvent("delegation.subagent_created", {
|
|
26224
|
+
"delegation.session_id": sessionId,
|
|
26225
|
+
"delegation.parent_session_id": parentSessionId,
|
|
26226
|
+
"delegation.external_timeout_s": timeout,
|
|
26227
|
+
"delegation.internal_timeout_ms": Math.max(1e4, timeout * 1e3 - 15e3),
|
|
26228
|
+
"delegation.timeout_behavior": timeoutBehavior || "graceful",
|
|
26229
|
+
"delegation.bonus_steps": gracefulTimeoutBonusSteps ?? 2,
|
|
26230
|
+
"delegation.max_iterations": remainingIterations
|
|
26231
|
+
});
|
|
26199
26232
|
}
|
|
26200
26233
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
26201
26234
|
timeoutId = setTimeout(() => {
|
|
@@ -26204,6 +26237,7 @@ async function delegate({
|
|
|
26204
26237
|
}, timeout * 1e3);
|
|
26205
26238
|
});
|
|
26206
26239
|
let parentAbortHandler;
|
|
26240
|
+
let parentAbortHardCancelId = null;
|
|
26207
26241
|
const parentAbortPromise = new Promise((_, reject2) => {
|
|
26208
26242
|
if (parentAbortSignal) {
|
|
26209
26243
|
if (parentAbortSignal.aborted) {
|
|
@@ -26212,8 +26246,31 @@ async function delegate({
|
|
|
26212
26246
|
return;
|
|
26213
26247
|
}
|
|
26214
26248
|
parentAbortHandler = () => {
|
|
26215
|
-
subagent.
|
|
26216
|
-
|
|
26249
|
+
subagent.triggerGracefulWindDown();
|
|
26250
|
+
if (debug) {
|
|
26251
|
+
console.error(`[DELEGATE] Parent abort signal received \u2014 triggered graceful wind-down on subagent ${sessionId}`);
|
|
26252
|
+
}
|
|
26253
|
+
if (tracer) {
|
|
26254
|
+
tracer.addEvent("delegation.parent_abort_phase1", {
|
|
26255
|
+
"delegation.session_id": sessionId,
|
|
26256
|
+
"delegation.parent_session_id": parentSessionId,
|
|
26257
|
+
"delegation.action": "graceful_wind_down"
|
|
26258
|
+
});
|
|
26259
|
+
}
|
|
26260
|
+
parentAbortHardCancelId = setTimeout(() => {
|
|
26261
|
+
if (debug) {
|
|
26262
|
+
console.error(`[DELEGATE] Graceful wind-down deadline expired \u2014 hard cancelling subagent ${sessionId}`);
|
|
26263
|
+
}
|
|
26264
|
+
if (tracer) {
|
|
26265
|
+
tracer.addEvent("delegation.parent_abort_phase2", {
|
|
26266
|
+
"delegation.session_id": sessionId,
|
|
26267
|
+
"delegation.parent_session_id": parentSessionId,
|
|
26268
|
+
"delegation.action": "hard_cancel"
|
|
26269
|
+
});
|
|
26270
|
+
}
|
|
26271
|
+
subagent.cancel();
|
|
26272
|
+
reject2(new Error("Delegation cancelled: parent operation was aborted (graceful wind-down deadline expired)"));
|
|
26273
|
+
}, 3e4);
|
|
26217
26274
|
};
|
|
26218
26275
|
parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
|
|
26219
26276
|
}
|
|
@@ -26229,6 +26286,13 @@ async function delegate({
|
|
|
26229
26286
|
if (parentAbortHandler && parentAbortSignal) {
|
|
26230
26287
|
parentAbortSignal.removeEventListener("abort", parentAbortHandler);
|
|
26231
26288
|
}
|
|
26289
|
+
if (parentAbortHardCancelId) {
|
|
26290
|
+
clearTimeout(parentAbortHardCancelId);
|
|
26291
|
+
parentAbortHardCancelId = null;
|
|
26292
|
+
}
|
|
26293
|
+
if (onSubagentCompleted) {
|
|
26294
|
+
onSubagentCompleted(sessionId);
|
|
26295
|
+
}
|
|
26232
26296
|
}
|
|
26233
26297
|
if (timeoutId !== null) {
|
|
26234
26298
|
clearTimeout(timeoutId);
|
|
@@ -27064,14 +27128,64 @@ function detectStuckResponse(response) {
|
|
|
27064
27128
|
}
|
|
27065
27129
|
return false;
|
|
27066
27130
|
}
|
|
27131
|
+
function splitQuotedString(input) {
|
|
27132
|
+
const tokens = [];
|
|
27133
|
+
let current2 = "";
|
|
27134
|
+
let inQuote = null;
|
|
27135
|
+
let i = 0;
|
|
27136
|
+
while (i < input.length) {
|
|
27137
|
+
const ch = input[i];
|
|
27138
|
+
if (inQuote) {
|
|
27139
|
+
if (ch === "\\" && i + 1 < input.length) {
|
|
27140
|
+
current2 += input[i + 1];
|
|
27141
|
+
i += 2;
|
|
27142
|
+
continue;
|
|
27143
|
+
}
|
|
27144
|
+
if (ch === inQuote) {
|
|
27145
|
+
inQuote = null;
|
|
27146
|
+
i++;
|
|
27147
|
+
continue;
|
|
27148
|
+
}
|
|
27149
|
+
current2 += ch;
|
|
27150
|
+
i++;
|
|
27151
|
+
} else {
|
|
27152
|
+
if (ch === '"' || ch === "'") {
|
|
27153
|
+
inQuote = ch;
|
|
27154
|
+
i++;
|
|
27155
|
+
continue;
|
|
27156
|
+
}
|
|
27157
|
+
if (/[\s,]/.test(ch)) {
|
|
27158
|
+
if (current2.length > 0) {
|
|
27159
|
+
tokens.push(current2);
|
|
27160
|
+
current2 = "";
|
|
27161
|
+
}
|
|
27162
|
+
i++;
|
|
27163
|
+
continue;
|
|
27164
|
+
}
|
|
27165
|
+
current2 += ch;
|
|
27166
|
+
i++;
|
|
27167
|
+
}
|
|
27168
|
+
}
|
|
27169
|
+
if (current2.length > 0) {
|
|
27170
|
+
tokens.push(current2);
|
|
27171
|
+
}
|
|
27172
|
+
return tokens;
|
|
27173
|
+
}
|
|
27067
27174
|
function parseTargets(targets) {
|
|
27068
27175
|
if (!targets || typeof targets !== "string") {
|
|
27069
27176
|
return [];
|
|
27070
27177
|
}
|
|
27071
|
-
return targets
|
|
27178
|
+
return splitQuotedString(targets);
|
|
27072
27179
|
}
|
|
27073
27180
|
function parseAndResolvePaths(pathStr, cwd) {
|
|
27074
27181
|
if (!pathStr) return [];
|
|
27182
|
+
if (/["']/.test(pathStr)) {
|
|
27183
|
+
const paths2 = splitQuotedString(pathStr);
|
|
27184
|
+
return paths2.map((p) => {
|
|
27185
|
+
if ((0, import_path5.isAbsolute)(p)) return p;
|
|
27186
|
+
return cwd ? (0, import_path5.resolve)(cwd, p) : p;
|
|
27187
|
+
});
|
|
27188
|
+
}
|
|
27075
27189
|
let paths = pathStr.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
27076
27190
|
paths = paths.flatMap((p) => {
|
|
27077
27191
|
if (!/\s/.test(p)) return [p];
|
|
@@ -27081,9 +27195,7 @@ function parseAndResolvePaths(pathStr, cwd) {
|
|
|
27081
27195
|
return allLookLikePaths ? parts : [p];
|
|
27082
27196
|
});
|
|
27083
27197
|
return paths.map((p) => {
|
|
27084
|
-
if ((0, import_path5.isAbsolute)(p))
|
|
27085
|
-
return p;
|
|
27086
|
-
}
|
|
27198
|
+
if ((0, import_path5.isAbsolute)(p)) return p;
|
|
27087
27199
|
return cwd ? (0, import_path5.resolve)(cwd, p) : p;
|
|
27088
27200
|
});
|
|
27089
27201
|
}
|
|
@@ -27116,7 +27228,7 @@ var init_common = __esm({
|
|
|
27116
27228
|
searchSchema = external_exports2.object({
|
|
27117
27229
|
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."),
|
|
27118
27230
|
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.'),
|
|
27119
|
-
exact: external_exports2.boolean().optional().default(false).describe(
|
|
27231
|
+
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.`),
|
|
27120
27232
|
maxTokens: external_exports2.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
|
|
27121
27233
|
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."),
|
|
27122
27234
|
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.")
|
|
@@ -27427,6 +27539,10 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27427
27539
|
"- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
|
|
27428
27540
|
"- exact=true matches the literal string only \u2014 no stemming, no splitting.",
|
|
27429
27541
|
'- This is ideal for precise lookups: exact=true "ForwardMessage", exact=true "SessionLimiter", exact=true "ThrottleRetryLimit".',
|
|
27542
|
+
"- IMPORTANT: Use exact=true when searching for strings containing punctuation, quotes, or empty values.",
|
|
27543
|
+
" Default BM25 search strips punctuation and treats quoted empty strings as noise.",
|
|
27544
|
+
` Example: searching for 'description: ""' with exact=false will NOT find empty description fields \u2014 it just matches "description".`,
|
|
27545
|
+
` Use exact=true for literal patterns like 'description: ""', 'value: \\'\\'', or any YAML/config field with specific punctuation.`,
|
|
27430
27546
|
"- Do NOT use exact=true for exploratory/conceptual queries \u2014 use the default for those.",
|
|
27431
27547
|
"",
|
|
27432
27548
|
"Combining searches with OR:",
|
|
@@ -27486,7 +27602,13 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27486
27602
|
"WHEN TO STOP:",
|
|
27487
27603
|
"- After you have explored the main concept AND related subsystems.",
|
|
27488
27604
|
"- Once you have 5-15 targets covering different aspects of the query.",
|
|
27489
|
-
'- If you get a "DUPLICATE SEARCH BLOCKED" message,
|
|
27605
|
+
'- If you get a "DUPLICATE SEARCH BLOCKED" message, do NOT rephrase the same query \u2014 try a FUNDAMENTALLY different approach:',
|
|
27606
|
+
" * Switch between exact=true and exact=false",
|
|
27607
|
+
" * Search for a broader term and filter results manually",
|
|
27608
|
+
" * Use listFiles to browse the directory structure directly",
|
|
27609
|
+
" * Look for related/surrounding patterns instead of the exact string",
|
|
27610
|
+
"- If 2-3 genuinely different search approaches fail, STOP and report what you tried and why it failed.",
|
|
27611
|
+
" Do NOT keep trying variations of the same failing concept.",
|
|
27490
27612
|
"",
|
|
27491
27613
|
"Strategy:",
|
|
27492
27614
|
"1. Analyze the query \u2014 identify key concepts, then brainstorm SYNONYMS and alternative terms for each.",
|
|
@@ -27558,8 +27680,8 @@ var init_vercel = __esm({
|
|
|
27558
27680
|
}
|
|
27559
27681
|
return result;
|
|
27560
27682
|
};
|
|
27561
|
-
const previousSearches = /* @__PURE__ */ new
|
|
27562
|
-
|
|
27683
|
+
const previousSearches = /* @__PURE__ */ new Map();
|
|
27684
|
+
const dupBlockCounts = /* @__PURE__ */ new Map();
|
|
27563
27685
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
27564
27686
|
const MAX_PAGES_PER_QUERY = 3;
|
|
27565
27687
|
return (0, import_ai.tool)({
|
|
@@ -27610,20 +27732,25 @@ var init_vercel = __esm({
|
|
|
27610
27732
|
return await search(searchOptions);
|
|
27611
27733
|
};
|
|
27612
27734
|
if (!searchDelegate) {
|
|
27613
|
-
const searchKey = `${searchQuery}::${exact || false}`;
|
|
27735
|
+
const searchKey = `${searchPath}::${searchQuery}::${exact || false}`;
|
|
27614
27736
|
if (!nextPage) {
|
|
27615
27737
|
if (previousSearches.has(searchKey)) {
|
|
27616
|
-
|
|
27738
|
+
const blockCount = (dupBlockCounts.get(searchKey) || 0) + 1;
|
|
27739
|
+
dupBlockCounts.set(searchKey, blockCount);
|
|
27617
27740
|
if (debug) {
|
|
27618
|
-
console.error(`[DEDUP] Blocked duplicate search (${
|
|
27741
|
+
console.error(`[DEDUP] Blocked duplicate search (${blockCount}x): "${searchQuery}" (path: "${searchPath}")`);
|
|
27742
|
+
}
|
|
27743
|
+
if (blockCount >= 3) {
|
|
27744
|
+
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.";
|
|
27619
27745
|
}
|
|
27620
|
-
|
|
27621
|
-
|
|
27746
|
+
const prev = previousSearches.get(searchKey);
|
|
27747
|
+
if (prev.hadResults) {
|
|
27748
|
+
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.`;
|
|
27622
27749
|
}
|
|
27623
|
-
|
|
27750
|
+
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.`;
|
|
27751
|
+
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.`;
|
|
27624
27752
|
}
|
|
27625
|
-
previousSearches.
|
|
27626
|
-
consecutiveDupBlocks = 0;
|
|
27753
|
+
previousSearches.set(searchKey, { hadResults: false });
|
|
27627
27754
|
paginationCounts.set(searchKey, 0);
|
|
27628
27755
|
} else {
|
|
27629
27756
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
@@ -27637,6 +27764,14 @@ var init_vercel = __esm({
|
|
|
27637
27764
|
}
|
|
27638
27765
|
try {
|
|
27639
27766
|
const result = maybeAnnotate(await runRawSearch());
|
|
27767
|
+
if (typeof result === "string" && result.includes("No results found")) {
|
|
27768
|
+
if (/^[A-Z]+-\d+$/.test(searchQuery.trim()) || /^[A-Z]+-\d+$/.test(searchQuery.replace(/"/g, "").trim())) {
|
|
27769
|
+
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).";
|
|
27770
|
+
}
|
|
27771
|
+
} else if (typeof result === "string") {
|
|
27772
|
+
const entry = previousSearches.get(searchKey);
|
|
27773
|
+
if (entry) entry.hadResults = true;
|
|
27774
|
+
}
|
|
27640
27775
|
if (options.fileTracker && typeof result === "string") {
|
|
27641
27776
|
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
|
|
27642
27777
|
});
|
|
@@ -27919,7 +28054,31 @@ var init_vercel = __esm({
|
|
|
27919
28054
|
});
|
|
27920
28055
|
};
|
|
27921
28056
|
delegateTool = (options = {}) => {
|
|
27922
|
-
const {
|
|
28057
|
+
const {
|
|
28058
|
+
debug = false,
|
|
28059
|
+
timeout = 300,
|
|
28060
|
+
cwd,
|
|
28061
|
+
allowedFolders,
|
|
28062
|
+
workspaceRoot,
|
|
28063
|
+
enableBash = false,
|
|
28064
|
+
bashConfig,
|
|
28065
|
+
architectureFileName,
|
|
28066
|
+
enableMcp = false,
|
|
28067
|
+
mcpConfig = null,
|
|
28068
|
+
mcpConfigPath = null,
|
|
28069
|
+
delegationManager = null,
|
|
28070
|
+
// Timeout settings inherited from parent agent
|
|
28071
|
+
timeoutBehavior,
|
|
28072
|
+
maxOperationTimeout,
|
|
28073
|
+
requestTimeout,
|
|
28074
|
+
gracefulTimeoutBonusSteps,
|
|
28075
|
+
negotiatedTimeoutBudget,
|
|
28076
|
+
negotiatedTimeoutMaxRequests,
|
|
28077
|
+
negotiatedTimeoutMaxPerRequest,
|
|
28078
|
+
parentOperationStartTime,
|
|
28079
|
+
onSubagentCreated,
|
|
28080
|
+
onSubagentCompleted
|
|
28081
|
+
} = options;
|
|
27923
28082
|
return (0, import_ai.tool)({
|
|
27924
28083
|
name: "delegate",
|
|
27925
28084
|
description: delegateDescription,
|
|
@@ -27963,9 +28122,30 @@ var init_vercel = __esm({
|
|
|
27963
28122
|
console.error(`Using workspace root: ${effectivePath} (cwd was: ${cwd || "not set"})`);
|
|
27964
28123
|
}
|
|
27965
28124
|
}
|
|
28125
|
+
let effectiveTimeout = timeout;
|
|
28126
|
+
if (parentOperationStartTime && maxOperationTimeout) {
|
|
28127
|
+
const elapsed = Date.now() - parentOperationStartTime;
|
|
28128
|
+
const remaining = maxOperationTimeout - elapsed;
|
|
28129
|
+
const budgetCap = Math.max(30, Math.floor(remaining * 0.9 / 1e3));
|
|
28130
|
+
if (budgetCap < effectiveTimeout) {
|
|
28131
|
+
effectiveTimeout = budgetCap;
|
|
28132
|
+
if (debug) {
|
|
28133
|
+
console.error(`[DELEGATE] Capping timeout from ${timeout}s to ${effectiveTimeout}s (remaining parent budget: ${Math.floor(remaining / 1e3)}s)`);
|
|
28134
|
+
}
|
|
28135
|
+
if (tracer) {
|
|
28136
|
+
tracer.addEvent("delegation.budget_capped", {
|
|
28137
|
+
"delegation.original_timeout_s": timeout,
|
|
28138
|
+
"delegation.effective_timeout_s": effectiveTimeout,
|
|
28139
|
+
"delegation.parent_elapsed_ms": elapsed,
|
|
28140
|
+
"delegation.parent_remaining_ms": remaining,
|
|
28141
|
+
"delegation.parent_session_id": parentSessionId
|
|
28142
|
+
});
|
|
28143
|
+
}
|
|
28144
|
+
}
|
|
28145
|
+
}
|
|
27966
28146
|
const result = await delegate({
|
|
27967
28147
|
task,
|
|
27968
|
-
timeout,
|
|
28148
|
+
timeout: effectiveTimeout,
|
|
27969
28149
|
debug,
|
|
27970
28150
|
currentIteration: currentIteration || 0,
|
|
27971
28151
|
maxIterations: maxIterations || 30,
|
|
@@ -27984,7 +28164,14 @@ var init_vercel = __esm({
|
|
|
27984
28164
|
mcpConfigPath,
|
|
27985
28165
|
delegationManager,
|
|
27986
28166
|
// Per-instance delegation limits
|
|
27987
|
-
parentAbortSignal
|
|
28167
|
+
parentAbortSignal,
|
|
28168
|
+
// Inherit timeout settings for subagent
|
|
28169
|
+
timeoutBehavior,
|
|
28170
|
+
requestTimeout,
|
|
28171
|
+
gracefulTimeoutBonusSteps,
|
|
28172
|
+
// Subagent lifecycle callbacks for graceful stop coordination
|
|
28173
|
+
onSubagentCreated,
|
|
28174
|
+
onSubagentCompleted
|
|
27988
28175
|
});
|
|
27989
28176
|
return result;
|
|
27990
28177
|
}
|
|
@@ -48935,6 +49122,16 @@ var init_fileTracker = __esm({
|
|
|
48935
49122
|
}
|
|
48936
49123
|
});
|
|
48937
49124
|
|
|
49125
|
+
// src/agent/otelLogBridge.js
|
|
49126
|
+
var import_module, _require;
|
|
49127
|
+
var init_otelLogBridge = __esm({
|
|
49128
|
+
"src/agent/otelLogBridge.js"() {
|
|
49129
|
+
"use strict";
|
|
49130
|
+
import_module = require("module");
|
|
49131
|
+
_require = (0, import_module.createRequire)("file:///");
|
|
49132
|
+
}
|
|
49133
|
+
});
|
|
49134
|
+
|
|
48938
49135
|
// src/agent/simpleTelemetry.js
|
|
48939
49136
|
var import_fs10, import_path11;
|
|
48940
49137
|
var init_simpleTelemetry = __esm({
|
|
@@ -48942,6 +49139,7 @@ var init_simpleTelemetry = __esm({
|
|
|
48942
49139
|
"use strict";
|
|
48943
49140
|
import_fs10 = require("fs");
|
|
48944
49141
|
import_path11 = require("path");
|
|
49142
|
+
init_otelLogBridge();
|
|
48945
49143
|
}
|
|
48946
49144
|
});
|
|
48947
49145
|
|
|
@@ -63420,7 +63618,22 @@ function mapFlowchartParserError(err, text) {
|
|
|
63420
63618
|
const lineContent = allLines[Math.max(0, line - 1)] || "";
|
|
63421
63619
|
const beforeQuote = lineContent.slice(0, Math.max(0, column - 1));
|
|
63422
63620
|
const hasLinkBefore = beforeQuote.match(/--\s*$|==\s*$|-\.\s*$|-\.-\s*$|\[\s*$/);
|
|
63621
|
+
const caret0 = Math.max(0, column - 1);
|
|
63622
|
+
const firstBar = lineContent.lastIndexOf("|", caret0);
|
|
63623
|
+
const secondBar = firstBar >= 0 ? lineContent.indexOf("|", caret0 + 1) : -1;
|
|
63624
|
+
const inPipeLabel = firstBar >= 0 && secondBar > firstBar && firstBar < caret0 && secondBar > caret0;
|
|
63423
63625
|
if (inLinkRule || hasLinkBefore) {
|
|
63626
|
+
if (tokType === "QuotedString" && inPipeLabel) {
|
|
63627
|
+
return {
|
|
63628
|
+
line,
|
|
63629
|
+
column,
|
|
63630
|
+
severity: "error",
|
|
63631
|
+
code: "FL-EDGE-LABEL-QUOTE-IN-PIPES",
|
|
63632
|
+
message: "Quotes are not supported inside pipe-delimited edge labels.",
|
|
63633
|
+
hint: "Use " inside |...|, e.g., --|e.g. "navigate to example.com"|-->",
|
|
63634
|
+
length: len
|
|
63635
|
+
};
|
|
63636
|
+
}
|
|
63424
63637
|
if (tokType === "DiamondOpen" || tokType === "DiamondClose") {
|
|
63425
63638
|
return {
|
|
63426
63639
|
line,
|
|
@@ -64022,17 +64235,6 @@ ${br.example}`,
|
|
|
64022
64235
|
if (inRule("arrow") && err.name === "NoViableAltException") {
|
|
64023
64236
|
return { line, column, severity: "error", code: "SE-ARROW-INVALID", message: `Invalid sequence arrow near '${found}'.`, hint: "Use ->, -->, ->>, -->>, -x, --x, -), --), <<->>, or <<-->>", length: len };
|
|
64024
64237
|
}
|
|
64025
|
-
if ((err.name === "NoViableAltException" || err.name === "MismatchedTokenException") && tokType === "Minus") {
|
|
64026
|
-
return {
|
|
64027
|
-
line,
|
|
64028
|
-
column,
|
|
64029
|
-
severity: "error",
|
|
64030
|
-
code: "SE-BULLET-LINE-UNSUPPORTED",
|
|
64031
|
-
message: "Bullet list lines starting with '-' are not supported in sequence diagrams.",
|
|
64032
|
-
hint: "Wrap free\u2011form text in a note block instead, for example:\nNote over A : Item 1\nNote over A\n - Item 1\n - Item 2\nend note",
|
|
64033
|
-
length: len
|
|
64034
|
-
};
|
|
64035
|
-
}
|
|
64036
64238
|
if (inRule("noteStmt")) {
|
|
64037
64239
|
if (err.name === "MismatchedTokenException" && exp("Colon")) {
|
|
64038
64240
|
return { line, column, severity: "error", code: "SE-NOTE-MALFORMED", message: "Malformed note: missing colon before the note text.", hint: "Example: Note right of Alice: Hello", length: len };
|
|
@@ -64041,6 +64243,22 @@ ${br.example}`,
|
|
|
64041
64243
|
return { line, column, severity: "error", code: "SE-NOTE-MALFORMED", message: "Malformed note statement. Use left|right of X or over X[,Y]: text", hint: "Examples: Note over A,B: hi", length: len };
|
|
64042
64244
|
}
|
|
64043
64245
|
}
|
|
64246
|
+
if ((err.name === "NoViableAltException" || err.name === "MismatchedTokenException") && tokType === "Minus") {
|
|
64247
|
+
const nonWs = ltxt.search(/\S/);
|
|
64248
|
+
const minusAtLineStart = nonWs >= 0 && ltxt[nonWs] === "-" && column === nonWs + 1;
|
|
64249
|
+
if (!minusAtLineStart) {
|
|
64250
|
+
} else {
|
|
64251
|
+
return {
|
|
64252
|
+
line,
|
|
64253
|
+
column,
|
|
64254
|
+
severity: "error",
|
|
64255
|
+
code: "SE-BULLET-LINE-UNSUPPORTED",
|
|
64256
|
+
message: "Bullet list lines starting with '-' are not supported in sequence diagrams.",
|
|
64257
|
+
hint: "Wrap free\u2011form text in a note block instead, for example:\nNote over A : Item 1\nNote over A\n - Item 1\n - Item 2\nend note",
|
|
64258
|
+
length: len
|
|
64259
|
+
};
|
|
64260
|
+
}
|
|
64261
|
+
}
|
|
64044
64262
|
if (tokType === "ElseKeyword" && isInRule(err, "criticalBlock")) {
|
|
64045
64263
|
return {
|
|
64046
64264
|
line,
|
|
@@ -64840,7 +65058,7 @@ var Identifier2, NumberLiteral3, SequenceKeyword, ParticipantKeyword, ActorKeywo
|
|
|
64840
65058
|
var init_lexer4 = __esm({
|
|
64841
65059
|
"node_modules/@probelabs/maid/out/diagrams/sequence/lexer.js"() {
|
|
64842
65060
|
init_api6();
|
|
64843
|
-
Identifier2 = createToken({ name: "Identifier", pattern: /[A-Za-z_][A-Za-z0-9_]*/ });
|
|
65061
|
+
Identifier2 = createToken({ name: "Identifier", pattern: /[A-Za-z_][A-Za-z0-9_.]*(?:-(?![>x)\s])[A-Za-z0-9_.]+)*/ });
|
|
64844
65062
|
NumberLiteral3 = createToken({ name: "NumberLiteral", pattern: /[0-9]+/ });
|
|
64845
65063
|
SequenceKeyword = createToken({ name: "SequenceKeyword", pattern: /sequenceDiagram/, longer_alt: Identifier2 });
|
|
64846
65064
|
ParticipantKeyword = createToken({ name: "ParticipantKeyword", pattern: /participant/i, longer_alt: Identifier2 });
|
|
@@ -66660,6 +66878,135 @@ var init_validate5 = __esm({
|
|
|
66660
66878
|
}
|
|
66661
66879
|
});
|
|
66662
66880
|
|
|
66881
|
+
// node_modules/@probelabs/maid/out/core/frontmatter.js
|
|
66882
|
+
function parseFrontmatter(input) {
|
|
66883
|
+
const text = input.startsWith("\uFEFF") ? input.slice(1) : input;
|
|
66884
|
+
const lines = text.split(/\r?\n/);
|
|
66885
|
+
if (lines.length < 3 || lines[0].trim() !== "---")
|
|
66886
|
+
return null;
|
|
66887
|
+
let i = 1;
|
|
66888
|
+
const block = [];
|
|
66889
|
+
while (i < lines.length && lines[i].trim() !== "---") {
|
|
66890
|
+
block.push(lines[i]);
|
|
66891
|
+
i++;
|
|
66892
|
+
}
|
|
66893
|
+
if (i >= lines.length)
|
|
66894
|
+
return null;
|
|
66895
|
+
const body = lines.slice(i + 1).join("\n");
|
|
66896
|
+
const bodyStartLine = i + 2;
|
|
66897
|
+
const raw = block.join("\n");
|
|
66898
|
+
const config2 = {};
|
|
66899
|
+
const themeVars = {};
|
|
66900
|
+
let themeUnderConfig = false;
|
|
66901
|
+
let ctx = "root";
|
|
66902
|
+
for (const line of block) {
|
|
66903
|
+
if (!line.trim())
|
|
66904
|
+
continue;
|
|
66905
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
66906
|
+
const mKey = line.match(/^\s*([A-Za-z0-9_\-]+):\s*(.*)$/);
|
|
66907
|
+
if (!mKey)
|
|
66908
|
+
continue;
|
|
66909
|
+
const key = mKey[1];
|
|
66910
|
+
let value = mKey[2] || "";
|
|
66911
|
+
if (indent === 0) {
|
|
66912
|
+
if (key === "config") {
|
|
66913
|
+
ctx = "config";
|
|
66914
|
+
continue;
|
|
66915
|
+
}
|
|
66916
|
+
if (key === "themeVariables") {
|
|
66917
|
+
ctx = "theme";
|
|
66918
|
+
continue;
|
|
66919
|
+
}
|
|
66920
|
+
ctx = "root";
|
|
66921
|
+
continue;
|
|
66922
|
+
}
|
|
66923
|
+
if (ctx === "config") {
|
|
66924
|
+
if (indent <= 2 && key !== "pie" && key !== "themeVariables")
|
|
66925
|
+
continue;
|
|
66926
|
+
if (key === "pie") {
|
|
66927
|
+
ctx = "config.pie";
|
|
66928
|
+
ensure(config2, "pie", {});
|
|
66929
|
+
continue;
|
|
66930
|
+
}
|
|
66931
|
+
if (key === "themeVariables") {
|
|
66932
|
+
ctx = "theme";
|
|
66933
|
+
themeUnderConfig = true;
|
|
66934
|
+
continue;
|
|
66935
|
+
}
|
|
66936
|
+
continue;
|
|
66937
|
+
}
|
|
66938
|
+
if (ctx === "config.pie") {
|
|
66939
|
+
if (indent < 4) {
|
|
66940
|
+
if (key === "pie") {
|
|
66941
|
+
ctx = "config.pie";
|
|
66942
|
+
ensure(config2, "pie", {});
|
|
66943
|
+
continue;
|
|
66944
|
+
}
|
|
66945
|
+
if (key === "themeVariables") {
|
|
66946
|
+
ctx = "theme";
|
|
66947
|
+
themeUnderConfig = true;
|
|
66948
|
+
continue;
|
|
66949
|
+
}
|
|
66950
|
+
ctx = "config";
|
|
66951
|
+
continue;
|
|
66952
|
+
}
|
|
66953
|
+
setKV(config2.pie, key, value);
|
|
66954
|
+
continue;
|
|
66955
|
+
}
|
|
66956
|
+
if (ctx === "theme") {
|
|
66957
|
+
if (indent < 2) {
|
|
66958
|
+
ctx = "root";
|
|
66959
|
+
continue;
|
|
66960
|
+
}
|
|
66961
|
+
setKV(themeVars, key, value);
|
|
66962
|
+
continue;
|
|
66963
|
+
}
|
|
66964
|
+
}
|
|
66965
|
+
if (themeUnderConfig && Object.keys(themeVars).length) {
|
|
66966
|
+
ensure(config2, "themeVariables", {});
|
|
66967
|
+
Object.assign(config2.themeVariables, themeVars);
|
|
66968
|
+
}
|
|
66969
|
+
return {
|
|
66970
|
+
raw,
|
|
66971
|
+
body,
|
|
66972
|
+
bodyStartLine,
|
|
66973
|
+
config: Object.keys(config2).length ? config2 : void 0,
|
|
66974
|
+
themeVariables: Object.keys(themeVars).length ? themeVars : void 0
|
|
66975
|
+
};
|
|
66976
|
+
}
|
|
66977
|
+
function ensure(obj, key, def) {
|
|
66978
|
+
if (obj[key] == null)
|
|
66979
|
+
obj[key] = def;
|
|
66980
|
+
}
|
|
66981
|
+
function unquote(val) {
|
|
66982
|
+
const v = val.trim();
|
|
66983
|
+
if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'")) {
|
|
66984
|
+
return v.slice(1, -1);
|
|
66985
|
+
}
|
|
66986
|
+
return v;
|
|
66987
|
+
}
|
|
66988
|
+
function setKV(target, key, rawValue) {
|
|
66989
|
+
const v = unquote(rawValue);
|
|
66990
|
+
if (v === "") {
|
|
66991
|
+
target[key] = "";
|
|
66992
|
+
return;
|
|
66993
|
+
}
|
|
66994
|
+
const num = Number(v);
|
|
66995
|
+
if (!Number.isNaN(num) && /^-?[0-9]+(\.[0-9]+)?$/.test(v)) {
|
|
66996
|
+
target[key] = num;
|
|
66997
|
+
return;
|
|
66998
|
+
}
|
|
66999
|
+
if (/^(true|false)$/i.test(v)) {
|
|
67000
|
+
target[key] = /^true$/i.test(v);
|
|
67001
|
+
return;
|
|
67002
|
+
}
|
|
67003
|
+
target[key] = v;
|
|
67004
|
+
}
|
|
67005
|
+
var init_frontmatter = __esm({
|
|
67006
|
+
"node_modules/@probelabs/maid/out/core/frontmatter.js"() {
|
|
67007
|
+
}
|
|
67008
|
+
});
|
|
67009
|
+
|
|
66663
67010
|
// node_modules/@probelabs/maid/out/core/router.js
|
|
66664
67011
|
function firstNonCommentLine(text) {
|
|
66665
67012
|
const lines = text.split(/\r?\n/);
|
|
@@ -66674,7 +67021,8 @@ function firstNonCommentLine(text) {
|
|
|
66674
67021
|
return void 0;
|
|
66675
67022
|
}
|
|
66676
67023
|
function detectDiagramType(text) {
|
|
66677
|
-
const
|
|
67024
|
+
const { content } = stripFrontmatter(text);
|
|
67025
|
+
const header = firstNonCommentLine(content);
|
|
66678
67026
|
if (!header)
|
|
66679
67027
|
return "unknown";
|
|
66680
67028
|
if (/^(flowchart|graph)\b/i.test(header))
|
|
@@ -66726,20 +67074,26 @@ function isOtherMermaidDiagram(headerLine) {
|
|
|
66726
67074
|
return OTHER.has(t);
|
|
66727
67075
|
}
|
|
66728
67076
|
function validate(text, options = {}) {
|
|
66729
|
-
const
|
|
67077
|
+
const { content, lineOffset } = stripFrontmatter(text);
|
|
67078
|
+
const type = detectDiagramType(content);
|
|
67079
|
+
const withOffset = (errors) => {
|
|
67080
|
+
if (lineOffset === 0)
|
|
67081
|
+
return errors;
|
|
67082
|
+
return errors.map((e) => ({ ...e, line: Math.max(1, (e.line || 1) + lineOffset) }));
|
|
67083
|
+
};
|
|
66730
67084
|
switch (type) {
|
|
66731
67085
|
case "flowchart":
|
|
66732
|
-
return { type, errors: validateFlowchart(
|
|
67086
|
+
return { type, errors: withOffset(validateFlowchart(content, options)) };
|
|
66733
67087
|
case "pie":
|
|
66734
|
-
return { type, errors: validatePie(
|
|
67088
|
+
return { type, errors: withOffset(validatePie(content, options)) };
|
|
66735
67089
|
case "sequence":
|
|
66736
|
-
return { type, errors: validateSequence(
|
|
67090
|
+
return { type, errors: withOffset(validateSequence(content, options)) };
|
|
66737
67091
|
case "class":
|
|
66738
|
-
return { type, errors: validateClass(
|
|
67092
|
+
return { type, errors: withOffset(validateClass(content, options)) };
|
|
66739
67093
|
case "state":
|
|
66740
|
-
return { type, errors: validateState(
|
|
67094
|
+
return { type, errors: withOffset(validateState(content, options)) };
|
|
66741
67095
|
default:
|
|
66742
|
-
const header = firstNonCommentLine(
|
|
67096
|
+
const header = firstNonCommentLine(content);
|
|
66743
67097
|
if (isOtherMermaidDiagram(header)) {
|
|
66744
67098
|
return { type, errors: [] };
|
|
66745
67099
|
}
|
|
@@ -66747,7 +67101,7 @@ function validate(text, options = {}) {
|
|
|
66747
67101
|
type,
|
|
66748
67102
|
errors: [
|
|
66749
67103
|
{
|
|
66750
|
-
line: 1,
|
|
67104
|
+
line: lineOffset + 1,
|
|
66751
67105
|
column: 1,
|
|
66752
67106
|
message: 'Diagram must start with "graph", "flowchart", "pie", "sequenceDiagram", "classDiagram" or "stateDiagram[-v2]"',
|
|
66753
67107
|
severity: "error",
|
|
@@ -66758,6 +67112,12 @@ function validate(text, options = {}) {
|
|
|
66758
67112
|
};
|
|
66759
67113
|
}
|
|
66760
67114
|
}
|
|
67115
|
+
function stripFrontmatter(text) {
|
|
67116
|
+
const fm = parseFrontmatter(text);
|
|
67117
|
+
if (!fm)
|
|
67118
|
+
return { content: text, lineOffset: 0 };
|
|
67119
|
+
return { content: fm.body, lineOffset: fm.bodyStartLine - 1 };
|
|
67120
|
+
}
|
|
66761
67121
|
var init_router = __esm({
|
|
66762
67122
|
"node_modules/@probelabs/maid/out/core/router.js"() {
|
|
66763
67123
|
init_validate();
|
|
@@ -66765,6 +67125,7 @@ var init_router = __esm({
|
|
|
66765
67125
|
init_validate3();
|
|
66766
67126
|
init_validate4();
|
|
66767
67127
|
init_validate5();
|
|
67128
|
+
init_frontmatter();
|
|
66768
67129
|
}
|
|
66769
67130
|
});
|
|
66770
67131
|
|
|
@@ -66976,16 +67337,18 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
66976
67337
|
}
|
|
66977
67338
|
continue;
|
|
66978
67339
|
}
|
|
66979
|
-
if (is("FL-EDGE-LABEL-BRACKET", e) || is("FL-EDGE-LABEL-CURLY-IN-PIPES", e)) {
|
|
67340
|
+
if (is("FL-EDGE-LABEL-BRACKET", e) || is("FL-EDGE-LABEL-CURLY-IN-PIPES", e) || is("FL-EDGE-LABEL-QUOTE-IN-PIPES", e)) {
|
|
66980
67341
|
const lineText = lineTextAt(text, e.line);
|
|
66981
|
-
const
|
|
66982
|
-
const
|
|
67342
|
+
const col = Math.max(0, e.column - 1);
|
|
67343
|
+
const firstBar = lineText.lastIndexOf("|", col);
|
|
67344
|
+
const secondBar = firstBar >= 0 ? lineText.indexOf("|", col + 1) : -1;
|
|
66983
67345
|
if (firstBar >= 0 && secondBar > firstBar) {
|
|
66984
67346
|
const before = lineText.slice(0, firstBar + 1);
|
|
66985
67347
|
const label = lineText.slice(firstBar + 1, secondBar);
|
|
66986
67348
|
const after = lineText.slice(secondBar);
|
|
66987
67349
|
let fixedLabel = label.replace(/\[/g, "[").replace(/\]/g, "]");
|
|
66988
67350
|
fixedLabel = fixedLabel.replace(/\{/g, "{").replace(/\}/g, "}");
|
|
67351
|
+
fixedLabel = fixedLabel.replace(/\\"/g, """).replace(/"/g, """);
|
|
66989
67352
|
const fixedLine = before + fixedLabel + after;
|
|
66990
67353
|
const finalLine = fixedLine.replace(/\[([^\]]*)\]/g, (m, seg) => "[" + String(seg).replace(/`/g, "") + "]");
|
|
66991
67354
|
edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: lineText.length + 1 }, newText: finalLine });
|
|
@@ -78375,7 +78738,7 @@ ${overlay}</g>`;
|
|
|
78375
78738
|
});
|
|
78376
78739
|
|
|
78377
78740
|
// node_modules/@probelabs/maid/out/renderer/pie-builder.js
|
|
78378
|
-
function
|
|
78741
|
+
function unquote2(s) {
|
|
78379
78742
|
if (!s)
|
|
78380
78743
|
return s;
|
|
78381
78744
|
const first2 = s.charAt(0);
|
|
@@ -78425,7 +78788,7 @@ function buildPieModel(text) {
|
|
|
78425
78788
|
const collect = (k) => {
|
|
78426
78789
|
const arr = tnode.children?.[k] ?? [];
|
|
78427
78790
|
for (const tok of arr)
|
|
78428
|
-
parts.push(
|
|
78791
|
+
parts.push(unquote2(tok.image));
|
|
78429
78792
|
};
|
|
78430
78793
|
collect("QuotedString");
|
|
78431
78794
|
collect("Text");
|
|
@@ -78438,7 +78801,7 @@ function buildPieModel(text) {
|
|
|
78438
78801
|
const labelTok = snode.children?.sliceLabel?.[0]?.children?.QuotedString?.[0];
|
|
78439
78802
|
const numTok = snode.children?.NumberLiteral?.[0];
|
|
78440
78803
|
if (labelTok && numTok) {
|
|
78441
|
-
const label =
|
|
78804
|
+
const label = unquote2(labelTok.image).trim();
|
|
78442
78805
|
const value = Number(numTok.image);
|
|
78443
78806
|
if (!Number.isNaN(value)) {
|
|
78444
78807
|
model.slices.push({ label, value });
|
|
@@ -79594,128 +79957,6 @@ var init_state_renderer = __esm({
|
|
|
79594
79957
|
}
|
|
79595
79958
|
});
|
|
79596
79959
|
|
|
79597
|
-
// node_modules/@probelabs/maid/out/core/frontmatter.js
|
|
79598
|
-
function parseFrontmatter(input) {
|
|
79599
|
-
const text = input.startsWith("\uFEFF") ? input.slice(1) : input;
|
|
79600
|
-
const lines = text.split(/\r?\n/);
|
|
79601
|
-
if (lines.length < 3 || lines[0].trim() !== "---")
|
|
79602
|
-
return null;
|
|
79603
|
-
let i = 1;
|
|
79604
|
-
const block = [];
|
|
79605
|
-
while (i < lines.length && lines[i].trim() !== "---") {
|
|
79606
|
-
block.push(lines[i]);
|
|
79607
|
-
i++;
|
|
79608
|
-
}
|
|
79609
|
-
if (i >= lines.length)
|
|
79610
|
-
return null;
|
|
79611
|
-
const body = lines.slice(i + 1).join("\n");
|
|
79612
|
-
const raw = block.join("\n");
|
|
79613
|
-
const config2 = {};
|
|
79614
|
-
const themeVars = {};
|
|
79615
|
-
let themeUnderConfig = false;
|
|
79616
|
-
let ctx = "root";
|
|
79617
|
-
for (const line of block) {
|
|
79618
|
-
if (!line.trim())
|
|
79619
|
-
continue;
|
|
79620
|
-
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
79621
|
-
const mKey = line.match(/^\s*([A-Za-z0-9_\-]+):\s*(.*)$/);
|
|
79622
|
-
if (!mKey)
|
|
79623
|
-
continue;
|
|
79624
|
-
const key = mKey[1];
|
|
79625
|
-
let value = mKey[2] || "";
|
|
79626
|
-
if (indent === 0) {
|
|
79627
|
-
if (key === "config") {
|
|
79628
|
-
ctx = "config";
|
|
79629
|
-
continue;
|
|
79630
|
-
}
|
|
79631
|
-
if (key === "themeVariables") {
|
|
79632
|
-
ctx = "theme";
|
|
79633
|
-
continue;
|
|
79634
|
-
}
|
|
79635
|
-
ctx = "root";
|
|
79636
|
-
continue;
|
|
79637
|
-
}
|
|
79638
|
-
if (ctx === "config") {
|
|
79639
|
-
if (indent <= 2 && key !== "pie" && key !== "themeVariables")
|
|
79640
|
-
continue;
|
|
79641
|
-
if (key === "pie") {
|
|
79642
|
-
ctx = "config.pie";
|
|
79643
|
-
ensure(config2, "pie", {});
|
|
79644
|
-
continue;
|
|
79645
|
-
}
|
|
79646
|
-
if (key === "themeVariables") {
|
|
79647
|
-
ctx = "theme";
|
|
79648
|
-
themeUnderConfig = true;
|
|
79649
|
-
continue;
|
|
79650
|
-
}
|
|
79651
|
-
continue;
|
|
79652
|
-
}
|
|
79653
|
-
if (ctx === "config.pie") {
|
|
79654
|
-
if (indent < 4) {
|
|
79655
|
-
if (key === "pie") {
|
|
79656
|
-
ctx = "config.pie";
|
|
79657
|
-
ensure(config2, "pie", {});
|
|
79658
|
-
continue;
|
|
79659
|
-
}
|
|
79660
|
-
if (key === "themeVariables") {
|
|
79661
|
-
ctx = "theme";
|
|
79662
|
-
themeUnderConfig = true;
|
|
79663
|
-
continue;
|
|
79664
|
-
}
|
|
79665
|
-
ctx = "config";
|
|
79666
|
-
continue;
|
|
79667
|
-
}
|
|
79668
|
-
setKV(config2.pie, key, value);
|
|
79669
|
-
continue;
|
|
79670
|
-
}
|
|
79671
|
-
if (ctx === "theme") {
|
|
79672
|
-
if (indent < 2) {
|
|
79673
|
-
ctx = "root";
|
|
79674
|
-
continue;
|
|
79675
|
-
}
|
|
79676
|
-
setKV(themeVars, key, value);
|
|
79677
|
-
continue;
|
|
79678
|
-
}
|
|
79679
|
-
}
|
|
79680
|
-
if (themeUnderConfig && Object.keys(themeVars).length) {
|
|
79681
|
-
ensure(config2, "themeVariables", {});
|
|
79682
|
-
Object.assign(config2.themeVariables, themeVars);
|
|
79683
|
-
}
|
|
79684
|
-
return { raw, body, config: Object.keys(config2).length ? config2 : void 0, themeVariables: Object.keys(themeVars).length ? themeVars : void 0 };
|
|
79685
|
-
}
|
|
79686
|
-
function ensure(obj, key, def) {
|
|
79687
|
-
if (obj[key] == null)
|
|
79688
|
-
obj[key] = def;
|
|
79689
|
-
}
|
|
79690
|
-
function unquote2(val) {
|
|
79691
|
-
const v = val.trim();
|
|
79692
|
-
if (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'")) {
|
|
79693
|
-
return v.slice(1, -1);
|
|
79694
|
-
}
|
|
79695
|
-
return v;
|
|
79696
|
-
}
|
|
79697
|
-
function setKV(target, key, rawValue) {
|
|
79698
|
-
const v = unquote2(rawValue);
|
|
79699
|
-
if (v === "") {
|
|
79700
|
-
target[key] = "";
|
|
79701
|
-
return;
|
|
79702
|
-
}
|
|
79703
|
-
const num = Number(v);
|
|
79704
|
-
if (!Number.isNaN(num) && /^-?[0-9]+(\.[0-9]+)?$/.test(v)) {
|
|
79705
|
-
target[key] = num;
|
|
79706
|
-
return;
|
|
79707
|
-
}
|
|
79708
|
-
if (/^(true|false)$/i.test(v)) {
|
|
79709
|
-
target[key] = /^true$/i.test(v);
|
|
79710
|
-
return;
|
|
79711
|
-
}
|
|
79712
|
-
target[key] = v;
|
|
79713
|
-
}
|
|
79714
|
-
var init_frontmatter = __esm({
|
|
79715
|
-
"node_modules/@probelabs/maid/out/core/frontmatter.js"() {
|
|
79716
|
-
}
|
|
79717
|
-
});
|
|
79718
|
-
|
|
79719
79960
|
// node_modules/@probelabs/maid/out/renderer/class-builder.js
|
|
79720
79961
|
function textFromTokens3(tokens) {
|
|
79721
79962
|
if (!tokens || tokens.length === 0)
|
|
@@ -88952,6 +89193,9 @@ function isMethodAllowed(methodName, allowedMethods, blockedMethods) {
|
|
|
88952
89193
|
}
|
|
88953
89194
|
function createTransport(serverConfig) {
|
|
88954
89195
|
const { transport, command, args, url: url2, env } = serverConfig;
|
|
89196
|
+
if (serverConfig.transportInstance) {
|
|
89197
|
+
return serverConfig.transportInstance;
|
|
89198
|
+
}
|
|
88955
89199
|
switch (transport) {
|
|
88956
89200
|
case "stdio":
|
|
88957
89201
|
return new import_stdio.StdioClientTransport({
|
|
@@ -89035,6 +89279,7 @@ var init_client = __esm({
|
|
|
89035
89279
|
this.debug = options.debug || process.env.DEBUG_MCP === "1";
|
|
89036
89280
|
this.config = null;
|
|
89037
89281
|
this.tracer = options.tracer || null;
|
|
89282
|
+
this.agentEvents = options.agentEvents || null;
|
|
89038
89283
|
}
|
|
89039
89284
|
/**
|
|
89040
89285
|
* Record an MCP telemetry event if tracer is available
|
|
@@ -89262,11 +89507,21 @@ var init_client = __esm({
|
|
|
89262
89507
|
throw new Error(`Server ${tool6.serverName} not connected`);
|
|
89263
89508
|
}
|
|
89264
89509
|
const startTime = Date.now();
|
|
89510
|
+
const toolCallId = `mcp-${toolName}-${startTime}`;
|
|
89265
89511
|
this.recordMcpEvent("tool.call_started", {
|
|
89266
89512
|
toolName,
|
|
89267
89513
|
serverName: tool6.serverName,
|
|
89268
89514
|
originalToolName: tool6.originalName
|
|
89269
89515
|
});
|
|
89516
|
+
if (this.agentEvents) {
|
|
89517
|
+
this.agentEvents.emit("toolCall", {
|
|
89518
|
+
toolCallId,
|
|
89519
|
+
name: toolName,
|
|
89520
|
+
args,
|
|
89521
|
+
status: "started",
|
|
89522
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
89523
|
+
});
|
|
89524
|
+
}
|
|
89270
89525
|
try {
|
|
89271
89526
|
if (this.debug) {
|
|
89272
89527
|
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
@@ -89296,6 +89551,14 @@ var init_client = __esm({
|
|
|
89296
89551
|
originalToolName: tool6.originalName,
|
|
89297
89552
|
durationMs
|
|
89298
89553
|
});
|
|
89554
|
+
if (this.agentEvents) {
|
|
89555
|
+
this.agentEvents.emit("toolCall", {
|
|
89556
|
+
toolCallId,
|
|
89557
|
+
name: toolName,
|
|
89558
|
+
status: "completed",
|
|
89559
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
89560
|
+
});
|
|
89561
|
+
}
|
|
89299
89562
|
return result;
|
|
89300
89563
|
} catch (error40) {
|
|
89301
89564
|
const durationMs = Date.now() - startTime;
|
|
@@ -89311,9 +89574,64 @@ var init_client = __esm({
|
|
|
89311
89574
|
durationMs,
|
|
89312
89575
|
isTimeout: error40.message.includes("timeout")
|
|
89313
89576
|
});
|
|
89577
|
+
if (this.agentEvents) {
|
|
89578
|
+
this.agentEvents.emit("toolCall", {
|
|
89579
|
+
toolCallId,
|
|
89580
|
+
name: toolName,
|
|
89581
|
+
status: "error",
|
|
89582
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
89583
|
+
});
|
|
89584
|
+
}
|
|
89314
89585
|
throw error40;
|
|
89315
89586
|
}
|
|
89316
89587
|
}
|
|
89588
|
+
/**
|
|
89589
|
+
* Call graceful_stop on all MCP servers that expose it.
|
|
89590
|
+
* This signals agent-type MCP servers to wrap up their work.
|
|
89591
|
+
* @returns {Promise<Array<{server: string, success: boolean, error?: string}>>}
|
|
89592
|
+
*/
|
|
89593
|
+
async callGracefulStopAll() {
|
|
89594
|
+
const results = [];
|
|
89595
|
+
for (const [serverName, clientInfo] of this.clients) {
|
|
89596
|
+
const qualifiedName = `${serverName}_graceful_stop`;
|
|
89597
|
+
if (this.tools.has(qualifiedName)) {
|
|
89598
|
+
if (this.debug) {
|
|
89599
|
+
console.log(`[DEBUG] MCP callGracefulStopAll: calling graceful_stop on server "${serverName}"`);
|
|
89600
|
+
}
|
|
89601
|
+
try {
|
|
89602
|
+
const timeoutMs = 5e3;
|
|
89603
|
+
const timeoutPromise = new Promise(
|
|
89604
|
+
(_, reject2) => setTimeout(() => reject2(new Error("graceful_stop timeout")), timeoutMs)
|
|
89605
|
+
);
|
|
89606
|
+
await Promise.race([
|
|
89607
|
+
clientInfo.client.callTool({ name: "graceful_stop", arguments: {} }, void 0, { timeout: timeoutMs }),
|
|
89608
|
+
timeoutPromise
|
|
89609
|
+
]);
|
|
89610
|
+
results.push({ server: serverName, success: true });
|
|
89611
|
+
if (this.debug) {
|
|
89612
|
+
console.log(`[DEBUG] MCP callGracefulStopAll: server "${serverName}" acknowledged graceful_stop`);
|
|
89613
|
+
}
|
|
89614
|
+
} catch (e) {
|
|
89615
|
+
results.push({ server: serverName, success: false, error: e.message });
|
|
89616
|
+
if (this.debug) {
|
|
89617
|
+
console.log(`[DEBUG] MCP callGracefulStopAll: server "${serverName}" graceful_stop failed: ${e.message}`);
|
|
89618
|
+
}
|
|
89619
|
+
}
|
|
89620
|
+
}
|
|
89621
|
+
}
|
|
89622
|
+
if (this.debug) {
|
|
89623
|
+
const withStop = results.length;
|
|
89624
|
+
const total = this.clients.size;
|
|
89625
|
+
console.log(`[DEBUG] MCP callGracefulStopAll: ${withStop}/${total} servers had graceful_stop tool`);
|
|
89626
|
+
}
|
|
89627
|
+
this.recordMcpEvent("graceful_stop.sweep_completed", {
|
|
89628
|
+
servers_total: this.clients.size,
|
|
89629
|
+
servers_with_graceful_stop: results.length,
|
|
89630
|
+
servers_acknowledged: results.filter((r) => r.success).length,
|
|
89631
|
+
servers_failed: results.filter((r) => !r.success).length
|
|
89632
|
+
});
|
|
89633
|
+
return results;
|
|
89634
|
+
}
|
|
89317
89635
|
/**
|
|
89318
89636
|
* Get all available tools with their schemas
|
|
89319
89637
|
* @returns {Object} Map of tool name to tool definition
|
|
@@ -89341,10 +89659,29 @@ var init_client = __esm({
|
|
|
89341
89659
|
inputSchema: tool6.inputSchema,
|
|
89342
89660
|
execute: async (args) => {
|
|
89343
89661
|
const result = await this.callTool(name15, args);
|
|
89344
|
-
if (result.content
|
|
89345
|
-
return result
|
|
89662
|
+
if (!result.content || !result.content[0]) {
|
|
89663
|
+
return JSON.stringify(result);
|
|
89664
|
+
}
|
|
89665
|
+
const hasImage = result.content.some((block) => block.type === "image");
|
|
89666
|
+
if (hasImage) {
|
|
89667
|
+
return { _mcpContent: result.content };
|
|
89668
|
+
}
|
|
89669
|
+
return result.content[0].text;
|
|
89670
|
+
},
|
|
89671
|
+
// Convert MCP content blocks (including images) to Vercel AI SDK format
|
|
89672
|
+
toModelOutput: ({ output }) => {
|
|
89673
|
+
if (output && typeof output === "object" && output._mcpContent) {
|
|
89674
|
+
const parts = [];
|
|
89675
|
+
for (const block of output._mcpContent) {
|
|
89676
|
+
if (block.type === "text") {
|
|
89677
|
+
parts.push({ type: "text", text: block.text });
|
|
89678
|
+
} else if (block.type === "image") {
|
|
89679
|
+
parts.push({ type: "image-data", data: block.data, mediaType: block.mimeType });
|
|
89680
|
+
}
|
|
89681
|
+
}
|
|
89682
|
+
return { type: "content", value: parts };
|
|
89346
89683
|
}
|
|
89347
|
-
return JSON.stringify(
|
|
89684
|
+
return { type: "text", value: typeof output === "string" ? output : JSON.stringify(output) };
|
|
89348
89685
|
}
|
|
89349
89686
|
};
|
|
89350
89687
|
}
|
|
@@ -89430,6 +89767,7 @@ var init_xmlBridge = __esm({
|
|
|
89430
89767
|
constructor(options = {}) {
|
|
89431
89768
|
this.debug = options.debug || false;
|
|
89432
89769
|
this.tracer = options.tracer || null;
|
|
89770
|
+
this.agentEvents = options.agentEvents || null;
|
|
89433
89771
|
this.mcpTools = {};
|
|
89434
89772
|
this.mcpManager = null;
|
|
89435
89773
|
this.toolDescriptions = {};
|
|
@@ -89472,7 +89810,7 @@ var init_xmlBridge = __esm({
|
|
|
89472
89810
|
if (this.debug) {
|
|
89473
89811
|
console.error("[MCP DEBUG] Initializing MCP client manager...");
|
|
89474
89812
|
}
|
|
89475
|
-
this.mcpManager = new MCPClientManager({ debug: this.debug, tracer: this.tracer });
|
|
89813
|
+
this.mcpManager = new MCPClientManager({ debug: this.debug, tracer: this.tracer, agentEvents: this.agentEvents });
|
|
89476
89814
|
const result = await this.mcpManager.initialize(mcpConfigs);
|
|
89477
89815
|
const vercelTools = this.mcpManager.getVercelTools();
|
|
89478
89816
|
this.mcpTools = vercelTools;
|
|
@@ -89524,6 +89862,16 @@ var init_xmlBridge = __esm({
|
|
|
89524
89862
|
isMcpTool(toolName) {
|
|
89525
89863
|
return toolName in this.mcpTools;
|
|
89526
89864
|
}
|
|
89865
|
+
/**
|
|
89866
|
+
* Call graceful_stop on all MCP servers that expose it.
|
|
89867
|
+
* @returns {Promise<Array>}
|
|
89868
|
+
*/
|
|
89869
|
+
async callGracefulStopAll() {
|
|
89870
|
+
if (this.mcpManager) {
|
|
89871
|
+
return this.mcpManager.callGracefulStopAll();
|
|
89872
|
+
}
|
|
89873
|
+
return [];
|
|
89874
|
+
}
|
|
89527
89875
|
/**
|
|
89528
89876
|
* Clean up MCP connections
|
|
89529
89877
|
*/
|
|
@@ -96909,7 +97257,7 @@ function deriveDescription(rawDescription, body) {
|
|
|
96909
97257
|
}
|
|
96910
97258
|
return truncateDescription(description);
|
|
96911
97259
|
}
|
|
96912
|
-
function
|
|
97260
|
+
function stripFrontmatter2(content) {
|
|
96913
97261
|
const { body } = extractFrontmatter(content);
|
|
96914
97262
|
return body.trim();
|
|
96915
97263
|
}
|
|
@@ -97050,7 +97398,7 @@ var init_registry = __esm({
|
|
|
97050
97398
|
const skill = this.skillsByName.get(name15);
|
|
97051
97399
|
if (!skill) return null;
|
|
97052
97400
|
const content = await (0, import_promises3.readFile)(skill.skillFilePath, "utf8");
|
|
97053
|
-
return
|
|
97401
|
+
return stripFrontmatter2(content);
|
|
97054
97402
|
}
|
|
97055
97403
|
async _resolveRealPath(target) {
|
|
97056
97404
|
try {
|
|
@@ -99762,6 +100110,7 @@ var init_ProbeAgent = __esm({
|
|
|
99762
100110
|
this.debug = options.debug || process.env.DEBUG === "1";
|
|
99763
100111
|
this.cancelled = false;
|
|
99764
100112
|
this._abortController = new AbortController();
|
|
100113
|
+
this._activeSubagents = /* @__PURE__ */ new Map();
|
|
99765
100114
|
this.tracer = options.tracer || null;
|
|
99766
100115
|
this.outline = !!options.outline;
|
|
99767
100116
|
this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
|
|
@@ -99873,14 +100222,31 @@ var init_ProbeAgent = __esm({
|
|
|
99873
100222
|
this.timeoutBehavior = options.timeoutBehavior ?? (() => {
|
|
99874
100223
|
const val = process.env.TIMEOUT_BEHAVIOR;
|
|
99875
100224
|
if (val === "hard") return "hard";
|
|
100225
|
+
if (val === "negotiated") return "negotiated";
|
|
99876
100226
|
return "graceful";
|
|
99877
100227
|
})();
|
|
99878
100228
|
this.gracefulTimeoutBonusSteps = options.gracefulTimeoutBonusSteps ?? (() => {
|
|
99879
100229
|
const parsed = parseInt(process.env.GRACEFUL_TIMEOUT_BONUS_STEPS, 10);
|
|
99880
100230
|
return isNaN(parsed) || parsed < 1 || parsed > 20 ? 4 : parsed;
|
|
99881
100231
|
})();
|
|
100232
|
+
this.negotiatedTimeoutBudget = options.negotiatedTimeoutBudget ?? (() => {
|
|
100233
|
+
const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_BUDGET, 10);
|
|
100234
|
+
return isNaN(parsed) || parsed < 6e4 || parsed > 72e5 ? 18e5 : parsed;
|
|
100235
|
+
})();
|
|
100236
|
+
this.negotiatedTimeoutMaxRequests = options.negotiatedTimeoutMaxRequests ?? (() => {
|
|
100237
|
+
const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_MAX_REQUESTS, 10);
|
|
100238
|
+
return isNaN(parsed) || parsed < 1 || parsed > 10 ? 3 : parsed;
|
|
100239
|
+
})();
|
|
100240
|
+
this.negotiatedTimeoutMaxPerRequest = options.negotiatedTimeoutMaxPerRequest ?? (() => {
|
|
100241
|
+
const parsed = parseInt(process.env.NEGOTIATED_TIMEOUT_MAX_PER_REQUEST, 10);
|
|
100242
|
+
return isNaN(parsed) || parsed < 6e4 || parsed > 36e5 ? 6e5 : parsed;
|
|
100243
|
+
})();
|
|
100244
|
+
this.gracefulStopDeadline = options.gracefulStopDeadline ?? (() => {
|
|
100245
|
+
const parsed = parseInt(process.env.GRACEFUL_STOP_DEADLINE, 10);
|
|
100246
|
+
return isNaN(parsed) || parsed < 5e3 || parsed > 3e5 ? 45e3 : parsed;
|
|
100247
|
+
})();
|
|
99882
100248
|
if (this.debug) {
|
|
99883
|
-
console.log(`[DEBUG] Timeout behavior: ${this.timeoutBehavior}, bonus steps: ${this.gracefulTimeoutBonusSteps}`);
|
|
100249
|
+
console.log(`[DEBUG] Timeout behavior: ${this.timeoutBehavior}, bonus steps: ${this.gracefulTimeoutBonusSteps}, graceful stop deadline: ${this.gracefulStopDeadline}ms`);
|
|
99884
100250
|
}
|
|
99885
100251
|
this.retryConfig = options.retry || {};
|
|
99886
100252
|
this.retryManager = null;
|
|
@@ -100232,6 +100598,18 @@ var init_ProbeAgent = __esm({
|
|
|
100232
100598
|
// Per-instance delegation limits
|
|
100233
100599
|
parentAbortSignal: this._abortController.signal,
|
|
100234
100600
|
// Propagate cancellation to delegations
|
|
100601
|
+
// Timeout settings for delegate subagents to inherit
|
|
100602
|
+
timeoutBehavior: this.timeoutBehavior,
|
|
100603
|
+
maxOperationTimeout: this.maxOperationTimeout,
|
|
100604
|
+
requestTimeout: this.requestTimeout,
|
|
100605
|
+
gracefulTimeoutBonusSteps: this.gracefulTimeoutBonusSteps,
|
|
100606
|
+
negotiatedTimeoutBudget: this.negotiatedTimeoutBudget,
|
|
100607
|
+
negotiatedTimeoutMaxRequests: this.negotiatedTimeoutMaxRequests,
|
|
100608
|
+
negotiatedTimeoutMaxPerRequest: this.negotiatedTimeoutMaxPerRequest,
|
|
100609
|
+
parentOperationStartTime: this._operationStartTime,
|
|
100610
|
+
// For remaining budget calculation
|
|
100611
|
+
onSubagentCreated: (sid, subagent) => this._registerSubagent(sid, subagent),
|
|
100612
|
+
onSubagentCompleted: (sid) => this._unregisterSubagent(sid),
|
|
100235
100613
|
outputBuffer: this._outputBuffer,
|
|
100236
100614
|
concurrencyLimiter: this.concurrencyLimiter,
|
|
100237
100615
|
// Global AI concurrency limiter
|
|
@@ -100816,7 +101194,7 @@ var init_ProbeAgent = __esm({
|
|
|
100816
101194
|
}
|
|
100817
101195
|
if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
|
|
100818
101196
|
const gts = this._gracefulTimeoutState;
|
|
100819
|
-
if (this.timeoutBehavior === "graceful" && gts) {
|
|
101197
|
+
if ((this.timeoutBehavior === "graceful" || this.timeoutBehavior === "negotiated") && gts) {
|
|
100820
101198
|
} else {
|
|
100821
101199
|
timeoutState.timeoutId = setTimeout(() => {
|
|
100822
101200
|
controller.abort();
|
|
@@ -101720,7 +102098,7 @@ var init_ProbeAgent = __esm({
|
|
|
101720
102098
|
}
|
|
101721
102099
|
mcpConfig = null;
|
|
101722
102100
|
}
|
|
101723
|
-
this.mcpBridge = new MCPXmlBridge({ debug: this.debug });
|
|
102101
|
+
this.mcpBridge = new MCPXmlBridge({ debug: this.debug, agentEvents: this.events });
|
|
101724
102102
|
await this.mcpBridge.initialize(mcpConfig);
|
|
101725
102103
|
const mcpToolNames = this.mcpBridge.getToolNames();
|
|
101726
102104
|
const mcpToolCount = mcpToolNames.length;
|
|
@@ -102203,6 +102581,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102203
102581
|
} else {
|
|
102204
102582
|
options = schemaOrOptions || {};
|
|
102205
102583
|
}
|
|
102584
|
+
this._operationStartTime = Date.now();
|
|
102206
102585
|
try {
|
|
102207
102586
|
const oldHistoryLength = this.history.length;
|
|
102208
102587
|
if (this._outputBuffer && !options?._schemaFormatted && !options?._completionPromptProcessed) {
|
|
@@ -102283,7 +102662,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102283
102662
|
}
|
|
102284
102663
|
}
|
|
102285
102664
|
let currentIteration = 0;
|
|
102286
|
-
let finalResult =
|
|
102665
|
+
let finalResult = null;
|
|
102666
|
+
const DEFAULT_MAX_ITER_MSG = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
|
|
102667
|
+
const _toolCallLog = [];
|
|
102668
|
+
let abortSummaryTaken = false;
|
|
102287
102669
|
const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
|
|
102288
102670
|
const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
|
|
102289
102671
|
const isClaudeCode = this.clientApiProvider === "claude-code" || process.env.USE_CLAUDE_CODE === "true";
|
|
@@ -102423,6 +102805,233 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102423
102805
|
bonusStepsMax: this.gracefulTimeoutBonusSteps
|
|
102424
102806
|
};
|
|
102425
102807
|
this._gracefulTimeoutState = gracefulTimeoutState;
|
|
102808
|
+
const negotiatedTimeoutState = {
|
|
102809
|
+
extensionsUsed: 0,
|
|
102810
|
+
totalExtraTimeMs: 0,
|
|
102811
|
+
softTimeoutId: null,
|
|
102812
|
+
hardAbortTimeoutId: null,
|
|
102813
|
+
maxRequests: this.negotiatedTimeoutMaxRequests,
|
|
102814
|
+
maxPerRequestMs: this.negotiatedTimeoutMaxPerRequest,
|
|
102815
|
+
budgetMs: this.negotiatedTimeoutBudget,
|
|
102816
|
+
observerRunning: false,
|
|
102817
|
+
// true while observer LLM call is in flight
|
|
102818
|
+
extensionMessage: null,
|
|
102819
|
+
// message to show in prepareStep after extension granted
|
|
102820
|
+
startTime: Date.now()
|
|
102821
|
+
};
|
|
102822
|
+
this._negotiatedTimeoutState = negotiatedTimeoutState;
|
|
102823
|
+
const activeTools = /* @__PURE__ */ new Map();
|
|
102824
|
+
this._activeTools = activeTools;
|
|
102825
|
+
const onToolCall = (event) => {
|
|
102826
|
+
const key = event.toolCallId || `${event.name}:${JSON.stringify(event.args || {}).slice(0, 100)}`;
|
|
102827
|
+
if (event.status === "started") {
|
|
102828
|
+
activeTools.set(key, {
|
|
102829
|
+
name: event.name,
|
|
102830
|
+
args: event.args,
|
|
102831
|
+
startedAt: event.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
102832
|
+
});
|
|
102833
|
+
} else if (event.status === "completed" || event.status === "error") {
|
|
102834
|
+
activeTools.delete(key);
|
|
102835
|
+
}
|
|
102836
|
+
};
|
|
102837
|
+
this.events.on("toolCall", onToolCall);
|
|
102838
|
+
const runTimeoutObserver = async () => {
|
|
102839
|
+
if (negotiatedTimeoutState.observerRunning) return;
|
|
102840
|
+
negotiatedTimeoutState.observerRunning = true;
|
|
102841
|
+
const remainingRequests = negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed;
|
|
102842
|
+
const remainingBudgetMs = negotiatedTimeoutState.budgetMs - negotiatedTimeoutState.totalExtraTimeMs;
|
|
102843
|
+
const maxPerReqMin = Math.round(negotiatedTimeoutState.maxPerRequestMs / 6e4);
|
|
102844
|
+
const elapsedMin = Math.round((Date.now() - negotiatedTimeoutState.startTime) / 6e4);
|
|
102845
|
+
if (remainingRequests <= 0 || remainingBudgetMs <= 0) {
|
|
102846
|
+
if (this.debug) {
|
|
102847
|
+
console.log(`[DEBUG] Timeout observer: no extensions/budget remaining \u2014 aborting in-flight tools and triggering graceful wind-down`);
|
|
102848
|
+
}
|
|
102849
|
+
if (this.tracer) {
|
|
102850
|
+
this.tracer.addEvent("negotiated_timeout.observer_exhausted", {
|
|
102851
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
102852
|
+
max_requests: negotiatedTimeoutState.maxRequests,
|
|
102853
|
+
total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
|
|
102854
|
+
budget_ms: negotiatedTimeoutState.budgetMs,
|
|
102855
|
+
elapsed_min: elapsedMin,
|
|
102856
|
+
active_tools: Array.from(activeTools.values()).map((t) => t.name)
|
|
102857
|
+
});
|
|
102858
|
+
}
|
|
102859
|
+
await this._initiateGracefulStop(gracefulTimeoutState, "budget/extensions exhausted");
|
|
102860
|
+
negotiatedTimeoutState.observerRunning = false;
|
|
102861
|
+
return;
|
|
102862
|
+
}
|
|
102863
|
+
const activeToolsList = Array.from(activeTools.values());
|
|
102864
|
+
const now = Date.now();
|
|
102865
|
+
const formatDuration = (ms) => {
|
|
102866
|
+
const totalSec = Math.round(ms / 1e3);
|
|
102867
|
+
if (totalSec < 60) return `${totalSec}s`;
|
|
102868
|
+
const min = Math.floor(totalSec / 60);
|
|
102869
|
+
const sec = totalSec % 60;
|
|
102870
|
+
if (min < 60) return `${min}m ${sec}s`;
|
|
102871
|
+
const hr = Math.floor(min / 60);
|
|
102872
|
+
const remainMin = min % 60;
|
|
102873
|
+
return `${hr}h ${remainMin}m`;
|
|
102874
|
+
};
|
|
102875
|
+
const activeToolsDesc = activeToolsList.length > 0 ? activeToolsList.map((t) => {
|
|
102876
|
+
const runningForMs = now - new Date(t.startedAt).getTime();
|
|
102877
|
+
return `- ${t.name}(${JSON.stringify(t.args || {}).slice(0, 200)}) \u2014 running for ${formatDuration(runningForMs)}`;
|
|
102878
|
+
}).join("\n") : "(none currently running)";
|
|
102879
|
+
const recentHistory = this.history.slice(-6).map((msg) => {
|
|
102880
|
+
const content = typeof msg.content === "string" ? msg.content.slice(0, 300) : JSON.stringify(msg.content).slice(0, 300);
|
|
102881
|
+
return `[${msg.role}]: ${content}`;
|
|
102882
|
+
}).join("\n");
|
|
102883
|
+
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.
|
|
102884
|
+
|
|
102885
|
+
## Recent Conversation
|
|
102886
|
+
${recentHistory || "(no history yet)"}
|
|
102887
|
+
|
|
102888
|
+
## Currently Running Tools
|
|
102889
|
+
${activeToolsDesc}
|
|
102890
|
+
|
|
102891
|
+
## Budget
|
|
102892
|
+
- Extensions used: ${negotiatedTimeoutState.extensionsUsed}/${negotiatedTimeoutState.maxRequests}
|
|
102893
|
+
- Time budget remaining: ${Math.round(remainingBudgetMs / 6e4)} minutes
|
|
102894
|
+
- Max per extension: ${maxPerReqMin} minutes
|
|
102895
|
+
|
|
102896
|
+
Decide whether the agent should get more time. EXTEND if:
|
|
102897
|
+
- Tools are actively running (especially delegates or complex analysis) \u2014 they need time to finish
|
|
102898
|
+
- The agent is making clear progress on a complex task
|
|
102899
|
+
- New information is being gathered that will improve the final answer
|
|
102900
|
+
|
|
102901
|
+
DO NOT EXTEND if:
|
|
102902
|
+
- The agent appears stuck in a loop (repeating the same tool calls or getting the same errors)
|
|
102903
|
+
- The conversation shows the agent retrying failed operations without changing approach
|
|
102904
|
+
- The agent has enough information to answer but keeps searching for more
|
|
102905
|
+
- Tool calls are returning empty or error results repeatedly
|
|
102906
|
+
- The agent is doing redundant work (searching for things it already found)
|
|
102907
|
+
|
|
102908
|
+
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.
|
|
102909
|
+
|
|
102910
|
+
Respond with ONLY valid JSON (no markdown, no explanation):
|
|
102911
|
+
{"extend": true, "minutes": <1-${maxPerReqMin}>, "reason": "your reason here"}
|
|
102912
|
+
or
|
|
102913
|
+
{"extend": false, "reason": "your reason here"}`;
|
|
102914
|
+
const observerFn = async () => {
|
|
102915
|
+
const modelInstance = this.provider ? this.provider(this.model) : this.model;
|
|
102916
|
+
if (this.debug) {
|
|
102917
|
+
console.log(`[DEBUG] Timeout observer: making LLM call (${activeToolsList.length} active tools, ${elapsedMin} min elapsed)`);
|
|
102918
|
+
}
|
|
102919
|
+
if (this.tracer) {
|
|
102920
|
+
this.tracer.addEvent("negotiated_timeout.observer_invoked", {
|
|
102921
|
+
elapsed_min: elapsedMin,
|
|
102922
|
+
active_tools: activeToolsList.map((t) => t.name),
|
|
102923
|
+
active_tools_detail: activeToolsList.map((t) => ({
|
|
102924
|
+
name: t.name,
|
|
102925
|
+
running_for_ms: now - new Date(t.startedAt).getTime(),
|
|
102926
|
+
args_preview: JSON.stringify(t.args || {}).slice(0, 100)
|
|
102927
|
+
})),
|
|
102928
|
+
active_tools_count: activeToolsList.length,
|
|
102929
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
102930
|
+
remaining_requests: remainingRequests,
|
|
102931
|
+
remaining_budget_ms: remainingBudgetMs,
|
|
102932
|
+
history_length: this.history.length
|
|
102933
|
+
});
|
|
102934
|
+
}
|
|
102935
|
+
const observerResult = await (0, import_ai6.generateText)({
|
|
102936
|
+
model: modelInstance,
|
|
102937
|
+
messages: [{ role: "user", content: observerPrompt }],
|
|
102938
|
+
maxTokens: 500
|
|
102939
|
+
});
|
|
102940
|
+
const responseText = observerResult.text.trim();
|
|
102941
|
+
if (this.tracer) {
|
|
102942
|
+
this.tracer.addEvent("negotiated_timeout.observer_response", {
|
|
102943
|
+
response_text: responseText,
|
|
102944
|
+
usage_prompt_tokens: observerResult.usage?.promptTokens,
|
|
102945
|
+
usage_completion_tokens: observerResult.usage?.completionTokens
|
|
102946
|
+
});
|
|
102947
|
+
}
|
|
102948
|
+
const jsonStr = responseText.replace(/^```(?:json)?\s*/, "").replace(/\s*```$/, "");
|
|
102949
|
+
const decision = JSON.parse(jsonStr);
|
|
102950
|
+
if (decision.extend && decision.minutes > 0) {
|
|
102951
|
+
const requestedMs = Math.min(decision.minutes, maxPerReqMin) * 6e4;
|
|
102952
|
+
const grantedMs = Math.min(requestedMs, remainingBudgetMs, negotiatedTimeoutState.maxPerRequestMs);
|
|
102953
|
+
const grantedMin = Math.round(grantedMs / 6e4 * 10) / 10;
|
|
102954
|
+
negotiatedTimeoutState.extensionsUsed++;
|
|
102955
|
+
negotiatedTimeoutState.totalExtraTimeMs += grantedMs;
|
|
102956
|
+
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.`;
|
|
102957
|
+
negotiatedTimeoutState.softTimeoutId = setTimeout(() => {
|
|
102958
|
+
runTimeoutObserver();
|
|
102959
|
+
}, grantedMs);
|
|
102960
|
+
if (this.debug) {
|
|
102961
|
+
console.log(`[DEBUG] Timeout observer: granted ${grantedMin} min (reason: ${decision.reason}). Extensions: ${negotiatedTimeoutState.extensionsUsed}/${negotiatedTimeoutState.maxRequests}`);
|
|
102962
|
+
}
|
|
102963
|
+
if (this.tracer) {
|
|
102964
|
+
this.tracer.addEvent("negotiated_timeout.observer_extended", {
|
|
102965
|
+
decision_reason: decision.reason,
|
|
102966
|
+
requested_minutes: decision.minutes,
|
|
102967
|
+
granted_ms: grantedMs,
|
|
102968
|
+
granted_min: grantedMin,
|
|
102969
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
102970
|
+
max_requests: negotiatedTimeoutState.maxRequests,
|
|
102971
|
+
total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
|
|
102972
|
+
budget_remaining_ms: remainingBudgetMs - grantedMs,
|
|
102973
|
+
active_tools: activeToolsList.map((t) => t.name),
|
|
102974
|
+
active_tools_count: activeToolsList.length
|
|
102975
|
+
});
|
|
102976
|
+
}
|
|
102977
|
+
this.events.emit("timeout.extended", {
|
|
102978
|
+
grantedMs,
|
|
102979
|
+
reason: decision.reason || "work in progress",
|
|
102980
|
+
extensionsUsed: negotiatedTimeoutState.extensionsUsed,
|
|
102981
|
+
extensionsRemaining: negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed,
|
|
102982
|
+
totalExtraTimeMs: negotiatedTimeoutState.totalExtraTimeMs,
|
|
102983
|
+
budgetRemainingMs: remainingBudgetMs - grantedMs
|
|
102984
|
+
});
|
|
102985
|
+
} else {
|
|
102986
|
+
if (this.debug) {
|
|
102987
|
+
console.log(`[DEBUG] Timeout observer: declined extension (reason: ${decision.reason}). Initiating graceful stop.`);
|
|
102988
|
+
}
|
|
102989
|
+
if (this.tracer) {
|
|
102990
|
+
this.tracer.addEvent("negotiated_timeout.observer_declined", {
|
|
102991
|
+
decision_reason: decision.reason,
|
|
102992
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
102993
|
+
total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
|
|
102994
|
+
elapsed_min: elapsedMin,
|
|
102995
|
+
active_tools: activeToolsList.map((t) => t.name)
|
|
102996
|
+
});
|
|
102997
|
+
}
|
|
102998
|
+
this.events.emit("timeout.windingDown", {
|
|
102999
|
+
reason: decision.reason || "observer declined",
|
|
103000
|
+
extensionsUsed: negotiatedTimeoutState.extensionsUsed,
|
|
103001
|
+
totalExtraTimeMs: negotiatedTimeoutState.totalExtraTimeMs
|
|
103002
|
+
});
|
|
103003
|
+
await this._initiateGracefulStop(gracefulTimeoutState, `observer declined: ${decision.reason}`);
|
|
103004
|
+
}
|
|
103005
|
+
};
|
|
103006
|
+
try {
|
|
103007
|
+
if (this.tracer) {
|
|
103008
|
+
await this.tracer.withSpan("negotiated_timeout.observer", observerFn, {
|
|
103009
|
+
"timeout.elapsed_min": elapsedMin,
|
|
103010
|
+
"timeout.extensions_used": negotiatedTimeoutState.extensionsUsed,
|
|
103011
|
+
"timeout.active_tools_count": activeToolsList.length,
|
|
103012
|
+
"timeout.remaining_budget_ms": remainingBudgetMs
|
|
103013
|
+
});
|
|
103014
|
+
} else {
|
|
103015
|
+
await observerFn();
|
|
103016
|
+
}
|
|
103017
|
+
} catch (err) {
|
|
103018
|
+
if (this.debug) {
|
|
103019
|
+
console.log(`[DEBUG] Timeout observer: LLM call failed (${err.message}). Initiating graceful stop.`);
|
|
103020
|
+
}
|
|
103021
|
+
if (this.tracer) {
|
|
103022
|
+
this.tracer.addEvent("negotiated_timeout.observer_error", {
|
|
103023
|
+
error_message: err.message,
|
|
103024
|
+
error_name: err.name,
|
|
103025
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
103026
|
+
elapsed_min: elapsedMin
|
|
103027
|
+
});
|
|
103028
|
+
}
|
|
103029
|
+
await this._initiateGracefulStop(gracefulTimeoutState, `observer error: ${err.message}`);
|
|
103030
|
+
} finally {
|
|
103031
|
+
negotiatedTimeoutState.observerRunning = false;
|
|
103032
|
+
}
|
|
103033
|
+
};
|
|
103034
|
+
negotiatedTimeoutState.runObserver = runTimeoutObserver;
|
|
102426
103035
|
let compactionAttempted = false;
|
|
102427
103036
|
while (true) {
|
|
102428
103037
|
try {
|
|
@@ -102484,6 +103093,14 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102484
103093
|
return false;
|
|
102485
103094
|
},
|
|
102486
103095
|
prepareStep: ({ steps, stepNumber }) => {
|
|
103096
|
+
if (negotiatedTimeoutState.extensionMessage && !gracefulTimeoutState.triggered) {
|
|
103097
|
+
const msg = negotiatedTimeoutState.extensionMessage;
|
|
103098
|
+
negotiatedTimeoutState.extensionMessage = null;
|
|
103099
|
+
if (this.debug) {
|
|
103100
|
+
console.log(`[DEBUG] prepareStep: delivering timeout observer extension message`);
|
|
103101
|
+
}
|
|
103102
|
+
return { userMessage: msg };
|
|
103103
|
+
}
|
|
102487
103104
|
if (gracefulTimeoutState.triggered) {
|
|
102488
103105
|
gracefulTimeoutState.bonusStepsUsed++;
|
|
102489
103106
|
const remaining = gracefulTimeoutState.bonusStepsMax - gracefulTimeoutState.bonusStepsUsed;
|
|
@@ -102509,8 +103126,12 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
102509
103126
|
return { toolChoice: "none" };
|
|
102510
103127
|
}
|
|
102511
103128
|
if (stepNumber === maxIterations - 1) {
|
|
103129
|
+
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);
|
|
103130
|
+
const searchSummary = searchesTried.length > 0 ? `
|
|
103131
|
+
Searches attempted: ${searchesTried.join(", ")}` : "";
|
|
102512
103132
|
return {
|
|
102513
|
-
toolChoice: "none"
|
|
103133
|
+
toolChoice: "none",
|
|
103134
|
+
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}`
|
|
102514
103135
|
};
|
|
102515
103136
|
}
|
|
102516
103137
|
if (steps.length >= 2) {
|
|
@@ -102589,6 +103210,11 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102589
103210
|
const { toolResults, toolCalls, text, reasoningText, finishReason, usage } = stepResult;
|
|
102590
103211
|
currentIteration++;
|
|
102591
103212
|
toolContext.currentIteration = currentIteration;
|
|
103213
|
+
if (toolCalls?.length > 0) {
|
|
103214
|
+
for (const tc of toolCalls) {
|
|
103215
|
+
_toolCallLog.push({ name: tc.toolName, args: tc.args || {} });
|
|
103216
|
+
}
|
|
103217
|
+
}
|
|
102592
103218
|
if (this.tracer) {
|
|
102593
103219
|
const stepEvent = {
|
|
102594
103220
|
"iteration": currentIteration,
|
|
@@ -102680,6 +103306,14 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102680
103306
|
}, 6e4);
|
|
102681
103307
|
}, this.maxOperationTimeout);
|
|
102682
103308
|
}
|
|
103309
|
+
if (this.timeoutBehavior === "negotiated" && this.maxOperationTimeout > 0) {
|
|
103310
|
+
negotiatedTimeoutState.softTimeoutId = setTimeout(() => {
|
|
103311
|
+
if (this.debug) {
|
|
103312
|
+
console.log(`[DEBUG] Soft timeout after ${this.maxOperationTimeout}ms \u2014 invoking timeout observer`);
|
|
103313
|
+
}
|
|
103314
|
+
runTimeoutObserver();
|
|
103315
|
+
}, this.maxOperationTimeout);
|
|
103316
|
+
}
|
|
102683
103317
|
try {
|
|
102684
103318
|
const steps = await result.steps;
|
|
102685
103319
|
let finalText;
|
|
@@ -102700,6 +103334,12 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102700
103334
|
} finally {
|
|
102701
103335
|
if (gracefulTimeoutId) clearTimeout(gracefulTimeoutId);
|
|
102702
103336
|
if (hardAbortTimeoutId) clearTimeout(hardAbortTimeoutId);
|
|
103337
|
+
if (negotiatedTimeoutState.softTimeoutId) clearTimeout(negotiatedTimeoutState.softTimeoutId);
|
|
103338
|
+
if (this._gracefulStopHardAbortId) {
|
|
103339
|
+
clearTimeout(this._gracefulStopHardAbortId);
|
|
103340
|
+
this._gracefulStopHardAbortId = null;
|
|
103341
|
+
}
|
|
103342
|
+
this.events.removeListener("toolCall", onToolCall);
|
|
102703
103343
|
}
|
|
102704
103344
|
};
|
|
102705
103345
|
let aiResult;
|
|
@@ -102739,7 +103379,7 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102739
103379
|
}
|
|
102740
103380
|
if (gracefulTimeoutState.triggered) {
|
|
102741
103381
|
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";
|
|
102742
|
-
if (!finalResult || finalResult === "I was unable to complete your request
|
|
103382
|
+
if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG || finalResult.startsWith("I was unable to complete your request after")) {
|
|
102743
103383
|
try {
|
|
102744
103384
|
const allText = await aiResult.result.text;
|
|
102745
103385
|
if (allText && allText.trim()) {
|
|
@@ -102787,7 +103427,7 @@ ${toolSummaries.join("\n\n---\n\n")}`;
|
|
|
102787
103427
|
currentMessages.push(msg);
|
|
102788
103428
|
}
|
|
102789
103429
|
}
|
|
102790
|
-
if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && finalResult) {
|
|
103430
|
+
if (this.completionPrompt && !options._completionPromptProcessed && !completionPromptInjected && !abortSummaryTaken && finalResult) {
|
|
102791
103431
|
completionPromptInjected = true;
|
|
102792
103432
|
preCompletionResult = finalResult;
|
|
102793
103433
|
if (this.debug) {
|
|
@@ -102859,6 +103499,118 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102859
103499
|
}
|
|
102860
103500
|
break;
|
|
102861
103501
|
} catch (error40) {
|
|
103502
|
+
if (gracefulTimeoutState.triggered && error40?.name === "AbortError") {
|
|
103503
|
+
if (this.debug) {
|
|
103504
|
+
console.log(`[DEBUG] Negotiated timeout: abort caught \u2014 making summary LLM call with conversation context`);
|
|
103505
|
+
}
|
|
103506
|
+
if (this.tracer) {
|
|
103507
|
+
this.tracer.addEvent("negotiated_timeout.abort_summary_started", {
|
|
103508
|
+
conversation_messages: currentMessages.length,
|
|
103509
|
+
has_schema: !!options.schema,
|
|
103510
|
+
has_tasks: !!(this.enableTasks && this.taskManager)
|
|
103511
|
+
});
|
|
103512
|
+
}
|
|
103513
|
+
try {
|
|
103514
|
+
let taskContext = "";
|
|
103515
|
+
if (this.enableTasks && this.taskManager) {
|
|
103516
|
+
const taskSummary = this.taskManager.getTaskSummary?.();
|
|
103517
|
+
if (taskSummary) {
|
|
103518
|
+
taskContext = `
|
|
103519
|
+
|
|
103520
|
+
## Task Status
|
|
103521
|
+
${taskSummary}
|
|
103522
|
+
|
|
103523
|
+
Acknowledge which tasks were completed and which were not.`;
|
|
103524
|
+
}
|
|
103525
|
+
}
|
|
103526
|
+
let schemaContext = "";
|
|
103527
|
+
if (options.schema) {
|
|
103528
|
+
try {
|
|
103529
|
+
const parsedSchema = typeof options.schema === "string" ? JSON.parse(options.schema) : options.schema;
|
|
103530
|
+
schemaContext = `
|
|
103531
|
+
|
|
103532
|
+
IMPORTANT: Your response MUST be valid JSON matching this schema:
|
|
103533
|
+
${JSON.stringify(parsedSchema, null, 2)}
|
|
103534
|
+
|
|
103535
|
+
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.`;
|
|
103536
|
+
} catch {
|
|
103537
|
+
}
|
|
103538
|
+
}
|
|
103539
|
+
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.
|
|
103540
|
+
|
|
103541
|
+
Please provide a DETAILED summary of:
|
|
103542
|
+
1. What you were asked to do (the original task)
|
|
103543
|
+
2. What you accomplished \u2014 include ALL findings, code snippets, data, and conclusions you gathered
|
|
103544
|
+
3. What was still in progress or not yet started
|
|
103545
|
+
4. Any partial results or recommendations you can offer based on what you found so far${taskContext}${schemaContext}
|
|
103546
|
+
|
|
103547
|
+
Be thorough \u2014 this is the user's only response. Include all useful information you collected.`;
|
|
103548
|
+
const summaryMessages = [
|
|
103549
|
+
...currentMessages,
|
|
103550
|
+
{ role: "user", content: summaryPrompt }
|
|
103551
|
+
];
|
|
103552
|
+
const modelInstance = this.provider ? this.provider(this.model) : this.model;
|
|
103553
|
+
const summaryFn = async () => {
|
|
103554
|
+
const summaryResult = await (0, import_ai6.generateText)({
|
|
103555
|
+
model: modelInstance,
|
|
103556
|
+
messages: this.prepareMessagesWithImages(summaryMessages),
|
|
103557
|
+
maxTokens: 4e3
|
|
103558
|
+
});
|
|
103559
|
+
if (this.tracer) {
|
|
103560
|
+
this.tracer.addEvent("negotiated_timeout.abort_summary_completed", {
|
|
103561
|
+
summary_length: summaryResult.text?.length || 0,
|
|
103562
|
+
usage_prompt_tokens: summaryResult.usage?.promptTokens,
|
|
103563
|
+
usage_completion_tokens: summaryResult.usage?.completionTokens
|
|
103564
|
+
});
|
|
103565
|
+
}
|
|
103566
|
+
if (summaryResult.usage) {
|
|
103567
|
+
this.tokenCounter.recordUsage(summaryResult.usage);
|
|
103568
|
+
}
|
|
103569
|
+
return summaryResult.text;
|
|
103570
|
+
};
|
|
103571
|
+
let summaryText;
|
|
103572
|
+
if (this.tracer) {
|
|
103573
|
+
summaryText = await this.tracer.withSpan("negotiated_timeout.abort_summary", summaryFn, {
|
|
103574
|
+
"summary.conversation_messages": currentMessages.length
|
|
103575
|
+
});
|
|
103576
|
+
} else {
|
|
103577
|
+
summaryText = await summaryFn();
|
|
103578
|
+
}
|
|
103579
|
+
if (options.schema) {
|
|
103580
|
+
finalResult = summaryText || "{}";
|
|
103581
|
+
} else {
|
|
103582
|
+
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";
|
|
103583
|
+
finalResult = timeoutNotice + (summaryText || "The operation was interrupted before a response could be generated.");
|
|
103584
|
+
}
|
|
103585
|
+
if (options.onStream && finalResult) {
|
|
103586
|
+
options.onStream(finalResult);
|
|
103587
|
+
}
|
|
103588
|
+
if (this.debug) {
|
|
103589
|
+
console.log(`[DEBUG] Negotiated timeout: summary produced ${summaryText?.length || 0} chars`);
|
|
103590
|
+
}
|
|
103591
|
+
} catch (summaryErr) {
|
|
103592
|
+
if (this.debug) {
|
|
103593
|
+
console.log(`[DEBUG] Negotiated timeout: summary call failed (${summaryErr.message}), falling back to partial text`);
|
|
103594
|
+
}
|
|
103595
|
+
if (this.tracer) {
|
|
103596
|
+
this.tracer.addEvent("negotiated_timeout.abort_summary_error", {
|
|
103597
|
+
error_message: summaryErr.message
|
|
103598
|
+
});
|
|
103599
|
+
}
|
|
103600
|
+
const partialTexts = currentMessages.filter((m) => m.role === "assistant" && typeof m.content === "string" && m.content.trim()).map((m) => m.content);
|
|
103601
|
+
if (options.schema) {
|
|
103602
|
+
finalResult = partialTexts.length > 0 ? partialTexts[partialTexts.length - 1] : "{}";
|
|
103603
|
+
} else {
|
|
103604
|
+
const timeoutNotice = "**Note: This response was generated under a time constraint. The operation was interrupted and some work was not completed.**\n\n";
|
|
103605
|
+
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.";
|
|
103606
|
+
}
|
|
103607
|
+
if (options.onStream && finalResult) {
|
|
103608
|
+
options.onStream(finalResult);
|
|
103609
|
+
}
|
|
103610
|
+
}
|
|
103611
|
+
abortSummaryTaken = true;
|
|
103612
|
+
break;
|
|
103613
|
+
}
|
|
102862
103614
|
if (!compactionAttempted && handleContextLimitError) {
|
|
102863
103615
|
const compactionResult = handleContextLimitError(error40, currentMessages, {
|
|
102864
103616
|
keepLastSegment: true,
|
|
@@ -102893,6 +103645,36 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
102893
103645
|
}
|
|
102894
103646
|
if (currentIteration >= maxIterations) {
|
|
102895
103647
|
console.warn(`[WARN] Max tool iterations (${maxIterations}) reached for session ${this.sessionId}.`);
|
|
103648
|
+
if (!finalResult || finalResult === DEFAULT_MAX_ITER_MSG) {
|
|
103649
|
+
try {
|
|
103650
|
+
const searchQueries = [];
|
|
103651
|
+
const toolCounts = {};
|
|
103652
|
+
for (const tc of _toolCallLog) {
|
|
103653
|
+
toolCounts[tc.name] = (toolCounts[tc.name] || 0) + 1;
|
|
103654
|
+
if (tc.name === "search") {
|
|
103655
|
+
const q = tc.args.query || "";
|
|
103656
|
+
const exact = tc.args.exact ? " (exact)" : "";
|
|
103657
|
+
searchQueries.push(`"${q}"${exact}`);
|
|
103658
|
+
}
|
|
103659
|
+
}
|
|
103660
|
+
const toolBreakdown = Object.entries(toolCounts).map(([name15, count]) => `${name15}: ${count}x`).join(", ");
|
|
103661
|
+
const uniqueSearches = [...new Set(searchQueries)];
|
|
103662
|
+
let summary = `I was unable to complete your request after ${currentIteration} tool iterations.
|
|
103663
|
+
|
|
103664
|
+
`;
|
|
103665
|
+
summary += `Tool calls made: ${toolBreakdown || "none"}
|
|
103666
|
+
`;
|
|
103667
|
+
if (uniqueSearches.length > 0) {
|
|
103668
|
+
summary += `Search queries tried: ${uniqueSearches.join(", ")}
|
|
103669
|
+
`;
|
|
103670
|
+
}
|
|
103671
|
+
summary += `
|
|
103672
|
+
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.`;
|
|
103673
|
+
finalResult = summary;
|
|
103674
|
+
} catch {
|
|
103675
|
+
finalResult = DEFAULT_MAX_ITER_MSG;
|
|
103676
|
+
}
|
|
103677
|
+
}
|
|
102896
103678
|
}
|
|
102897
103679
|
this.history = currentMessages.map((msg) => ({ ...msg }));
|
|
102898
103680
|
if (this.history.length > MAX_HISTORY_MESSAGES) {
|
|
@@ -103427,6 +104209,134 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
103427
104209
|
console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
|
|
103428
104210
|
}
|
|
103429
104211
|
}
|
|
104212
|
+
/**
|
|
104213
|
+
* Trigger graceful wind-down from outside (e.g., parent agent).
|
|
104214
|
+
* Unlike cancel(), this does NOT abort — it sets the graceful timeout flag
|
|
104215
|
+
* so the agent finishes its current step and then winds down naturally.
|
|
104216
|
+
*/
|
|
104217
|
+
triggerGracefulWindDown() {
|
|
104218
|
+
if (this._gracefulTimeoutState && !this._gracefulTimeoutState.triggered) {
|
|
104219
|
+
this._gracefulTimeoutState.triggered = true;
|
|
104220
|
+
if (this.debug) {
|
|
104221
|
+
console.log(`[DEBUG] Graceful wind-down triggered externally for session ${this.sessionId}`);
|
|
104222
|
+
}
|
|
104223
|
+
if (this.tracer) {
|
|
104224
|
+
this.tracer.addEvent("graceful_stop.external_trigger", {
|
|
104225
|
+
"session.id": this.sessionId
|
|
104226
|
+
});
|
|
104227
|
+
}
|
|
104228
|
+
} else if (this.debug) {
|
|
104229
|
+
console.log(`[DEBUG] Graceful wind-down already active for session ${this.sessionId}, skipping`);
|
|
104230
|
+
}
|
|
104231
|
+
}
|
|
104232
|
+
/**
|
|
104233
|
+
* Initiate two-phase graceful stop: signal subagents and MCP servers to wind down,
|
|
104234
|
+
* then hard-abort after a deadline if they haven't finished.
|
|
104235
|
+
* @param {Object} gracefulTimeoutState - The graceful timeout state object from run()
|
|
104236
|
+
* @param {string} reason - Why the graceful stop was initiated
|
|
104237
|
+
*/
|
|
104238
|
+
async _initiateGracefulStop(gracefulTimeoutState, reason) {
|
|
104239
|
+
if (gracefulTimeoutState.triggered) return;
|
|
104240
|
+
if (this.debug) {
|
|
104241
|
+
console.log(`[DEBUG] Initiating graceful stop: ${reason} (subagents: ${this._activeSubagents.size}, hasMcpBridge: ${!!this.mcpBridge}, deadline: ${this.gracefulStopDeadline}ms)`);
|
|
104242
|
+
}
|
|
104243
|
+
gracefulTimeoutState.triggered = true;
|
|
104244
|
+
if (this.tracer) {
|
|
104245
|
+
this.tracer.addEvent("graceful_stop.initiated", {
|
|
104246
|
+
"session.id": this.sessionId,
|
|
104247
|
+
"graceful_stop.reason": reason,
|
|
104248
|
+
"graceful_stop.active_subagents": this._activeSubagents.size,
|
|
104249
|
+
"graceful_stop.has_mcp_bridge": !!this.mcpBridge,
|
|
104250
|
+
"graceful_stop.deadline_ms": this.gracefulStopDeadline
|
|
104251
|
+
});
|
|
104252
|
+
}
|
|
104253
|
+
let subagentsSignalled = 0;
|
|
104254
|
+
let subagentErrors = 0;
|
|
104255
|
+
for (const [sid, subagent] of this._activeSubagents) {
|
|
104256
|
+
try {
|
|
104257
|
+
subagent.triggerGracefulWindDown();
|
|
104258
|
+
subagentsSignalled++;
|
|
104259
|
+
if (this.debug) {
|
|
104260
|
+
console.log(`[DEBUG] Triggered graceful wind-down on subagent ${sid}`);
|
|
104261
|
+
}
|
|
104262
|
+
} catch (e) {
|
|
104263
|
+
subagentErrors++;
|
|
104264
|
+
if (this.debug) {
|
|
104265
|
+
console.log(`[DEBUG] Failed to trigger wind-down on subagent ${sid}: ${e.message}`);
|
|
104266
|
+
}
|
|
104267
|
+
}
|
|
104268
|
+
}
|
|
104269
|
+
let mcpResults = [];
|
|
104270
|
+
if (this.mcpBridge) {
|
|
104271
|
+
try {
|
|
104272
|
+
mcpResults = await this.mcpBridge.callGracefulStopAll();
|
|
104273
|
+
if (this.debug && mcpResults.length > 0) {
|
|
104274
|
+
console.log(`[DEBUG] MCP graceful_stop results: ${JSON.stringify(mcpResults)}`);
|
|
104275
|
+
}
|
|
104276
|
+
} catch (e) {
|
|
104277
|
+
if (this.debug) {
|
|
104278
|
+
console.log(`[DEBUG] MCP graceful_stop failed: ${e.message}`);
|
|
104279
|
+
}
|
|
104280
|
+
}
|
|
104281
|
+
}
|
|
104282
|
+
if (this.tracer) {
|
|
104283
|
+
this.tracer.addEvent("graceful_stop.signals_sent", {
|
|
104284
|
+
"session.id": this.sessionId,
|
|
104285
|
+
"graceful_stop.subagents_signalled": subagentsSignalled,
|
|
104286
|
+
"graceful_stop.subagent_errors": subagentErrors,
|
|
104287
|
+
"graceful_stop.mcp_servers_called": mcpResults.filter((r) => r.success).length,
|
|
104288
|
+
"graceful_stop.mcp_servers_failed": mcpResults.filter((r) => !r.success).length,
|
|
104289
|
+
"graceful_stop.mcp_servers_total": mcpResults.length
|
|
104290
|
+
});
|
|
104291
|
+
}
|
|
104292
|
+
this._gracefulStopHardAbortId = setTimeout(() => {
|
|
104293
|
+
if (this.debug) {
|
|
104294
|
+
console.log(`[DEBUG] Graceful stop deadline (${this.gracefulStopDeadline}ms) expired \u2014 hard aborting`);
|
|
104295
|
+
}
|
|
104296
|
+
if (this.tracer) {
|
|
104297
|
+
this.tracer.addEvent("graceful_stop.deadline_expired", {
|
|
104298
|
+
"session.id": this.sessionId,
|
|
104299
|
+
"graceful_stop.deadline_ms": this.gracefulStopDeadline
|
|
104300
|
+
});
|
|
104301
|
+
}
|
|
104302
|
+
if (this._abortController) this._abortController.abort();
|
|
104303
|
+
}, this.gracefulStopDeadline);
|
|
104304
|
+
}
|
|
104305
|
+
/**
|
|
104306
|
+
* Register an active subagent for graceful stop coordination.
|
|
104307
|
+
* @param {string} sessionId
|
|
104308
|
+
* @param {ProbeAgent} subagent
|
|
104309
|
+
*/
|
|
104310
|
+
_registerSubagent(sessionId, subagent) {
|
|
104311
|
+
this._activeSubagents.set(sessionId, subagent);
|
|
104312
|
+
if (this.debug) {
|
|
104313
|
+
console.log(`[DEBUG] Registered subagent ${sessionId} (active: ${this._activeSubagents.size})`);
|
|
104314
|
+
}
|
|
104315
|
+
if (this.tracer) {
|
|
104316
|
+
this.tracer.addEvent("subagent.registered", {
|
|
104317
|
+
"session.id": this.sessionId,
|
|
104318
|
+
"subagent.session_id": sessionId,
|
|
104319
|
+
"subagent.active_count": this._activeSubagents.size
|
|
104320
|
+
});
|
|
104321
|
+
}
|
|
104322
|
+
}
|
|
104323
|
+
/**
|
|
104324
|
+
* Unregister a completed subagent.
|
|
104325
|
+
* @param {string} sessionId
|
|
104326
|
+
*/
|
|
104327
|
+
_unregisterSubagent(sessionId) {
|
|
104328
|
+
this._activeSubagents.delete(sessionId);
|
|
104329
|
+
if (this.debug) {
|
|
104330
|
+
console.log(`[DEBUG] Unregistered subagent ${sessionId} (active: ${this._activeSubagents.size})`);
|
|
104331
|
+
}
|
|
104332
|
+
if (this.tracer) {
|
|
104333
|
+
this.tracer.addEvent("subagent.unregistered", {
|
|
104334
|
+
"session.id": this.sessionId,
|
|
104335
|
+
"subagent.session_id": sessionId,
|
|
104336
|
+
"subagent.active_count": this._activeSubagents.size
|
|
104337
|
+
});
|
|
104338
|
+
}
|
|
104339
|
+
}
|
|
103430
104340
|
/**
|
|
103431
104341
|
* Get the abort signal for this agent.
|
|
103432
104342
|
* Delegations and subagents should check this signal.
|