@probelabs/probe 0.6.0-rc285 → 0.6.0-rc287
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-rc287-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc287-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc287-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc287-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc287-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +20 -3
- package/build/agent/index.js +151 -32
- package/build/downloader.js +5 -1
- package/build/tools/common.js +11 -1
- package/build/tools/vercel.js +38 -7
- package/cjs/agent/ProbeAgent.cjs +151 -32
- package/cjs/index.cjs +151 -32
- package/package.json +3 -3
- package/src/agent/ProbeAgent.js +20 -3
- package/src/downloader.js +5 -1
- package/src/tools/common.js +11 -1
- package/src/tools/vercel.js +38 -7
- package/bin/binaries/probe-v0.6.0-rc285-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc285-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc285-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc285-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc285-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -93,7 +93,7 @@ import { formatAvailableSkillsXml as formatAvailableSkills } from './skills/form
|
|
|
93
93
|
import { createSkillToolInstances } from './skills/tools.js';
|
|
94
94
|
import { RetryManager, createRetryManagerFromEnv } from './RetryManager.js';
|
|
95
95
|
import { FallbackManager, createFallbackManagerFromEnv, buildFallbackProvidersFromEnv } from './FallbackManager.js';
|
|
96
|
-
import { handleContextLimitError } from './contextCompactor.js';
|
|
96
|
+
import { handleContextLimitError, compactMessages, calculateCompactionStats } from './contextCompactor.js';
|
|
97
97
|
import { formatErrorForAI, ParameterError } from '../utils/error-types.js';
|
|
98
98
|
import { getCommonPrefix, toRelativePath, safeRealpath } from '../utils/path-validation.js';
|
|
99
99
|
import { truncateIfNeeded, getMaxOutputTokens } from './outputTruncator.js';
|
|
@@ -3227,6 +3227,25 @@ Follow these instructions carefully:
|
|
|
3227
3227
|
];
|
|
3228
3228
|
}
|
|
3229
3229
|
|
|
3230
|
+
// Proactively compact for multi-turn conversations.
|
|
3231
|
+
// On turn 2+, previous turns contain full tool call/result history which can
|
|
3232
|
+
// be 50K+ tokens. This drowns out the new user message and causes the model to
|
|
3233
|
+
// focus on prior context rather than the new question.
|
|
3234
|
+
// compactMessages strips intermediate monologue from completed segments,
|
|
3235
|
+
// keeping user messages + final answers from prior turns.
|
|
3236
|
+
// Must run AFTER adding the new user message so the compactor sees 2+ segments
|
|
3237
|
+
// (completed prior turns + the new incomplete turn), preserving the latest segment.
|
|
3238
|
+
if (this.history.length > 0) {
|
|
3239
|
+
const compacted = compactMessages(currentMessages, { keepLastSegment: true, minSegmentsToKeep: 1 });
|
|
3240
|
+
if (compacted.length < currentMessages.length) {
|
|
3241
|
+
const stats = calculateCompactionStats(currentMessages, compacted);
|
|
3242
|
+
if (this.debug) {
|
|
3243
|
+
console.log(`[DEBUG] Proactive history compaction: ${currentMessages.length} → ${compacted.length} messages (${stats.reductionPercent}% reduction, ~${stats.tokensSaved} tokens saved)`);
|
|
3244
|
+
}
|
|
3245
|
+
currentMessages = compacted;
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
|
|
3230
3249
|
let currentIteration = 0;
|
|
3231
3250
|
let finalResult = 'I was unable to complete your request due to reaching the maximum number of tool iterations.';
|
|
3232
3251
|
|
|
@@ -4141,8 +4160,6 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
4141
4160
|
* @returns {Object} Compaction statistics
|
|
4142
4161
|
*/
|
|
4143
4162
|
async compactHistory(options = {}) {
|
|
4144
|
-
const { compactMessages, calculateCompactionStats } = await import('./contextCompactor.js');
|
|
4145
|
-
|
|
4146
4163
|
if (this.history.length === 0) {
|
|
4147
4164
|
if (this.debug) {
|
|
4148
4165
|
console.log(`[DEBUG] No history to compact for session ${this.sessionId}`);
|
package/build/agent/index.js
CHANGED
|
@@ -2755,7 +2755,11 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2755
2755
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2756
2756
|
console.log(`Extracting zip to ${extractDir}...`);
|
|
2757
2757
|
}
|
|
2758
|
-
|
|
2758
|
+
if (isWindows) {
|
|
2759
|
+
await exec(`powershell -NoProfile -Command "Expand-Archive -Path '${assetPath}' -DestinationPath '${extractDir}' -Force"`);
|
|
2760
|
+
} else {
|
|
2761
|
+
await exec(`unzip -q "${assetPath}" -d "${extractDir}"`);
|
|
2762
|
+
}
|
|
2759
2763
|
} else {
|
|
2760
2764
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2761
2765
|
console.log(`Copying binary directly to ${binaryPath}`);
|
|
@@ -8929,7 +8933,14 @@ function parseTargets(targets) {
|
|
|
8929
8933
|
}
|
|
8930
8934
|
function parseAndResolvePaths(pathStr, cwd) {
|
|
8931
8935
|
if (!pathStr) return [];
|
|
8932
|
-
|
|
8936
|
+
let paths = pathStr.split(",").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
8937
|
+
paths = paths.flatMap((p) => {
|
|
8938
|
+
if (!/\s/.test(p)) return [p];
|
|
8939
|
+
const parts = p.split(/\s+/).filter(Boolean);
|
|
8940
|
+
if (parts.length <= 1) return [p];
|
|
8941
|
+
const allLookLikePaths = parts.every((part) => /[/\\]/.test(part) || /\.\w+/.test(part));
|
|
8942
|
+
return allLookLikePaths ? parts : [p];
|
|
8943
|
+
});
|
|
8933
8944
|
return paths.map((p) => {
|
|
8934
8945
|
if (isAbsolute(p)) {
|
|
8935
8946
|
return p;
|
|
@@ -9163,11 +9174,24 @@ function normalizeTargets(targets) {
|
|
|
9163
9174
|
if (typeof target !== "string") continue;
|
|
9164
9175
|
const trimmed = target.trim();
|
|
9165
9176
|
if (!trimmed || seen.has(trimmed)) continue;
|
|
9166
|
-
|
|
9167
|
-
|
|
9177
|
+
const subTargets = splitSpaceSeparatedPaths(trimmed);
|
|
9178
|
+
for (const sub of subTargets) {
|
|
9179
|
+
if (!seen.has(sub)) {
|
|
9180
|
+
seen.add(sub);
|
|
9181
|
+
normalized.push(sub);
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9168
9184
|
}
|
|
9169
9185
|
return normalized;
|
|
9170
9186
|
}
|
|
9187
|
+
function splitSpaceSeparatedPaths(target) {
|
|
9188
|
+
if (!/\s/.test(target)) return [target];
|
|
9189
|
+
const parts = target.split(/\s+/).filter(Boolean);
|
|
9190
|
+
if (parts.length <= 1) return [target];
|
|
9191
|
+
const allLookLikePaths = parts.every((p) => /[/\\]/.test(p) || /\.\w+/.test(p));
|
|
9192
|
+
if (allLookLikePaths) return parts;
|
|
9193
|
+
return [target];
|
|
9194
|
+
}
|
|
9171
9195
|
function extractJsonSnippet(text) {
|
|
9172
9196
|
const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)```/i);
|
|
9173
9197
|
if (jsonBlockMatch) {
|
|
@@ -9651,14 +9675,15 @@ var init_vercel = __esm({
|
|
|
9651
9675
|
const parsedTargets = parseTargets(targets);
|
|
9652
9676
|
extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
|
|
9653
9677
|
if (options.allowedFolders && options.allowedFolders.length > 0) {
|
|
9678
|
+
const { join: pathJoin, sep: pathSep } = await import("path");
|
|
9654
9679
|
extractFiles = extractFiles.map((target) => {
|
|
9655
9680
|
const { filePart, suffix } = splitTargetSuffix(target);
|
|
9656
9681
|
if (existsSync(filePart)) return target;
|
|
9657
|
-
const cwdPrefix = effectiveCwd.endsWith(
|
|
9682
|
+
const cwdPrefix = effectiveCwd.endsWith(pathSep) ? effectiveCwd : effectiveCwd + pathSep;
|
|
9658
9683
|
const relativePart = filePart.startsWith(cwdPrefix) ? filePart.slice(cwdPrefix.length) : null;
|
|
9659
9684
|
if (relativePart) {
|
|
9660
9685
|
for (const folder of options.allowedFolders) {
|
|
9661
|
-
const candidate = folder
|
|
9686
|
+
const candidate = pathJoin(folder, relativePart);
|
|
9662
9687
|
if (existsSync(candidate)) {
|
|
9663
9688
|
if (debug) console.error(`[extract] Auto-fixed path: ${filePart} \u2192 ${candidate}`);
|
|
9664
9689
|
return candidate + suffix;
|
|
@@ -9666,11 +9691,12 @@ var init_vercel = __esm({
|
|
|
9666
9691
|
}
|
|
9667
9692
|
}
|
|
9668
9693
|
for (const folder of options.allowedFolders) {
|
|
9669
|
-
const folderPrefix = folder.endsWith(
|
|
9670
|
-
const
|
|
9694
|
+
const folderPrefix = folder.endsWith(pathSep) ? folder : folder + pathSep;
|
|
9695
|
+
const sepEscaped = pathSep === "\\" ? "\\\\" : pathSep;
|
|
9696
|
+
const wsParent = folderPrefix.replace(new RegExp("[^" + sepEscaped + "]+" + sepEscaped + "$"), "");
|
|
9671
9697
|
if (filePart.startsWith(wsParent)) {
|
|
9672
9698
|
const tail = filePart.slice(wsParent.length);
|
|
9673
|
-
const candidate = folderPrefix
|
|
9699
|
+
const candidate = pathJoin(folderPrefix, tail);
|
|
9674
9700
|
if (candidate !== filePart && existsSync(candidate)) {
|
|
9675
9701
|
if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} \u2192 ${candidate}`);
|
|
9676
9702
|
return candidate + suffix;
|
|
@@ -44301,6 +44327,7 @@ var init_parser2 = __esm({
|
|
|
44301
44327
|
{ ALT: () => this.CONSUME(Identifier) },
|
|
44302
44328
|
{ ALT: () => this.CONSUME(Text) },
|
|
44303
44329
|
{ ALT: () => this.CONSUME(NumberLiteral) },
|
|
44330
|
+
{ ALT: () => this.CONSUME(ColorValue) },
|
|
44304
44331
|
// Note: RoundOpen and RoundClose (parentheses) are NOT allowed in unquoted labels
|
|
44305
44332
|
// to match Mermaid's behavior - use quoted labels like ["text (with parens)"] instead
|
|
44306
44333
|
// Allow HTML-like tags (e.g., <br/>) inside labels
|
|
@@ -44392,6 +44419,7 @@ var init_parser2 = __esm({
|
|
|
44392
44419
|
{ ALT: () => this.CONSUME(Identifier) },
|
|
44393
44420
|
{ ALT: () => this.CONSUME(Text) },
|
|
44394
44421
|
{ ALT: () => this.CONSUME(NumberLiteral) },
|
|
44422
|
+
{ ALT: () => this.CONSUME(ColorValue) },
|
|
44395
44423
|
// Allow HTML-like angle brackets and slashes for <br/>, <i>, etc.
|
|
44396
44424
|
{ ALT: () => this.CONSUME(AngleLess) },
|
|
44397
44425
|
{ ALT: () => this.CONSUME(AngleOpen) },
|
|
@@ -45567,13 +45595,24 @@ function mapFlowchartParserError(err, text) {
|
|
|
45567
45595
|
length: len
|
|
45568
45596
|
};
|
|
45569
45597
|
}
|
|
45570
|
-
if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose") {
|
|
45598
|
+
if (tokType === "QuotedString" || tokType === "SquareOpen" || tokType === "SquareClose" || tokType === "DiamondOpen" || tokType === "DiamondClose") {
|
|
45571
45599
|
const context = err?.context;
|
|
45572
45600
|
const inLinkRule = context?.ruleStack?.includes("linkTextInline") || context?.ruleStack?.includes("link") || false;
|
|
45573
45601
|
const lineContent = allLines[Math.max(0, line - 1)] || "";
|
|
45574
45602
|
const beforeQuote = lineContent.slice(0, Math.max(0, column - 1));
|
|
45575
45603
|
const hasLinkBefore = beforeQuote.match(/--\s*$|==\s*$|-\.\s*$|-\.-\s*$|\[\s*$/);
|
|
45576
45604
|
if (inLinkRule || hasLinkBefore) {
|
|
45605
|
+
if (tokType === "DiamondOpen" || tokType === "DiamondClose") {
|
|
45606
|
+
return {
|
|
45607
|
+
line,
|
|
45608
|
+
column,
|
|
45609
|
+
severity: "error",
|
|
45610
|
+
code: "FL-EDGE-LABEL-CURLY-IN-PIPES",
|
|
45611
|
+
message: "Curly braces { } are not supported inside pipe-delimited edge labels.",
|
|
45612
|
+
hint: "Use HTML entities { and } inside |...|, e.g., --|Resolve ${VAR}|-->",
|
|
45613
|
+
length: len
|
|
45614
|
+
};
|
|
45615
|
+
}
|
|
45577
45616
|
if (tokType === "SquareOpen" || tokType === "SquareClose") {
|
|
45578
45617
|
return {
|
|
45579
45618
|
line,
|
|
@@ -45635,6 +45674,17 @@ function mapFlowchartParserError(err, text) {
|
|
|
45635
45674
|
length: len
|
|
45636
45675
|
};
|
|
45637
45676
|
}
|
|
45677
|
+
if (tokType === "DiamondOpen" || tokType === "DiamondClose") {
|
|
45678
|
+
return {
|
|
45679
|
+
line,
|
|
45680
|
+
column,
|
|
45681
|
+
severity: "error",
|
|
45682
|
+
code: "FL-LABEL-CURLY-IN-UNQUOTED",
|
|
45683
|
+
message: "Curly braces are not supported inside unquoted node labels.",
|
|
45684
|
+
hint: "Use { and } for literal braces, e.g., C[Substitute {params}].",
|
|
45685
|
+
length: len
|
|
45686
|
+
};
|
|
45687
|
+
}
|
|
45638
45688
|
{
|
|
45639
45689
|
const caret0 = Math.max(0, column - 1);
|
|
45640
45690
|
const openIdx = lineStr.lastIndexOf("[", caret0);
|
|
@@ -45676,9 +45726,20 @@ function mapFlowchartParserError(err, text) {
|
|
|
45676
45726
|
length: len
|
|
45677
45727
|
};
|
|
45678
45728
|
}
|
|
45729
|
+
if (seg.includes("{") || seg.includes("}")) {
|
|
45730
|
+
return {
|
|
45731
|
+
line,
|
|
45732
|
+
column,
|
|
45733
|
+
severity: "error",
|
|
45734
|
+
code: "FL-LABEL-CURLY-IN-UNQUOTED",
|
|
45735
|
+
message: "Curly braces are not supported inside unquoted node labels.",
|
|
45736
|
+
hint: "Use { and } for literal braces, e.g., C[Substitute {params}].",
|
|
45737
|
+
length: len
|
|
45738
|
+
};
|
|
45739
|
+
}
|
|
45679
45740
|
}
|
|
45680
45741
|
}
|
|
45681
|
-
if (tokType === "QuotedString"
|
|
45742
|
+
if (tokType === "QuotedString") {
|
|
45682
45743
|
return {
|
|
45683
45744
|
line,
|
|
45684
45745
|
column,
|
|
@@ -45689,6 +45750,17 @@ function mapFlowchartParserError(err, text) {
|
|
|
45689
45750
|
length: len
|
|
45690
45751
|
};
|
|
45691
45752
|
}
|
|
45753
|
+
if (tokType === "SquareOpen" || tokType === "SquareClose") {
|
|
45754
|
+
return {
|
|
45755
|
+
line,
|
|
45756
|
+
column,
|
|
45757
|
+
severity: "error",
|
|
45758
|
+
code: "FL-LABEL-BRACKET-IN-UNQUOTED",
|
|
45759
|
+
message: "Square brackets are not supported inside unquoted node labels.",
|
|
45760
|
+
hint: 'Use [ and ] for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
|
|
45761
|
+
length: len
|
|
45762
|
+
};
|
|
45763
|
+
}
|
|
45692
45764
|
const q = findInnerQuoteIssue("[");
|
|
45693
45765
|
if (q?.kind === "escaped") {
|
|
45694
45766
|
return { line, column: q.column, severity: "error", code: "FL-LABEL-ESCAPED-QUOTE", message: 'Escaped quotes (\\") in node labels are not supported by Mermaid. Use " instead.', hint: 'Prefer "He said "Hi"".', length: 2 };
|
|
@@ -45699,7 +45771,7 @@ function mapFlowchartParserError(err, text) {
|
|
|
45699
45771
|
return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '['. Add a matching ']' before the arrow or newline.", hint: "Example: A[Label] --> B", length: 1 };
|
|
45700
45772
|
}
|
|
45701
45773
|
if (expecting(err, "RoundClose")) {
|
|
45702
|
-
if (tokType === "QuotedString"
|
|
45774
|
+
if (tokType === "QuotedString") {
|
|
45703
45775
|
return {
|
|
45704
45776
|
line,
|
|
45705
45777
|
column,
|
|
@@ -45710,6 +45782,17 @@ function mapFlowchartParserError(err, text) {
|
|
|
45710
45782
|
length: len
|
|
45711
45783
|
};
|
|
45712
45784
|
}
|
|
45785
|
+
if (tokType === "SquareOpen" || tokType === "SquareClose") {
|
|
45786
|
+
return {
|
|
45787
|
+
line,
|
|
45788
|
+
column,
|
|
45789
|
+
severity: "error",
|
|
45790
|
+
code: "FL-LABEL-BRACKET-IN-UNQUOTED",
|
|
45791
|
+
message: "Square brackets are not supported inside unquoted node labels.",
|
|
45792
|
+
hint: 'Use [ and ] for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
|
|
45793
|
+
length: len
|
|
45794
|
+
};
|
|
45795
|
+
}
|
|
45713
45796
|
{
|
|
45714
45797
|
const caret0 = Math.max(0, column - 1);
|
|
45715
45798
|
const openIdx = lineStr.lastIndexOf("(", caret0);
|
|
@@ -45738,7 +45821,7 @@ function mapFlowchartParserError(err, text) {
|
|
|
45738
45821
|
return { line, column, severity: "error", code: "FL-NODE-UNCLOSED-BRACKET", message: "Unclosed '('. Add a matching ')'.", hint: "Example: B(Label)", length: 1 };
|
|
45739
45822
|
}
|
|
45740
45823
|
if (expecting(err, "DiamondClose")) {
|
|
45741
|
-
if (tokType === "QuotedString"
|
|
45824
|
+
if (tokType === "QuotedString") {
|
|
45742
45825
|
return {
|
|
45743
45826
|
line,
|
|
45744
45827
|
column,
|
|
@@ -45749,6 +45832,17 @@ function mapFlowchartParserError(err, text) {
|
|
|
45749
45832
|
length: len
|
|
45750
45833
|
};
|
|
45751
45834
|
}
|
|
45835
|
+
if (tokType === "SquareOpen" || tokType === "SquareClose") {
|
|
45836
|
+
return {
|
|
45837
|
+
line,
|
|
45838
|
+
column,
|
|
45839
|
+
severity: "error",
|
|
45840
|
+
code: "FL-LABEL-BRACKET-IN-UNQUOTED",
|
|
45841
|
+
message: "Square brackets are not supported inside unquoted node labels.",
|
|
45842
|
+
hint: 'Use [ and ] for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]',
|
|
45843
|
+
length: len
|
|
45844
|
+
};
|
|
45845
|
+
}
|
|
45752
45846
|
{
|
|
45753
45847
|
const caret0 = Math.max(0, column - 1);
|
|
45754
45848
|
const openIdx = lineStr.lastIndexOf("{", caret0);
|
|
@@ -46591,7 +46685,7 @@ function validateFlowchart(text, options = {}) {
|
|
|
46591
46685
|
const byLine = /* @__PURE__ */ new Map();
|
|
46592
46686
|
const collect = (arr) => {
|
|
46593
46687
|
for (const e of arr || []) {
|
|
46594
|
-
if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED" || e.code === "FL-LABEL-SLASH-UNQUOTED")) {
|
|
46688
|
+
if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED" || e.code === "FL-LABEL-SLASH-UNQUOTED" || e.code === "FL-LABEL-CURLY-IN-UNQUOTED" || e.code === "FL-LABEL-BRACKET-IN-UNQUOTED")) {
|
|
46595
46689
|
const ln = e.line ?? 0;
|
|
46596
46690
|
const col = e.column ?? 1;
|
|
46597
46691
|
const list = byLine.get(ln) || [];
|
|
@@ -46673,6 +46767,9 @@ function validateFlowchart(text, options = {}) {
|
|
|
46673
46767
|
const hasParens = seg.includes("(") || seg.includes(")");
|
|
46674
46768
|
const hasAt = seg.includes("@");
|
|
46675
46769
|
const hasQuote = seg.includes('"');
|
|
46770
|
+
const hasCurly = seg.includes("{") || seg.includes("}");
|
|
46771
|
+
const hasBracket = seg.includes("[") || seg.includes("]");
|
|
46772
|
+
const isDoubleSquare = raw.slice(i, i + 2) === "[[" && raw.slice(j - 2, j) === "]]";
|
|
46676
46773
|
const isSingleQuoted = /^'[^]*'$/.test(trimmed);
|
|
46677
46774
|
const hasLeadingSlash = lsp === "/" || lsp === "\\";
|
|
46678
46775
|
if (!covered && !isQuoted && !isParenWrapped && hasParens) {
|
|
@@ -46690,6 +46787,16 @@ function validateFlowchart(text, options = {}) {
|
|
|
46690
46787
|
existing.push({ start: startCol, end: endCol });
|
|
46691
46788
|
byLine.set(ln, existing);
|
|
46692
46789
|
}
|
|
46790
|
+
if (!covered && !isQuoted && !isSlashPair && hasCurly) {
|
|
46791
|
+
errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-CURLY-IN-UNQUOTED", message: "Curly braces are not supported inside unquoted node labels.", hint: "Use { and } for literal braces, e.g., C[Substitute {params}]." });
|
|
46792
|
+
existing.push({ start: startCol, end: endCol });
|
|
46793
|
+
byLine.set(ln, existing);
|
|
46794
|
+
}
|
|
46795
|
+
if (!covered && !isQuoted && !isSlashPair && !isDoubleSquare && hasBracket) {
|
|
46796
|
+
errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-BRACKET-IN-UNQUOTED", message: "Square brackets are not supported inside unquoted node labels.", hint: 'Use [ and ] for literal brackets or wrap the label in quotes, e.g., C["bind_paths[]"]' });
|
|
46797
|
+
existing.push({ start: startCol, end: endCol });
|
|
46798
|
+
byLine.set(ln, existing);
|
|
46799
|
+
}
|
|
46693
46800
|
if (!covered && !isQuoted && !isSlashPair && hasQuote && !isSingleQuoted) {
|
|
46694
46801
|
errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-QUOTE-IN-UNQUOTED", message: "Quotes are not allowed inside unquoted node labels. Use " for quotes or wrap the entire label in quotes.", hint: 'Example: C["HTML Output: data-trigger-visibility="true""]' });
|
|
46695
46802
|
existing.push({ start: startCol, end: endCol });
|
|
@@ -49050,7 +49157,7 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
49050
49157
|
}
|
|
49051
49158
|
continue;
|
|
49052
49159
|
}
|
|
49053
|
-
if (is("FL-EDGE-LABEL-BRACKET", e)) {
|
|
49160
|
+
if (is("FL-EDGE-LABEL-BRACKET", e) || is("FL-EDGE-LABEL-CURLY-IN-PIPES", e)) {
|
|
49054
49161
|
const lineText = lineTextAt(text, e.line);
|
|
49055
49162
|
const firstBar = lineText.indexOf("|");
|
|
49056
49163
|
const secondBar = firstBar >= 0 ? lineText.indexOf("|", firstBar + 1) : -1;
|
|
@@ -49058,7 +49165,8 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
49058
49165
|
const before = lineText.slice(0, firstBar + 1);
|
|
49059
49166
|
const label = lineText.slice(firstBar + 1, secondBar);
|
|
49060
49167
|
const after = lineText.slice(secondBar);
|
|
49061
|
-
|
|
49168
|
+
let fixedLabel = label.replace(/\[/g, "[").replace(/\]/g, "]");
|
|
49169
|
+
fixedLabel = fixedLabel.replace(/\{/g, "{").replace(/\}/g, "}");
|
|
49062
49170
|
const fixedLine = before + fixedLabel + after;
|
|
49063
49171
|
const finalLine = fixedLine.replace(/\[([^\]]*)\]/g, (m, seg) => "[" + String(seg).replace(/`/g, "") + "]");
|
|
49064
49172
|
edits.push({ start: { line: e.line, column: 1 }, end: { line: e.line, column: lineText.length + 1 }, newText: finalLine });
|
|
@@ -49698,7 +49806,7 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
49698
49806
|
}
|
|
49699
49807
|
continue;
|
|
49700
49808
|
}
|
|
49701
|
-
if (is("FL-LABEL-PARENS-UNQUOTED", e) || is("FL-LABEL-AT-IN-UNQUOTED", e) || is("FL-LABEL-SLASH-UNQUOTED", e)) {
|
|
49809
|
+
if (is("FL-LABEL-PARENS-UNQUOTED", e) || is("FL-LABEL-AT-IN-UNQUOTED", e) || is("FL-LABEL-SLASH-UNQUOTED", e) || is("FL-LABEL-CURLY-IN-UNQUOTED", e) || is("FL-LABEL-BRACKET-IN-UNQUOTED", e)) {
|
|
49702
49810
|
if (level === "safe" || level === "all") {
|
|
49703
49811
|
if (patchedLines.has(e.line))
|
|
49704
49812
|
continue;
|
|
@@ -49739,7 +49847,7 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
49739
49847
|
if (openIdx === -1)
|
|
49740
49848
|
break;
|
|
49741
49849
|
const contentStart = openIdx + shape.open.length;
|
|
49742
|
-
const closeIdx = shape.open === "(" && shape.close === ")" ? findMatchingCloser(lineText, openIdx, shape.open, shape.close) : lineText.indexOf(shape.close, contentStart);
|
|
49850
|
+
const closeIdx = shape.open === "(" && shape.close === ")" || shape.open === "[" && shape.close === "]" ? findMatchingCloser(lineText, openIdx, shape.open, shape.close) : lineText.indexOf(shape.close, contentStart);
|
|
49743
49851
|
if (closeIdx === -1)
|
|
49744
49852
|
break;
|
|
49745
49853
|
if (openIdx <= caret0 && caret0 < closeIdx) {
|
|
@@ -49759,11 +49867,21 @@ function computeFixes(text, errors, level = "safe") {
|
|
|
49759
49867
|
const isSlashPair = (l, r) => l === "/" && r === "/" || l === "\\" && r === "\\" || l === "/" && r === "\\" || l === "\\" && r === "/";
|
|
49760
49868
|
const isParallelogramShape = core.length >= 2 && isSlashPair(left, right);
|
|
49761
49869
|
let replaced;
|
|
49762
|
-
if (!isParallelogramShape) {
|
|
49763
|
-
const
|
|
49870
|
+
if (is("FL-LABEL-BRACKET-IN-UNQUOTED", e) && !isParallelogramShape) {
|
|
49871
|
+
const hasOtherHazards = /[(){}@]/.test(inner) || inner.includes('"') || inner.includes('\\"');
|
|
49872
|
+
if (hasOtherHazards) {
|
|
49873
|
+
const escaped = inner.replace(/`/g, "").replace(/\\"/g, """).replace(/"/g, """);
|
|
49874
|
+
replaced = '"' + escaped + '"';
|
|
49875
|
+
} else {
|
|
49876
|
+
replaced = inner.replace(/\[/g, "[").replace(/\]/g, "]");
|
|
49877
|
+
}
|
|
49878
|
+
} else if (is("FL-LABEL-CURLY-IN-UNQUOTED", e)) {
|
|
49879
|
+
replaced = inner.replace(/\{/g, "{").replace(/\}/g, "}");
|
|
49880
|
+
} else if (!isParallelogramShape) {
|
|
49881
|
+
const escaped = inner.replace(/`/g, "").replace(/\\"/g, """).replace(/"/g, """);
|
|
49764
49882
|
replaced = '"' + escaped + '"';
|
|
49765
49883
|
} else {
|
|
49766
|
-
replaced = inner.replace(/`/g, "").replace(/\(/g, "(").replace(/\)/g, ")").replace(/\"/g, """).replace(/"/g, """);
|
|
49884
|
+
replaced = inner.replace(/`/g, "").replace(/\(/g, "(").replace(/\)/g, ")").replace(/\[/g, "[").replace(/\]/g, "]").replace(/\\"/g, """).replace(/"/g, """);
|
|
49767
49885
|
}
|
|
49768
49886
|
if (replaced !== inner) {
|
|
49769
49887
|
edits.push({ start: { line: e.line, column: contentStart + 1 }, end: { line: e.line, column: closeIdx + 1 }, newText: replaced });
|
|
@@ -79910,14 +80028,6 @@ var init_FallbackManager = __esm({
|
|
|
79910
80028
|
});
|
|
79911
80029
|
|
|
79912
80030
|
// src/agent/contextCompactor.js
|
|
79913
|
-
var contextCompactor_exports = {};
|
|
79914
|
-
__export(contextCompactor_exports, {
|
|
79915
|
-
calculateCompactionStats: () => calculateCompactionStats,
|
|
79916
|
-
compactMessages: () => compactMessages,
|
|
79917
|
-
handleContextLimitError: () => handleContextLimitError,
|
|
79918
|
-
identifyMessageSegments: () => identifyMessageSegments,
|
|
79919
|
-
isContextLimitError: () => isContextLimitError
|
|
79920
|
-
});
|
|
79921
80031
|
function isContextLimitError(error) {
|
|
79922
80032
|
if (!error) return false;
|
|
79923
80033
|
const errorMessage = (typeof error === "string" ? error : error?.message || "").toLowerCase();
|
|
@@ -84234,6 +84344,16 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
84234
84344
|
userMessage
|
|
84235
84345
|
];
|
|
84236
84346
|
}
|
|
84347
|
+
if (this.history.length > 0) {
|
|
84348
|
+
const compacted = compactMessages(currentMessages, { keepLastSegment: true, minSegmentsToKeep: 1 });
|
|
84349
|
+
if (compacted.length < currentMessages.length) {
|
|
84350
|
+
const stats = calculateCompactionStats(currentMessages, compacted);
|
|
84351
|
+
if (this.debug) {
|
|
84352
|
+
console.log(`[DEBUG] Proactive history compaction: ${currentMessages.length} \u2192 ${compacted.length} messages (${stats.reductionPercent}% reduction, ~${stats.tokensSaved} tokens saved)`);
|
|
84353
|
+
}
|
|
84354
|
+
currentMessages = compacted;
|
|
84355
|
+
}
|
|
84356
|
+
}
|
|
84237
84357
|
let currentIteration = 0;
|
|
84238
84358
|
let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
|
|
84239
84359
|
const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
|
|
@@ -84934,7 +85054,6 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
84934
85054
|
* @returns {Object} Compaction statistics
|
|
84935
85055
|
*/
|
|
84936
85056
|
async compactHistory(options = {}) {
|
|
84937
|
-
const { compactMessages: compactMessages2, calculateCompactionStats: calculateCompactionStats2 } = await Promise.resolve().then(() => (init_contextCompactor(), contextCompactor_exports));
|
|
84938
85057
|
if (this.history.length === 0) {
|
|
84939
85058
|
if (this.debug) {
|
|
84940
85059
|
console.log(`[DEBUG] No history to compact for session ${this.sessionId}`);
|
|
@@ -84949,8 +85068,8 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
84949
85068
|
tokensSaved: 0
|
|
84950
85069
|
};
|
|
84951
85070
|
}
|
|
84952
|
-
const compactedMessages =
|
|
84953
|
-
const stats =
|
|
85071
|
+
const compactedMessages = compactMessages(this.history, options);
|
|
85072
|
+
const stats = calculateCompactionStats(this.history, compactedMessages);
|
|
84954
85073
|
this.history = compactedMessages;
|
|
84955
85074
|
try {
|
|
84956
85075
|
await this.storageAdapter.clearHistory(this.sessionId);
|
package/build/downloader.js
CHANGED
|
@@ -743,7 +743,11 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
743
743
|
if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
|
|
744
744
|
console.log(`Extracting zip to ${extractDir}...`);
|
|
745
745
|
}
|
|
746
|
-
|
|
746
|
+
if (isWindows) {
|
|
747
|
+
await exec(`powershell -NoProfile -Command "Expand-Archive -Path '${assetPath}' -DestinationPath '${extractDir}' -Force"`);
|
|
748
|
+
} else {
|
|
749
|
+
await exec(`unzip -q "${assetPath}" -d "${extractDir}"`);
|
|
750
|
+
}
|
|
747
751
|
} else {
|
|
748
752
|
// Assume it's a direct binary
|
|
749
753
|
if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
|
package/build/tools/common.js
CHANGED
|
@@ -228,7 +228,17 @@ export function parseAndResolvePaths(pathStr, cwd) {
|
|
|
228
228
|
if (!pathStr) return [];
|
|
229
229
|
|
|
230
230
|
// Split on comma and trim whitespace
|
|
231
|
-
|
|
231
|
+
let paths = pathStr.split(',').map(p => p.trim()).filter(p => p.length > 0);
|
|
232
|
+
|
|
233
|
+
// Auto-fix: model sometimes passes space-separated file paths as one string
|
|
234
|
+
// e.g. "src/ranking.rs src/simd_ranking.rs" — split if each part looks like a path
|
|
235
|
+
paths = paths.flatMap(p => {
|
|
236
|
+
if (!/\s/.test(p)) return [p];
|
|
237
|
+
const parts = p.split(/\s+/).filter(Boolean);
|
|
238
|
+
if (parts.length <= 1) return [p];
|
|
239
|
+
const allLookLikePaths = parts.every(part => /[/\\]/.test(part) || /\.\w+/.test(part));
|
|
240
|
+
return allLookLikePaths ? parts : [p];
|
|
241
|
+
});
|
|
232
242
|
|
|
233
243
|
// Resolve relative paths against cwd
|
|
234
244
|
return paths.map(p => {
|
package/build/tools/vercel.js
CHANGED
|
@@ -105,13 +105,42 @@ function normalizeTargets(targets) {
|
|
|
105
105
|
if (typeof target !== 'string') continue;
|
|
106
106
|
const trimmed = target.trim();
|
|
107
107
|
if (!trimmed || seen.has(trimmed)) continue;
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
|
|
109
|
+
// Auto-fix: model sometimes puts multiple space-separated file paths in one string
|
|
110
|
+
// e.g. "src/ranking.rs src/simd_ranking.rs" — split them apart
|
|
111
|
+
const subTargets = splitSpaceSeparatedPaths(trimmed);
|
|
112
|
+
for (const sub of subTargets) {
|
|
113
|
+
if (!seen.has(sub)) {
|
|
114
|
+
seen.add(sub);
|
|
115
|
+
normalized.push(sub);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
return normalized;
|
|
113
121
|
}
|
|
114
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Split a string that may contain multiple space-separated file paths.
|
|
125
|
+
* Detects patterns like "path/file.ext path2/file2.ext" and splits them.
|
|
126
|
+
* Preserves single paths and paths with suffixes like ":10-20" or "#Symbol".
|
|
127
|
+
*/
|
|
128
|
+
function splitSpaceSeparatedPaths(target) {
|
|
129
|
+
// If no spaces, it's a single target
|
|
130
|
+
if (!/\s/.test(target)) return [target];
|
|
131
|
+
|
|
132
|
+
// Split on whitespace and check if parts look like file paths
|
|
133
|
+
const parts = target.split(/\s+/).filter(Boolean);
|
|
134
|
+
if (parts.length <= 1) return [target];
|
|
135
|
+
|
|
136
|
+
// Check if each part looks like a file path (has a dot extension or path separator)
|
|
137
|
+
const allLookLikePaths = parts.every(p => /[/\\]/.test(p) || /\.\w+/.test(p));
|
|
138
|
+
if (allLookLikePaths) return parts;
|
|
139
|
+
|
|
140
|
+
// Not confident these are separate paths — return as-is
|
|
141
|
+
return [target];
|
|
142
|
+
}
|
|
143
|
+
|
|
115
144
|
function extractJsonSnippet(text) {
|
|
116
145
|
const jsonBlockMatch = text.match(/```json\s*([\s\S]*?)```/i);
|
|
117
146
|
if (jsonBlockMatch) {
|
|
@@ -691,19 +720,20 @@ export const extractTool = (options = {}) => {
|
|
|
691
720
|
// model constructs wrong absolute paths (e.g., /workspace/gateway/file.go
|
|
692
721
|
// instead of /workspace/tyk/gateway/file.go)
|
|
693
722
|
if (options.allowedFolders && options.allowedFolders.length > 0) {
|
|
723
|
+
const { join: pathJoin, sep: pathSep } = await import('path');
|
|
694
724
|
extractFiles = extractFiles.map(target => {
|
|
695
725
|
const { filePart, suffix } = splitTargetSuffix(target);
|
|
696
726
|
if (existsSync(filePart)) return target;
|
|
697
727
|
|
|
698
728
|
// Try resolving the relative tail against each allowedFolder
|
|
699
|
-
const cwdPrefix =
|
|
729
|
+
const cwdPrefix = effectiveCwd.endsWith(pathSep) ? effectiveCwd : effectiveCwd + pathSep;
|
|
700
730
|
const relativePart = filePart.startsWith(cwdPrefix)
|
|
701
731
|
? filePart.slice(cwdPrefix.length)
|
|
702
732
|
: null;
|
|
703
733
|
|
|
704
734
|
if (relativePart) {
|
|
705
735
|
for (const folder of options.allowedFolders) {
|
|
706
|
-
const candidate = folder
|
|
736
|
+
const candidate = pathJoin(folder, relativePart);
|
|
707
737
|
if (existsSync(candidate)) {
|
|
708
738
|
if (debug) console.error(`[extract] Auto-fixed path: ${filePart} → ${candidate}`);
|
|
709
739
|
return candidate + suffix;
|
|
@@ -714,11 +744,12 @@ export const extractTool = (options = {}) => {
|
|
|
714
744
|
// Try stripping workspace prefix and resolving against allowedFolders
|
|
715
745
|
// e.g., /tmp/visor-workspaces/abc/gateway/file.go → try each folder + gateway/file.go
|
|
716
746
|
for (const folder of options.allowedFolders) {
|
|
717
|
-
const folderPrefix = folder.endsWith(
|
|
718
|
-
const
|
|
747
|
+
const folderPrefix = folder.endsWith(pathSep) ? folder : folder + pathSep;
|
|
748
|
+
const sepEscaped = pathSep === '\\' ? '\\\\' : pathSep;
|
|
749
|
+
const wsParent = folderPrefix.replace(new RegExp('[^' + sepEscaped + ']+' + sepEscaped + '$'), '');
|
|
719
750
|
if (filePart.startsWith(wsParent)) {
|
|
720
751
|
const tail = filePart.slice(wsParent.length);
|
|
721
|
-
const candidate = folderPrefix
|
|
752
|
+
const candidate = pathJoin(folderPrefix, tail);
|
|
722
753
|
if (candidate !== filePart && existsSync(candidate)) {
|
|
723
754
|
if (debug) console.error(`[extract] Auto-fixed path via workspace: ${filePart} → ${candidate}`);
|
|
724
755
|
return candidate + suffix;
|