@probelabs/probe 0.6.0-rc301 → 0.6.0-rc303
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/bin/binaries/{probe-v0.6.0-rc301-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc303-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc301-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc303-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc301-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc303-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc301-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc303-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc301-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc303-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.js +56 -12
- package/build/agent/bashCommandUtils.js +4 -4
- package/build/agent/bashPermissions.js +34 -0
- package/build/tools/bash.js +2 -0
- package/build/tools/vercel.js +101 -3
- package/cjs/agent/ProbeAgent.cjs +271 -35
- package/cjs/index.cjs +271 -35
- package/package.json +2 -2
- package/src/agent/ProbeAgent.js +56 -12
- package/src/agent/bashCommandUtils.js +4 -4
- package/src/agent/bashPermissions.js +34 -0
- package/src/tools/bash.js +2 -0
- package/src/tools/vercel.js +101 -3
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -27561,8 +27561,23 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27561
27561
|
'- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
|
|
27562
27562
|
"- NEVER repeat the same search query \u2014 you will get the same results. Changing the path does NOT change this.",
|
|
27563
27563
|
"- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
|
|
27564
|
-
"
|
|
27565
|
-
"
|
|
27564
|
+
"",
|
|
27565
|
+
"When a search returns no results:",
|
|
27566
|
+
'- If you searched a SUBFOLDER (e.g., path="gateway/"), the term might exist elsewhere.',
|
|
27567
|
+
" Try searching from the workspace root (omit the path parameter) or a different directory.",
|
|
27568
|
+
" But do NOT retry the same subfolder with different quoting \u2014 that will not help.",
|
|
27569
|
+
"- If you searched the WORKSPACE ROOT and got no results, the term does not exist in this codebase.",
|
|
27570
|
+
' Changing quotes, adding "func " prefix, or switching to method syntax will NOT help.',
|
|
27571
|
+
"- These are ALL the same failed search, NOT different searches:",
|
|
27572
|
+
' search("func ctxGetData") \u2192 no results',
|
|
27573
|
+
' search("ctxGetData") \u2192 no results \u2190 WASTED, same concept, different quoting',
|
|
27574
|
+
" search(ctxGetData) \u2192 no results \u2190 WASTED, same concept, no quotes",
|
|
27575
|
+
' search("ctx.GetData") \u2192 no results \u2190 WASTED, method syntax of same concept',
|
|
27576
|
+
' After the FIRST "no results" at a given scope, either widen the search path or try',
|
|
27577
|
+
" a fundamentally different approach: search for a broader concept, use listFiles",
|
|
27578
|
+
" to discover actual function names, or extract a known file to read real code.",
|
|
27579
|
+
"- If 2 searches return no results for a concept (across different scopes), the code likely",
|
|
27580
|
+
" uses different naming than you expect \u2014 discover the real names via extract or listFiles.",
|
|
27566
27581
|
"",
|
|
27567
27582
|
"When to use exact=true:",
|
|
27568
27583
|
"- Use exact=true when searching for a KNOWN symbol name (function, type, variable, struct).",
|
|
@@ -27615,6 +27630,21 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27615
27630
|
' \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" \u2192 search "ForwardMessage" (WRONG: repeating the exact same query)',
|
|
27616
27631
|
' \u2192 search "authentication" \u2192 wait \u2192 search "session management" \u2192 wait (WRONG: these are independent, run them in parallel)',
|
|
27617
27632
|
"",
|
|
27633
|
+
" WORST pattern \u2014 retrying a non-existent function with quote/syntax variations (this wastes 30 minutes):",
|
|
27634
|
+
' \u2192 search "func ctxGetData" \u2192 no results',
|
|
27635
|
+
' \u2192 search "ctxGetData" \u2192 no results \u2190 WRONG: same term without "func" prefix',
|
|
27636
|
+
' \u2192 search "ctx.GetData" \u2192 no results \u2190 WRONG: method syntax of same concept',
|
|
27637
|
+
' \u2192 search "ctx.SetData" \u2192 no results \u2190 WRONG: Set variant of same concept',
|
|
27638
|
+
" \u2192 search ctxGetData \u2192 no results \u2190 WRONG: unquoted version of same term",
|
|
27639
|
+
" \u2192 extract api.go \u2192 extract api.go \u2192 extract api.go (8 times!) \u2190 WRONG: re-reading same file",
|
|
27640
|
+
' FIX: After "func ctxGetData" returns no results in gateway/:',
|
|
27641
|
+
" Option A: Widen scope \u2014 search from the workspace root (omit path) in case the",
|
|
27642
|
+
" function is defined in a different package (e.g., apidef/, user/, config/).",
|
|
27643
|
+
" Option B: Discover real names \u2014 extract a file you KNOW uses context (e.g., a",
|
|
27644
|
+
" middleware file) and READ what functions it actually calls.",
|
|
27645
|
+
" Option C: Browse \u2014 use listFiles to see what files exist and extract the relevant ones.",
|
|
27646
|
+
" NEVER: retry the same concept with different quoting in the same directory.",
|
|
27647
|
+
"",
|
|
27618
27648
|
"Keyword tips:",
|
|
27619
27649
|
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
27620
27650
|
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
@@ -27653,7 +27683,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
27653
27683
|
" - Type references and imports \u2192 include type definitions.",
|
|
27654
27684
|
" - Registered handlers/middleware \u2192 include all registered items.",
|
|
27655
27685
|
"6. If a search returns results, use extract to verify relevance. Run multiple extracts in parallel too.",
|
|
27656
|
-
"7. If a search returns NO results
|
|
27686
|
+
"7. If a search returns NO results: widen the path scope if you searched a subfolder, or move on. Do NOT retry with quote/syntax variations \u2014 they search the same index.",
|
|
27657
27687
|
"8. Once you have enough targets (typically 5-15), output your final JSON answer immediately.",
|
|
27658
27688
|
"",
|
|
27659
27689
|
`Query: ${searchQuery}`,
|
|
@@ -27713,7 +27743,14 @@ var init_vercel = __esm({
|
|
|
27713
27743
|
const previousSearches = /* @__PURE__ */ new Map();
|
|
27714
27744
|
const dupBlockCounts = /* @__PURE__ */ new Map();
|
|
27715
27745
|
const paginationCounts = /* @__PURE__ */ new Map();
|
|
27746
|
+
let consecutiveNoResults = 0;
|
|
27747
|
+
const MAX_CONSECUTIVE_NO_RESULTS = 4;
|
|
27748
|
+
const failedConcepts = /* @__PURE__ */ new Map();
|
|
27716
27749
|
const MAX_PAGES_PER_QUERY = 3;
|
|
27750
|
+
function normalizeQueryConcept(query2) {
|
|
27751
|
+
if (!query2) return "";
|
|
27752
|
+
return query2.replace(/^["']|["']$/g, "").replace(/\./g, "").replace(/[_\-\s]+/g, "").toLowerCase().trim();
|
|
27753
|
+
}
|
|
27717
27754
|
return (0, import_ai.tool)({
|
|
27718
27755
|
name: "search",
|
|
27719
27756
|
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
@@ -27782,6 +27819,41 @@ var init_vercel = __esm({
|
|
|
27782
27819
|
}
|
|
27783
27820
|
previousSearches.set(searchKey, { hadResults: false });
|
|
27784
27821
|
paginationCounts.set(searchKey, 0);
|
|
27822
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
27823
|
+
if (failedConcepts.has(normalizedKey) && failedConcepts.get(normalizedKey) >= 2) {
|
|
27824
|
+
const conceptCount = failedConcepts.get(normalizedKey) + 1;
|
|
27825
|
+
failedConcepts.set(normalizedKey, conceptCount);
|
|
27826
|
+
if (debug) {
|
|
27827
|
+
console.error(`[CONCEPT-DEDUP] Blocked variation of failed concept (${conceptCount}x): "${searchQuery}" normalized to "${normalizeQueryConcept(searchQuery)}"`);
|
|
27828
|
+
}
|
|
27829
|
+
const isSubfolder = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
27830
|
+
const scopeHint = isSubfolder ? `
|
|
27831
|
+
- Try searching from the workspace root (omit the path parameter) \u2014 the term may exist in a different directory` : `
|
|
27832
|
+
- The term does not exist in this codebase at any path`;
|
|
27833
|
+
return `CONCEPT ALREADY FAILED (${conceptCount} variations tried). You already searched for "${normalizeQueryConcept(searchQuery)}" with different quoting/syntax in this path and got NO results each time. Changing quotes, adding "func" prefix, or switching to method syntax will NOT change the results.
|
|
27834
|
+
|
|
27835
|
+
Change your strategy:${scopeHint}
|
|
27836
|
+
- Use extract on a file you ALREADY found to read actual code and discover real function/type names
|
|
27837
|
+
- Use listFiles to browse directories and find what functions actually exist
|
|
27838
|
+
- Search for a BROADER concept (e.g., instead of "ctxGetData", try "context" or "middleware data access")
|
|
27839
|
+
- If you have enough information from prior searches, provide your final answer NOW`;
|
|
27840
|
+
}
|
|
27841
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS) {
|
|
27842
|
+
if (debug) {
|
|
27843
|
+
console.error(`[CIRCUIT-BREAKER] ${consecutiveNoResults} consecutive no-result searches, blocking: "${searchQuery}"`);
|
|
27844
|
+
}
|
|
27845
|
+
const isSubfolderCB = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
27846
|
+
const cbScopeHint = isSubfolderCB ? `
|
|
27847
|
+
- You have been searching in "${path9}" \u2014 try searching from the workspace root or a different directory` : "";
|
|
27848
|
+
return `CIRCUIT BREAKER: Your last ${consecutiveNoResults} searches ALL returned no results. You appear to be guessing function/type names that don't match what's actually in the code.
|
|
27849
|
+
|
|
27850
|
+
Change your approach:${cbScopeHint}
|
|
27851
|
+
1. Use extract on files you already found \u2014 read the actual code to discover real function names
|
|
27852
|
+
2. Use listFiles to browse directories and see what files/functions actually exist
|
|
27853
|
+
3. If you found some results earlier, those are likely sufficient \u2014 provide your final answer
|
|
27854
|
+
|
|
27855
|
+
Retrying search query variations will not help. Discover real names from real code instead.`;
|
|
27856
|
+
}
|
|
27785
27857
|
} else {
|
|
27786
27858
|
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
27787
27859
|
paginationCounts.set(searchKey, pageCount);
|
|
@@ -27795,10 +27867,24 @@ var init_vercel = __esm({
|
|
|
27795
27867
|
try {
|
|
27796
27868
|
const result = maybeAnnotate(await runRawSearch());
|
|
27797
27869
|
if (typeof result === "string" && result.includes("No results found")) {
|
|
27870
|
+
consecutiveNoResults++;
|
|
27871
|
+
const normalizedKey = `${searchPath}::${normalizeQueryConcept(searchQuery)}`;
|
|
27872
|
+
failedConcepts.set(normalizedKey, (failedConcepts.get(normalizedKey) || 0) + 1);
|
|
27873
|
+
if (debug) {
|
|
27874
|
+
console.error(`[NO-RESULTS] consecutiveNoResults=${consecutiveNoResults}, concept "${normalizeQueryConcept(searchQuery)}" failed ${failedConcepts.get(normalizedKey)}x`);
|
|
27875
|
+
}
|
|
27798
27876
|
if (/^[A-Z]+-\d+$/.test(searchQuery.trim()) || /^[A-Z]+-\d+$/.test(searchQuery.replace(/"/g, "").trim())) {
|
|
27799
27877
|
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).";
|
|
27800
27878
|
}
|
|
27879
|
+
if (consecutiveNoResults >= MAX_CONSECUTIVE_NO_RESULTS - 1) {
|
|
27880
|
+
const isSubfolderWarn = path9 && path9 !== effectiveSearchCwd && path9 !== ".";
|
|
27881
|
+
const warnScopeHint = isSubfolderWarn ? ` You are searching in "${path9}" \u2014 consider searching from the workspace root or a different directory.` : "";
|
|
27882
|
+
return result + `
|
|
27883
|
+
|
|
27884
|
+
\u26A0\uFE0F WARNING: ${consecutiveNoResults} consecutive searches returned no results.${warnScopeHint} Before your next action: use extract on a file you already found to read actual code, or use listFiles to discover what functions really exist. One more failed search will trigger the circuit breaker.`;
|
|
27885
|
+
}
|
|
27801
27886
|
} else if (typeof result === "string") {
|
|
27887
|
+
consecutiveNoResults = 0;
|
|
27802
27888
|
const entry = previousSearches.get(searchKey);
|
|
27803
27889
|
if (entry) entry.hadResults = true;
|
|
27804
27890
|
}
|
|
@@ -28828,10 +28914,9 @@ function parseSimpleCommand(command) {
|
|
|
28828
28914
|
// Command substitution $()
|
|
28829
28915
|
/`/,
|
|
28830
28916
|
// Command substitution ``
|
|
28831
|
-
|
|
28832
|
-
//
|
|
28833
|
-
|
|
28834
|
-
// Redirection <
|
|
28917
|
+
// Note: > and < (redirection) are intentionally NOT in this list.
|
|
28918
|
+
// They are not command separators — they redirect I/O on a single command.
|
|
28919
|
+
// The base command is still checked against allow/deny lists.
|
|
28835
28920
|
/\*\*/,
|
|
28836
28921
|
// Glob patterns (potentially dangerous)
|
|
28837
28922
|
/^\s*\{.*,.*\}|\{.*\.\.\.*\}/
|
|
@@ -28934,12 +29019,8 @@ function isComplexPattern(pattern) {
|
|
|
28934
29019
|
// Background execution
|
|
28935
29020
|
/\$\(/,
|
|
28936
29021
|
// Command substitution $()
|
|
28937
|
-
|
|
29022
|
+
/`/
|
|
28938
29023
|
// Command substitution ``
|
|
28939
|
-
/>/,
|
|
28940
|
-
// Redirection >
|
|
28941
|
-
/</
|
|
28942
|
-
// Redirection <
|
|
28943
29024
|
];
|
|
28944
29025
|
return operatorPatterns.some((p) => p.test(pattern));
|
|
28945
29026
|
}
|
|
@@ -29048,12 +29129,14 @@ var init_bashPermissions = __esm({
|
|
|
29048
29129
|
* @param {string[]} [config.deny] - Additional deny patterns (always win)
|
|
29049
29130
|
* @param {boolean} [config.disableDefaultAllow] - Disable default allow list
|
|
29050
29131
|
* @param {boolean} [config.disableDefaultDeny] - Disable default deny list
|
|
29132
|
+
* @param {boolean} [config.allowEdit] - Whether file editing is allowed (controls output redirection)
|
|
29051
29133
|
* @param {boolean} [config.debug] - Enable debug logging
|
|
29052
29134
|
* @param {Object} [config.tracer] - Optional tracer for telemetry
|
|
29053
29135
|
*/
|
|
29054
29136
|
constructor(config2 = {}) {
|
|
29055
29137
|
this.debug = config2.debug || false;
|
|
29056
29138
|
this.tracer = config2.tracer || null;
|
|
29139
|
+
this.allowEdit = config2.allowEdit || false;
|
|
29057
29140
|
this.defaultAllowPatterns = config2.disableDefaultAllow ? [] : [...DEFAULT_ALLOW_PATTERNS];
|
|
29058
29141
|
this.customAllowPatterns = config2.allow && Array.isArray(config2.allow) ? [...config2.allow] : [];
|
|
29059
29142
|
this.allowPatterns = [...this.defaultAllowPatterns, ...this.customAllowPatterns];
|
|
@@ -29139,6 +29222,24 @@ var init_bashPermissions = __esm({
|
|
|
29139
29222
|
console.log(`[BashPermissions] Checking simple command: "${command}"`);
|
|
29140
29223
|
console.log(`[BashPermissions] Parsed: ${parsed.command} with args: [${parsed.args.join(", ")}]`);
|
|
29141
29224
|
}
|
|
29225
|
+
if (!this.allowEdit && parsed.args.some((arg) => arg === ">" || arg === ">>")) {
|
|
29226
|
+
const result2 = {
|
|
29227
|
+
allowed: false,
|
|
29228
|
+
reason: "Output redirection (> or >>) requires edit permissions (allowEdit)",
|
|
29229
|
+
command,
|
|
29230
|
+
parsed
|
|
29231
|
+
};
|
|
29232
|
+
if (this.debug) {
|
|
29233
|
+
console.log(`[BashPermissions] DENIED - output redirection without allowEdit`);
|
|
29234
|
+
}
|
|
29235
|
+
this.recordBashEvent("permission.denied", {
|
|
29236
|
+
command,
|
|
29237
|
+
parsedCommand: parsed.command,
|
|
29238
|
+
reason: "output_redirection_without_allow_edit",
|
|
29239
|
+
isComplex: false
|
|
29240
|
+
});
|
|
29241
|
+
return result2;
|
|
29242
|
+
}
|
|
29142
29243
|
if (matchesAnyPattern(parsed, this.customDenyPatterns)) {
|
|
29143
29244
|
const matchedPatterns = this.customDenyPatterns.filter((pattern) => matchesPattern(parsed, pattern));
|
|
29144
29245
|
if (this.debug) {
|
|
@@ -29408,6 +29509,15 @@ var init_bashPermissions = __esm({
|
|
|
29408
29509
|
deniedReason = parsed.error || "Component contains nested complex constructs";
|
|
29409
29510
|
break;
|
|
29410
29511
|
}
|
|
29512
|
+
if (!this.allowEdit && parsed.args && parsed.args.some((arg) => arg === ">" || arg === ">>")) {
|
|
29513
|
+
if (this.debug) {
|
|
29514
|
+
console.log(`[BashPermissions] Component "${component}" has output redirection without allowEdit`);
|
|
29515
|
+
}
|
|
29516
|
+
allAllowed = false;
|
|
29517
|
+
deniedComponent = component;
|
|
29518
|
+
deniedReason = "Output redirection (> or >>) requires edit permissions (allowEdit)";
|
|
29519
|
+
break;
|
|
29520
|
+
}
|
|
29411
29521
|
if (matchesAnyPattern(parsed, this.customDenyPatterns)) {
|
|
29412
29522
|
if (this.debug) {
|
|
29413
29523
|
console.log(`[BashPermissions] Component "${component}" matches custom deny pattern`);
|
|
@@ -29937,6 +30047,7 @@ var init_bash = __esm({
|
|
|
29937
30047
|
debug = false,
|
|
29938
30048
|
cwd,
|
|
29939
30049
|
allowedFolders = [],
|
|
30050
|
+
allowEdit = false,
|
|
29940
30051
|
workspaceRoot: providedWorkspaceRoot,
|
|
29941
30052
|
tracer = null
|
|
29942
30053
|
} = options;
|
|
@@ -29946,6 +30057,7 @@ var init_bash = __esm({
|
|
|
29946
30057
|
deny: bashConfig.deny,
|
|
29947
30058
|
disableDefaultAllow: bashConfig.disableDefaultAllow,
|
|
29948
30059
|
disableDefaultDeny: bashConfig.disableDefaultDeny,
|
|
30060
|
+
allowEdit,
|
|
29949
30061
|
debug,
|
|
29950
30062
|
tracer
|
|
29951
30063
|
});
|
|
@@ -65612,6 +65724,10 @@ var init_parser4 = __esm({
|
|
|
65612
65724
|
});
|
|
65613
65725
|
|
|
65614
65726
|
// node_modules/@probelabs/maid/out/diagrams/sequence/semantics.js
|
|
65727
|
+
function isEscapedEntitySemicolon(image, semicolonIdx) {
|
|
65728
|
+
const uptoSemicolon = image.slice(0, semicolonIdx + 1);
|
|
65729
|
+
return /(?:#\d+|&#\d+|&[A-Za-z][A-Za-z0-9]+);$/.test(uptoSemicolon);
|
|
65730
|
+
}
|
|
65615
65731
|
function analyzeSequence(_cst, _tokens) {
|
|
65616
65732
|
const ctx = { tokens: _tokens };
|
|
65617
65733
|
const v = new SequenceSemanticsVisitor(ctx);
|
|
@@ -65712,6 +65828,35 @@ function analyzeSequence(_cst, _tokens) {
|
|
|
65712
65828
|
if (arrowIdx > 0) {
|
|
65713
65829
|
const from = grabActorRef(arr, 0);
|
|
65714
65830
|
const to = grabActorRef(arr, arrowIdx + 1);
|
|
65831
|
+
const colonIdx = arr.findIndex((tk, idx) => idx > arrowIdx && tk.tokenType === Colon3);
|
|
65832
|
+
if (colonIdx !== -1) {
|
|
65833
|
+
let semicolonColumn = null;
|
|
65834
|
+
for (let i = colonIdx + 1; i < arr.length && semicolonColumn == null; i++) {
|
|
65835
|
+
const tk = arr[i];
|
|
65836
|
+
const img = tk.image || "";
|
|
65837
|
+
if (!img.includes(";"))
|
|
65838
|
+
continue;
|
|
65839
|
+
for (let j = 0; j < img.length; j++) {
|
|
65840
|
+
if (img[j] !== ";")
|
|
65841
|
+
continue;
|
|
65842
|
+
if (isEscapedEntitySemicolon(img, j))
|
|
65843
|
+
continue;
|
|
65844
|
+
semicolonColumn = (tk.startColumn ?? 1) + j;
|
|
65845
|
+
break;
|
|
65846
|
+
}
|
|
65847
|
+
}
|
|
65848
|
+
if (semicolonColumn != null) {
|
|
65849
|
+
errs.push({
|
|
65850
|
+
line: ln,
|
|
65851
|
+
column: semicolonColumn,
|
|
65852
|
+
severity: "error",
|
|
65853
|
+
code: "SE-MSG-SEMICOLON-UNESCAPED",
|
|
65854
|
+
message: "Semicolons in sequence message text must be escaped as '#59;'.",
|
|
65855
|
+
hint: "Replace ';' with '#59;' in the message text.",
|
|
65856
|
+
length: 1
|
|
65857
|
+
});
|
|
65858
|
+
}
|
|
65859
|
+
}
|
|
65715
65860
|
if (from || to) {
|
|
65716
65861
|
const plusTok = arr.find((tk) => tk.tokenType === Plus);
|
|
65717
65862
|
const minusTok = arr.find((tk) => tk.tokenType === Minus);
|
|
@@ -67286,6 +67431,20 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
67286
67431
|
out = out.split(SENT_Q).join(""");
|
|
67287
67432
|
return out;
|
|
67288
67433
|
}
|
|
67434
|
+
function escapeUnescapedSemicolons(textPart) {
|
|
67435
|
+
let out = "";
|
|
67436
|
+
for (let i = 0; i < textPart.length; i++) {
|
|
67437
|
+
const ch = textPart[i];
|
|
67438
|
+
if (ch !== ";") {
|
|
67439
|
+
out += ch;
|
|
67440
|
+
continue;
|
|
67441
|
+
}
|
|
67442
|
+
const upto = textPart.slice(0, i + 1);
|
|
67443
|
+
const isEntity = /(?:#\d+|&#\d+|&[A-Za-z][A-Za-z0-9]+);$/.test(upto);
|
|
67444
|
+
out += isEntity ? ";" : "#59;";
|
|
67445
|
+
}
|
|
67446
|
+
return out;
|
|
67447
|
+
}
|
|
67289
67448
|
for (const e of errors) {
|
|
67290
67449
|
const key = `${e.code}@${e.line}:${e.column}:${e.length ?? 1}`;
|
|
67291
67450
|
if (seen.has(key))
|
|
@@ -68193,6 +68352,35 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
68193
68352
|
}
|
|
68194
68353
|
continue;
|
|
68195
68354
|
}
|
|
68355
|
+
if (is("SE-MSG-SEMICOLON-UNESCAPED", e)) {
|
|
68356
|
+
const lineText = lineTextAt(text, e.line);
|
|
68357
|
+
const arrows = ["<<-->>", "<<->>", "-->>", "->>", "-->", "->", "--x", "-x", "--)", "-)"];
|
|
68358
|
+
let ai = -1;
|
|
68359
|
+
let alen = 0;
|
|
68360
|
+
for (const a of arrows) {
|
|
68361
|
+
const idx = lineText.indexOf(a);
|
|
68362
|
+
if (idx !== -1 && (ai === -1 || idx < ai)) {
|
|
68363
|
+
ai = idx;
|
|
68364
|
+
alen = a.length;
|
|
68365
|
+
}
|
|
68366
|
+
}
|
|
68367
|
+
if (ai !== -1) {
|
|
68368
|
+
const colonIdx = lineText.indexOf(":", ai + alen);
|
|
68369
|
+
if (colonIdx !== -1) {
|
|
68370
|
+
const head2 = lineText.slice(0, colonIdx + 1);
|
|
68371
|
+
const tail = lineText.slice(colonIdx + 1);
|
|
68372
|
+
const fixedTail = escapeUnescapedSemicolons(tail);
|
|
68373
|
+
if (fixedTail !== tail) {
|
|
68374
|
+
edits.push({
|
|
68375
|
+
start: { line: e.line, column: 1 },
|
|
68376
|
+
end: { line: e.line, column: lineText.length + 1 },
|
|
68377
|
+
newText: head2 + fixedTail
|
|
68378
|
+
});
|
|
68379
|
+
}
|
|
68380
|
+
}
|
|
68381
|
+
}
|
|
68382
|
+
continue;
|
|
68383
|
+
}
|
|
68196
68384
|
if (is("SE-NOTE-MALFORMED", e)) {
|
|
68197
68385
|
const lineText = lineTextAt(text, e.line);
|
|
68198
68386
|
const mLR = /^(\s*)Note\s+(left|right)\s+of\s+(.+?)\s+(.+)$/.exec(lineText);
|
|
@@ -102912,19 +103100,23 @@ ${activeToolsDesc}
|
|
|
102912
103100
|
- Time budget remaining: ${Math.round(remainingBudgetMs / 6e4)} minutes
|
|
102913
103101
|
- Max per extension: ${maxPerReqMin} minutes
|
|
102914
103102
|
|
|
102915
|
-
|
|
103103
|
+
Your DEFAULT should be to EXTEND. The agent is working on a task and deserves time to finish. Only decline if you see clear evidence of a problem.
|
|
103104
|
+
|
|
103105
|
+
EXTEND (this should be your default) if:
|
|
102916
103106
|
- Tools are actively running (especially delegates or complex analysis) \u2014 they need time to finish
|
|
102917
|
-
- The agent is making
|
|
103107
|
+
- The agent is making progress, even if slowly \u2014 complex tasks take time
|
|
102918
103108
|
- New information is being gathered that will improve the final answer
|
|
103109
|
+
- The agent is iterating on an approach (trying different searches, refining queries) \u2014 this is normal problem-solving, not a loop
|
|
103110
|
+
- There is remaining budget and the task is not yet complete
|
|
103111
|
+
- When in doubt, extend \u2014 it's better to give the agent a chance than to cut it off prematurely
|
|
102919
103112
|
|
|
102920
|
-
DO NOT EXTEND if:
|
|
102921
|
-
- The agent
|
|
102922
|
-
- The
|
|
102923
|
-
-
|
|
102924
|
-
-
|
|
102925
|
-
- The agent is doing redundant work (searching for things it already found)
|
|
103113
|
+
DO NOT EXTEND only if you see CLEAR evidence of:
|
|
103114
|
+
- The agent is stuck in an obvious loop \u2014 repeating the EXACT same tool calls with the EXACT same arguments and getting the same errors back-to-back (3+ times)
|
|
103115
|
+
- The agent is retrying a fundamentally broken operation without changing its approach at all
|
|
103116
|
+
- Tool calls are consistently returning errors or empty results AND the agent is not adapting
|
|
103117
|
+
- The conversation clearly shows the agent has all the information it needs and is just making redundant calls
|
|
102926
103118
|
|
|
102927
|
-
|
|
103119
|
+
IMPORTANT: Iterating, refining, or trying variations is NOT the same as being stuck in a loop. A loop means identical repeated calls with no variation. Be generous with time \u2014 a slightly longer response time is much better than a prematurely cut-off incomplete answer.
|
|
102928
103120
|
|
|
102929
103121
|
Respond with ONLY valid JSON (no markdown, no explanation):
|
|
102930
103122
|
{"extend": true, "minutes": <1-${maxPerReqMin}>, "reason": "your reason here"}
|
|
@@ -102968,38 +103160,38 @@ or
|
|
|
102968
103160
|
const decision = JSON.parse(jsonStr);
|
|
102969
103161
|
if (decision.extend && decision.minutes > 0) {
|
|
102970
103162
|
const requestedMs = Math.min(decision.minutes, maxPerReqMin) * 6e4;
|
|
102971
|
-
const
|
|
102972
|
-
const
|
|
103163
|
+
const grantedMs2 = Math.min(requestedMs, remainingBudgetMs, negotiatedTimeoutState.maxPerRequestMs);
|
|
103164
|
+
const grantedMin2 = Math.round(grantedMs2 / 6e4 * 10) / 10;
|
|
102973
103165
|
negotiatedTimeoutState.extensionsUsed++;
|
|
102974
|
-
negotiatedTimeoutState.totalExtraTimeMs +=
|
|
102975
|
-
negotiatedTimeoutState.extensionMessage = `\u23F0 Time limit was reached. The timeout observer granted ${
|
|
103166
|
+
negotiatedTimeoutState.totalExtraTimeMs += grantedMs2;
|
|
103167
|
+
negotiatedTimeoutState.extensionMessage = `\u23F0 Time limit was reached. The timeout observer granted ${grantedMin2} more minute(s) (reason: ${decision.reason || "work in progress"}). Extensions remaining: ${negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed}. Continue your work efficiently.`;
|
|
102976
103168
|
negotiatedTimeoutState.softTimeoutId = setTimeout(() => {
|
|
102977
103169
|
runTimeoutObserver();
|
|
102978
|
-
},
|
|
103170
|
+
}, grantedMs2);
|
|
102979
103171
|
if (this.debug) {
|
|
102980
|
-
console.log(`[DEBUG] Timeout observer: granted ${
|
|
103172
|
+
console.log(`[DEBUG] Timeout observer: granted ${grantedMin2} min (reason: ${decision.reason}). Extensions: ${negotiatedTimeoutState.extensionsUsed}/${negotiatedTimeoutState.maxRequests}`);
|
|
102981
103173
|
}
|
|
102982
103174
|
if (this.tracer) {
|
|
102983
103175
|
this.tracer.addEvent("negotiated_timeout.observer_extended", {
|
|
102984
103176
|
decision_reason: decision.reason,
|
|
102985
103177
|
requested_minutes: decision.minutes,
|
|
102986
|
-
granted_ms:
|
|
102987
|
-
granted_min:
|
|
103178
|
+
granted_ms: grantedMs2,
|
|
103179
|
+
granted_min: grantedMin2,
|
|
102988
103180
|
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
102989
103181
|
max_requests: negotiatedTimeoutState.maxRequests,
|
|
102990
103182
|
total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs,
|
|
102991
|
-
budget_remaining_ms: remainingBudgetMs -
|
|
103183
|
+
budget_remaining_ms: remainingBudgetMs - grantedMs2,
|
|
102992
103184
|
active_tools: activeToolsList.map((t) => t.name),
|
|
102993
103185
|
active_tools_count: activeToolsList.length
|
|
102994
103186
|
});
|
|
102995
103187
|
}
|
|
102996
103188
|
this.events.emit("timeout.extended", {
|
|
102997
|
-
grantedMs,
|
|
103189
|
+
grantedMs: grantedMs2,
|
|
102998
103190
|
reason: decision.reason || "work in progress",
|
|
102999
103191
|
extensionsUsed: negotiatedTimeoutState.extensionsUsed,
|
|
103000
103192
|
extensionsRemaining: negotiatedTimeoutState.maxRequests - negotiatedTimeoutState.extensionsUsed,
|
|
103001
103193
|
totalExtraTimeMs: negotiatedTimeoutState.totalExtraTimeMs,
|
|
103002
|
-
budgetRemainingMs: remainingBudgetMs -
|
|
103194
|
+
budgetRemainingMs: remainingBudgetMs - grantedMs2
|
|
103003
103195
|
});
|
|
103004
103196
|
} else {
|
|
103005
103197
|
if (this.debug) {
|
|
@@ -103021,6 +103213,18 @@ or
|
|
|
103021
103213
|
});
|
|
103022
103214
|
await this._initiateGracefulStop(gracefulTimeoutState, `observer declined: ${decision.reason}`);
|
|
103023
103215
|
}
|
|
103216
|
+
return {
|
|
103217
|
+
decision: decision.extend ? "extended" : "declined",
|
|
103218
|
+
reason: decision.reason || "",
|
|
103219
|
+
...decision.extend ? {
|
|
103220
|
+
granted_ms: grantedMs,
|
|
103221
|
+
granted_min: grantedMin,
|
|
103222
|
+
budget_remaining_ms: remainingBudgetMs - grantedMs
|
|
103223
|
+
} : {},
|
|
103224
|
+
extensions_used: negotiatedTimeoutState.extensionsUsed,
|
|
103225
|
+
max_requests: negotiatedTimeoutState.maxRequests,
|
|
103226
|
+
total_extra_time_ms: negotiatedTimeoutState.totalExtraTimeMs
|
|
103227
|
+
};
|
|
103024
103228
|
};
|
|
103025
103229
|
try {
|
|
103026
103230
|
if (this.tracer) {
|
|
@@ -103029,6 +103233,23 @@ or
|
|
|
103029
103233
|
"timeout.extensions_used": negotiatedTimeoutState.extensionsUsed,
|
|
103030
103234
|
"timeout.active_tools_count": activeToolsList.length,
|
|
103031
103235
|
"timeout.remaining_budget_ms": remainingBudgetMs
|
|
103236
|
+
}, (span, result) => {
|
|
103237
|
+
if (result) {
|
|
103238
|
+
span.setAttributes({
|
|
103239
|
+
"observer.decision": result.decision,
|
|
103240
|
+
"observer.reason": result.reason,
|
|
103241
|
+
"observer.extensions_used": result.extensions_used,
|
|
103242
|
+
"observer.max_requests": result.max_requests,
|
|
103243
|
+
"observer.total_extra_time_ms": result.total_extra_time_ms
|
|
103244
|
+
});
|
|
103245
|
+
if (result.decision === "extended") {
|
|
103246
|
+
span.setAttributes({
|
|
103247
|
+
"observer.granted_ms": result.granted_ms,
|
|
103248
|
+
"observer.granted_min": result.granted_min,
|
|
103249
|
+
"observer.budget_remaining_ms": result.budget_remaining_ms
|
|
103250
|
+
});
|
|
103251
|
+
}
|
|
103252
|
+
}
|
|
103032
103253
|
});
|
|
103033
103254
|
} else {
|
|
103034
103255
|
await observerFn();
|
|
@@ -103136,7 +103357,13 @@ or
|
|
|
103136
103357
|
}
|
|
103137
103358
|
return {
|
|
103138
103359
|
toolChoice: "none",
|
|
103139
|
-
userMessage: `\u26A0\uFE0F TIME
|
|
103360
|
+
userMessage: `\u26A0\uFE0F TIME BUDGET EXHAUSTED. Your allocated time for this task has run out. You have ${remaining} step(s) remaining to provide your answer.
|
|
103361
|
+
|
|
103362
|
+
IMPORTANT: This is a time budget constraint, NOT a system shutdown or error. The system is working perfectly \u2014 you simply used all your allocated time.
|
|
103363
|
+
|
|
103364
|
+
Do NOT say things like "the system is shutting down" or "try again later" \u2014 the user submitted a request and is waiting for YOUR answer right now.
|
|
103365
|
+
|
|
103366
|
+
Provide your BEST answer NOW using the information you have already gathered. Do NOT call any more tools. Summarize your findings and respond completely. If something was not completed, honestly state what was not done and provide any partial results or recommendations you can offer.`
|
|
103140
103367
|
};
|
|
103141
103368
|
}
|
|
103142
103369
|
if (this.debug) {
|
|
@@ -103564,7 +103791,9 @@ Respond with ONLY valid JSON \u2014 no markdown, no explanation, no text outside
|
|
|
103564
103791
|
} catch {
|
|
103565
103792
|
}
|
|
103566
103793
|
}
|
|
103567
|
-
const summaryPrompt = `Your
|
|
103794
|
+
const summaryPrompt = `Your allocated time budget for this task has been exhausted. Some of your tool calls were cancelled mid-execution because the timeout observer determined the time limit was reached.
|
|
103795
|
+
|
|
103796
|
+
IMPORTANT: This is a time budget constraint, NOT a system shutdown or error. The system is working perfectly \u2014 you simply used all your allocated time. Do NOT say things like "the system is shutting down" or "try again later." The user is waiting for your answer RIGHT NOW.
|
|
103568
103797
|
|
|
103569
103798
|
Please provide a DETAILED summary of:
|
|
103570
103799
|
1. What you were asked to do (the original task)
|
|
@@ -103599,7 +103828,14 @@ Be thorough \u2014 this is the user's only response. Include all useful informat
|
|
|
103599
103828
|
let summaryText;
|
|
103600
103829
|
if (this.tracer) {
|
|
103601
103830
|
summaryText = await this.tracer.withSpan("negotiated_timeout.abort_summary", summaryFn, {
|
|
103602
|
-
"summary.conversation_messages": currentMessages.length
|
|
103831
|
+
"summary.conversation_messages": currentMessages.length,
|
|
103832
|
+
"observer.was_timeout": true
|
|
103833
|
+
}, (span, result) => {
|
|
103834
|
+
if (result) {
|
|
103835
|
+
span.setAttributes({
|
|
103836
|
+
"observer.summary_length": result.length
|
|
103837
|
+
});
|
|
103838
|
+
}
|
|
103603
103839
|
});
|
|
103604
103840
|
} else {
|
|
103605
103841
|
summaryText = await summaryFn();
|