@probelabs/probe 0.6.0-rc240 → 0.6.0-rc242
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-rc242-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc242-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc242-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc242-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc242-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.js +25 -0
- package/build/agent/dsl/environment.js +88 -1
- package/build/agent/dsl/validator.js +99 -8
- package/build/agent/index.js +199 -15
- package/build/tools/executePlan.js +86 -3
- package/cjs/agent/ProbeAgent.cjs +202 -17
- package/cjs/index.cjs +18123 -17938
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +25 -0
- package/src/agent/dsl/environment.js +88 -1
- package/src/agent/dsl/validator.js +99 -8
- package/src/tools/executePlan.js +86 -3
- package/bin/binaries/probe-v0.6.0-rc240-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc240-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc240-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc240-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc240-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -102,6 +102,7 @@ import { formatErrorForAI, ParameterError } from '../utils/error-types.js';
|
|
|
102
102
|
import { getCommonPrefix, toRelativePath, safeRealpath } from '../utils/path-validation.js';
|
|
103
103
|
import { truncateIfNeeded, getMaxOutputTokens } from './outputTruncator.js';
|
|
104
104
|
import { DelegationManager } from '../delegate.js';
|
|
105
|
+
import { extractRawOutputBlocks } from '../tools/executePlan.js';
|
|
105
106
|
import {
|
|
106
107
|
TaskManager,
|
|
107
108
|
createTaskTool,
|
|
@@ -3610,6 +3611,18 @@ Follow these instructions carefully:
|
|
|
3610
3611
|
|
|
3611
3612
|
let toolResultContent = typeof executionResult === 'string' ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
3612
3613
|
|
|
3614
|
+
// Extract raw output blocks and pass them through to output buffer (before truncation)
|
|
3615
|
+
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3616
|
+
if (this._outputBuffer) {
|
|
3617
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent, this._outputBuffer);
|
|
3618
|
+
if (extractedBlocks.length > 0) {
|
|
3619
|
+
toolResultContent = cleanedContent;
|
|
3620
|
+
if (this.debug) {
|
|
3621
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) to output buffer`);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3613
3626
|
// Truncate if output exceeds token limit
|
|
3614
3627
|
try {
|
|
3615
3628
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
|
@@ -3856,6 +3869,18 @@ Follow these instructions carefully:
|
|
|
3856
3869
|
toolResultContent = toolResultContent.split(wsPrefix).join('');
|
|
3857
3870
|
}
|
|
3858
3871
|
|
|
3872
|
+
// Extract raw output blocks and pass them through to output buffer (before truncation)
|
|
3873
|
+
// This prevents LLM from processing/hallucinating large structured output from execute_plan
|
|
3874
|
+
if (this._outputBuffer) {
|
|
3875
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent, this._outputBuffer);
|
|
3876
|
+
if (extractedBlocks.length > 0) {
|
|
3877
|
+
toolResultContent = cleanedContent;
|
|
3878
|
+
if (this.debug) {
|
|
3879
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) to output buffer`);
|
|
3880
|
+
}
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
|
|
3859
3884
|
// Truncate if output exceeds token limit
|
|
3860
3885
|
try {
|
|
3861
3886
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
|
@@ -227,9 +227,21 @@ export function generateSandboxGlobals(options) {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
// LLM() built-in — delegate already has its own OTEL, but we add a DSL-level span
|
|
230
|
+
// When schema is provided, auto-parse the JSON result for easier downstream processing
|
|
230
231
|
if (llmCall) {
|
|
231
232
|
const rawLLM = async (instruction, data, opts = {}) => {
|
|
232
|
-
|
|
233
|
+
const result = await llmCall(instruction, data, opts);
|
|
234
|
+
// Auto-parse JSON when schema is provided and result is a string
|
|
235
|
+
if (opts.schema && typeof result === 'string') {
|
|
236
|
+
try {
|
|
237
|
+
return JSON.parse(result);
|
|
238
|
+
} catch (e) {
|
|
239
|
+
// If parsing fails, return the raw string (may have formatting issues)
|
|
240
|
+
logFn?.('[LLM] Warning: schema provided but result is not valid JSON');
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
233
245
|
};
|
|
234
246
|
globals.LLM = traceToolCall('LLM', rawLLM, tracer, logFn);
|
|
235
247
|
}
|
|
@@ -306,6 +318,81 @@ export function generateSandboxGlobals(options) {
|
|
|
306
318
|
return chunks;
|
|
307
319
|
};
|
|
308
320
|
|
|
321
|
+
// chunkByKey() - chunk data ensuring same-key items stay together
|
|
322
|
+
// - Chunks CAN have multiple keys (customers)
|
|
323
|
+
// - But same key NEVER splits across chunks
|
|
324
|
+
globals.chunkByKey = (data, keyFn, maxTokens = 20000) => {
|
|
325
|
+
const CHARS_PER_TOKEN = 4;
|
|
326
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN;
|
|
327
|
+
const text = typeof data === 'string' ? data : JSON.stringify(data);
|
|
328
|
+
|
|
329
|
+
// Find all File: markers
|
|
330
|
+
const blockRegex = /^File: ([^\n]+)/gm;
|
|
331
|
+
const markers = [];
|
|
332
|
+
let match;
|
|
333
|
+
while ((match = blockRegex.exec(text)) !== null) {
|
|
334
|
+
markers.push({ index: match.index, file: match[1].trim() });
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// No File: headers - fallback to regular chunk
|
|
338
|
+
if (markers.length === 0) {
|
|
339
|
+
return globals.chunk(data, maxTokens);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const chunks = [];
|
|
343
|
+
let currentChunk = '';
|
|
344
|
+
let currentSize = 0;
|
|
345
|
+
let keysInChunk = new Set(); // Track which keys are in current chunk
|
|
346
|
+
|
|
347
|
+
// Process each block
|
|
348
|
+
for (let i = 0; i < markers.length; i++) {
|
|
349
|
+
const start = markers[i].index;
|
|
350
|
+
const end = i + 1 < markers.length ? markers[i + 1].index : text.length;
|
|
351
|
+
const block = text.slice(start, end).trim();
|
|
352
|
+
const file = markers[i].file;
|
|
353
|
+
const key = typeof keyFn === 'function' ? keyFn(file) : file;
|
|
354
|
+
|
|
355
|
+
const blockSize = block.length + 2; // +2 for \n\n separator
|
|
356
|
+
const wouldOverflow = currentSize + blockSize > maxChars;
|
|
357
|
+
const keyAlreadyInChunk = keysInChunk.has(key);
|
|
358
|
+
|
|
359
|
+
// Decision logic:
|
|
360
|
+
// - If key already in chunk: MUST add (never split a key)
|
|
361
|
+
// - If new key and would overflow: flush first, then add
|
|
362
|
+
// - If new key and fits: add to current chunk
|
|
363
|
+
|
|
364
|
+
if (!keyAlreadyInChunk && wouldOverflow && currentChunk) {
|
|
365
|
+
// New key would overflow - flush current chunk first
|
|
366
|
+
chunks.push(currentChunk.trim());
|
|
367
|
+
currentChunk = '';
|
|
368
|
+
currentSize = 0;
|
|
369
|
+
keysInChunk = new Set();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Add block to current chunk
|
|
373
|
+
if (currentChunk) currentChunk += '\n\n';
|
|
374
|
+
currentChunk += block;
|
|
375
|
+
currentSize += blockSize;
|
|
376
|
+
keysInChunk.add(key);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Flush final chunk
|
|
380
|
+
if (currentChunk.trim()) {
|
|
381
|
+
chunks.push(currentChunk.trim());
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return chunks.length > 0 ? chunks : [''];
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// extractPaths() - extract unique file paths from search results
|
|
388
|
+
// Parses File: headers and returns deduplicated array of paths
|
|
389
|
+
globals.extractPaths = (searchResults) => {
|
|
390
|
+
const text = typeof searchResults === 'string' ? searchResults : JSON.stringify(searchResults);
|
|
391
|
+
const matches = text.match(/^File: ([^\n]+)/gm) || [];
|
|
392
|
+
const paths = matches.map(m => m.replace('File: ', '').trim());
|
|
393
|
+
return [...new Set(paths)];
|
|
394
|
+
};
|
|
395
|
+
|
|
309
396
|
// Utility functions (pure, no async)
|
|
310
397
|
globals.log = (message) => {
|
|
311
398
|
// Collected by the runtime for the execution log
|
|
@@ -9,6 +9,81 @@
|
|
|
9
9
|
import * as acorn from 'acorn';
|
|
10
10
|
import * as walk from 'acorn-walk';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Convert a character offset to line and column numbers.
|
|
14
|
+
* @param {string} code - The source code
|
|
15
|
+
* @param {number} offset - Character offset
|
|
16
|
+
* @returns {{ line: number, column: number }}
|
|
17
|
+
*/
|
|
18
|
+
function offsetToLineColumn(code, offset) {
|
|
19
|
+
const lines = code.split('\n');
|
|
20
|
+
let pos = 0;
|
|
21
|
+
for (let i = 0; i < lines.length; i++) {
|
|
22
|
+
const lineLength = lines[i].length + 1; // +1 for newline
|
|
23
|
+
if (pos + lineLength > offset) {
|
|
24
|
+
return { line: i + 1, column: offset - pos + 1 };
|
|
25
|
+
}
|
|
26
|
+
pos += lineLength;
|
|
27
|
+
}
|
|
28
|
+
return { line: lines.length, column: 1 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Generate a code snippet with an arrow pointing to the error location.
|
|
33
|
+
* @param {string} code - The source code
|
|
34
|
+
* @param {number} line - Line number (1-based)
|
|
35
|
+
* @param {number} column - Column number (1-based)
|
|
36
|
+
* @param {number} contextLines - Number of lines to show before/after (default: 2)
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function generateErrorSnippet(code, line, column, contextLines = 2) {
|
|
40
|
+
const lines = code.split('\n');
|
|
41
|
+
const startLine = Math.max(0, line - 1 - contextLines);
|
|
42
|
+
const endLine = Math.min(lines.length, line + contextLines);
|
|
43
|
+
|
|
44
|
+
const snippetLines = [];
|
|
45
|
+
const lineNumWidth = String(endLine).length;
|
|
46
|
+
|
|
47
|
+
for (let i = startLine; i < endLine; i++) {
|
|
48
|
+
const lineNum = String(i + 1).padStart(lineNumWidth, ' ');
|
|
49
|
+
const marker = (i + 1 === line) ? '>' : ' ';
|
|
50
|
+
snippetLines.push(`${marker} ${lineNum} | ${lines[i]}`);
|
|
51
|
+
|
|
52
|
+
// Add arrow line for the error line
|
|
53
|
+
if (i + 1 === line) {
|
|
54
|
+
const padding = ' '.repeat(lineNumWidth + 4); // " 123 | " prefix
|
|
55
|
+
const arrow = ' '.repeat(Math.max(0, column - 1)) + '^';
|
|
56
|
+
snippetLines.push(`${padding}${arrow}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return snippetLines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Format an error message with code snippet.
|
|
65
|
+
* @param {string} message - The error message
|
|
66
|
+
* @param {string} code - The source code
|
|
67
|
+
* @param {number} offset - Character offset (optional, use -1 if line/column provided)
|
|
68
|
+
* @param {number} line - Line number (optional)
|
|
69
|
+
* @param {number} column - Column number (optional)
|
|
70
|
+
* @returns {string}
|
|
71
|
+
*/
|
|
72
|
+
function formatErrorWithSnippet(message, code, offset = -1, line = 0, column = 0) {
|
|
73
|
+
if (offset >= 0) {
|
|
74
|
+
const loc = offsetToLineColumn(code, offset);
|
|
75
|
+
line = loc.line;
|
|
76
|
+
column = loc.column;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (line <= 0) {
|
|
80
|
+
return message;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const snippet = generateErrorSnippet(code, line, column);
|
|
84
|
+
return `${message}\n\n${snippet}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
12
87
|
// Node types the LLM is allowed to generate
|
|
13
88
|
const ALLOWED_NODE_TYPES = new Set([
|
|
14
89
|
'Program',
|
|
@@ -102,16 +177,32 @@ export function validateDSL(code) {
|
|
|
102
177
|
ecmaVersion: 2022,
|
|
103
178
|
sourceType: 'script',
|
|
104
179
|
allowReturnOutsideFunction: true,
|
|
180
|
+
locations: true, // Enable location tracking for better error messages
|
|
105
181
|
});
|
|
106
182
|
} catch (e) {
|
|
107
|
-
|
|
183
|
+
// Acorn errors have loc property with line/column
|
|
184
|
+
const line = e.loc?.line || 0;
|
|
185
|
+
const column = e.loc?.column ? e.loc.column + 1 : 0; // Acorn column is 0-based
|
|
186
|
+
const formattedError = formatErrorWithSnippet(
|
|
187
|
+
`Syntax error: ${e.message}`,
|
|
188
|
+
code,
|
|
189
|
+
-1,
|
|
190
|
+
line,
|
|
191
|
+
column
|
|
192
|
+
);
|
|
193
|
+
return { valid: false, errors: [formattedError] };
|
|
108
194
|
}
|
|
109
195
|
|
|
196
|
+
// Helper to add error with code snippet
|
|
197
|
+
const addError = (message, position) => {
|
|
198
|
+
errors.push(formatErrorWithSnippet(message, code, position));
|
|
199
|
+
};
|
|
200
|
+
|
|
110
201
|
// Step 2: Walk every node and validate
|
|
111
202
|
walk.full(ast, (node) => {
|
|
112
203
|
// Check node type against whitelist
|
|
113
204
|
if (!ALLOWED_NODE_TYPES.has(node.type)) {
|
|
114
|
-
|
|
205
|
+
addError(`Blocked node type: ${node.type}`, node.start);
|
|
115
206
|
return;
|
|
116
207
|
}
|
|
117
208
|
|
|
@@ -121,7 +212,7 @@ export function validateDSL(code) {
|
|
|
121
212
|
node.type === 'FunctionExpression') &&
|
|
122
213
|
node.async
|
|
123
214
|
) {
|
|
124
|
-
|
|
215
|
+
addError(`Async functions are not allowed. Write synchronous code — the runtime handles async.`, node.start);
|
|
125
216
|
}
|
|
126
217
|
|
|
127
218
|
// Block generator functions
|
|
@@ -129,19 +220,19 @@ export function validateDSL(code) {
|
|
|
129
220
|
(node.type === 'FunctionExpression') &&
|
|
130
221
|
node.generator
|
|
131
222
|
) {
|
|
132
|
-
|
|
223
|
+
addError(`Generator functions are not allowed`, node.start);
|
|
133
224
|
}
|
|
134
225
|
|
|
135
226
|
|
|
136
227
|
// Check identifiers against blocklist
|
|
137
228
|
if (node.type === 'Identifier' && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
138
|
-
|
|
229
|
+
addError(`Blocked identifier: '${node.name}'`, node.start);
|
|
139
230
|
}
|
|
140
231
|
|
|
141
232
|
// Check member expressions for blocked properties
|
|
142
233
|
if (node.type === 'MemberExpression' && !node.computed) {
|
|
143
234
|
if (node.property.type === 'Identifier' && BLOCKED_PROPERTIES.has(node.property.name)) {
|
|
144
|
-
|
|
235
|
+
addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
|
|
145
236
|
}
|
|
146
237
|
}
|
|
147
238
|
|
|
@@ -149,7 +240,7 @@ export function validateDSL(code) {
|
|
|
149
240
|
if (node.type === 'MemberExpression' && node.computed) {
|
|
150
241
|
if (node.property.type === 'Literal' && typeof node.property.value === 'string') {
|
|
151
242
|
if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
|
|
152
|
-
|
|
243
|
+
addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
|
|
153
244
|
}
|
|
154
245
|
}
|
|
155
246
|
}
|
|
@@ -157,7 +248,7 @@ export function validateDSL(code) {
|
|
|
157
248
|
// Block variable declarations named with blocked identifiers
|
|
158
249
|
if (node.type === 'VariableDeclarator' && node.id.type === 'Identifier') {
|
|
159
250
|
if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
|
|
160
|
-
|
|
251
|
+
addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
|
|
161
252
|
}
|
|
162
253
|
}
|
|
163
254
|
});
|
package/build/agent/index.js
CHANGED
|
@@ -21481,6 +21481,50 @@ var init_walk = __esm({
|
|
|
21481
21481
|
});
|
|
21482
21482
|
|
|
21483
21483
|
// src/agent/dsl/validator.js
|
|
21484
|
+
function offsetToLineColumn(code, offset2) {
|
|
21485
|
+
const lines = code.split("\n");
|
|
21486
|
+
let pos = 0;
|
|
21487
|
+
for (let i = 0; i < lines.length; i++) {
|
|
21488
|
+
const lineLength = lines[i].length + 1;
|
|
21489
|
+
if (pos + lineLength > offset2) {
|
|
21490
|
+
return { line: i + 1, column: offset2 - pos + 1 };
|
|
21491
|
+
}
|
|
21492
|
+
pos += lineLength;
|
|
21493
|
+
}
|
|
21494
|
+
return { line: lines.length, column: 1 };
|
|
21495
|
+
}
|
|
21496
|
+
function generateErrorSnippet(code, line, column, contextLines = 2) {
|
|
21497
|
+
const lines = code.split("\n");
|
|
21498
|
+
const startLine = Math.max(0, line - 1 - contextLines);
|
|
21499
|
+
const endLine = Math.min(lines.length, line + contextLines);
|
|
21500
|
+
const snippetLines = [];
|
|
21501
|
+
const lineNumWidth = String(endLine).length;
|
|
21502
|
+
for (let i = startLine; i < endLine; i++) {
|
|
21503
|
+
const lineNum = String(i + 1).padStart(lineNumWidth, " ");
|
|
21504
|
+
const marker = i + 1 === line ? ">" : " ";
|
|
21505
|
+
snippetLines.push(`${marker} ${lineNum} | ${lines[i]}`);
|
|
21506
|
+
if (i + 1 === line) {
|
|
21507
|
+
const padding = " ".repeat(lineNumWidth + 4);
|
|
21508
|
+
const arrow = " ".repeat(Math.max(0, column - 1)) + "^";
|
|
21509
|
+
snippetLines.push(`${padding}${arrow}`);
|
|
21510
|
+
}
|
|
21511
|
+
}
|
|
21512
|
+
return snippetLines.join("\n");
|
|
21513
|
+
}
|
|
21514
|
+
function formatErrorWithSnippet(message, code, offset2 = -1, line = 0, column = 0) {
|
|
21515
|
+
if (offset2 >= 0) {
|
|
21516
|
+
const loc = offsetToLineColumn(code, offset2);
|
|
21517
|
+
line = loc.line;
|
|
21518
|
+
column = loc.column;
|
|
21519
|
+
}
|
|
21520
|
+
if (line <= 0) {
|
|
21521
|
+
return message;
|
|
21522
|
+
}
|
|
21523
|
+
const snippet = generateErrorSnippet(code, line, column);
|
|
21524
|
+
return `${message}
|
|
21525
|
+
|
|
21526
|
+
${snippet}`;
|
|
21527
|
+
}
|
|
21484
21528
|
function validateDSL(code) {
|
|
21485
21529
|
const errors = [];
|
|
21486
21530
|
let ast;
|
|
@@ -21488,40 +21532,54 @@ function validateDSL(code) {
|
|
|
21488
21532
|
ast = parse3(code, {
|
|
21489
21533
|
ecmaVersion: 2022,
|
|
21490
21534
|
sourceType: "script",
|
|
21491
|
-
allowReturnOutsideFunction: true
|
|
21535
|
+
allowReturnOutsideFunction: true,
|
|
21536
|
+
locations: true
|
|
21537
|
+
// Enable location tracking for better error messages
|
|
21492
21538
|
});
|
|
21493
21539
|
} catch (e) {
|
|
21494
|
-
|
|
21540
|
+
const line = e.loc?.line || 0;
|
|
21541
|
+
const column = e.loc?.column ? e.loc.column + 1 : 0;
|
|
21542
|
+
const formattedError = formatErrorWithSnippet(
|
|
21543
|
+
`Syntax error: ${e.message}`,
|
|
21544
|
+
code,
|
|
21545
|
+
-1,
|
|
21546
|
+
line,
|
|
21547
|
+
column
|
|
21548
|
+
);
|
|
21549
|
+
return { valid: false, errors: [formattedError] };
|
|
21495
21550
|
}
|
|
21551
|
+
const addError = (message, position) => {
|
|
21552
|
+
errors.push(formatErrorWithSnippet(message, code, position));
|
|
21553
|
+
};
|
|
21496
21554
|
full(ast, (node) => {
|
|
21497
21555
|
if (!ALLOWED_NODE_TYPES.has(node.type)) {
|
|
21498
|
-
|
|
21556
|
+
addError(`Blocked node type: ${node.type}`, node.start);
|
|
21499
21557
|
return;
|
|
21500
21558
|
}
|
|
21501
21559
|
if ((node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && node.async) {
|
|
21502
|
-
|
|
21560
|
+
addError(`Async functions are not allowed. Write synchronous code \u2014 the runtime handles async.`, node.start);
|
|
21503
21561
|
}
|
|
21504
21562
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
21505
|
-
|
|
21563
|
+
addError(`Generator functions are not allowed`, node.start);
|
|
21506
21564
|
}
|
|
21507
21565
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
21508
|
-
|
|
21566
|
+
addError(`Blocked identifier: '${node.name}'`, node.start);
|
|
21509
21567
|
}
|
|
21510
21568
|
if (node.type === "MemberExpression" && !node.computed) {
|
|
21511
21569
|
if (node.property.type === "Identifier" && BLOCKED_PROPERTIES.has(node.property.name)) {
|
|
21512
|
-
|
|
21570
|
+
addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
|
|
21513
21571
|
}
|
|
21514
21572
|
}
|
|
21515
21573
|
if (node.type === "MemberExpression" && node.computed) {
|
|
21516
21574
|
if (node.property.type === "Literal" && typeof node.property.value === "string") {
|
|
21517
21575
|
if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
|
|
21518
|
-
|
|
21576
|
+
addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
|
|
21519
21577
|
}
|
|
21520
21578
|
}
|
|
21521
21579
|
}
|
|
21522
21580
|
if (node.type === "VariableDeclarator" && node.id.type === "Identifier") {
|
|
21523
21581
|
if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
|
|
21524
|
-
|
|
21582
|
+
addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
|
|
21525
21583
|
}
|
|
21526
21584
|
}
|
|
21527
21585
|
});
|
|
@@ -21845,7 +21903,16 @@ function generateSandboxGlobals(options) {
|
|
|
21845
21903
|
}
|
|
21846
21904
|
if (llmCall) {
|
|
21847
21905
|
const rawLLM = async (instruction, data2, opts = {}) => {
|
|
21848
|
-
|
|
21906
|
+
const result = await llmCall(instruction, data2, opts);
|
|
21907
|
+
if (opts.schema && typeof result === "string") {
|
|
21908
|
+
try {
|
|
21909
|
+
return JSON.parse(result);
|
|
21910
|
+
} catch (e) {
|
|
21911
|
+
logFn?.("[LLM] Warning: schema provided but result is not valid JSON");
|
|
21912
|
+
return result;
|
|
21913
|
+
}
|
|
21914
|
+
}
|
|
21915
|
+
return result;
|
|
21849
21916
|
};
|
|
21850
21917
|
globals.LLM = traceToolCall("LLM", rawLLM, tracer, logFn);
|
|
21851
21918
|
}
|
|
@@ -21902,6 +21969,54 @@ function generateSandboxGlobals(options) {
|
|
|
21902
21969
|
}
|
|
21903
21970
|
return chunks;
|
|
21904
21971
|
};
|
|
21972
|
+
globals.chunkByKey = (data2, keyFn, maxTokens = 2e4) => {
|
|
21973
|
+
const CHARS_PER_TOKEN3 = 4;
|
|
21974
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN3;
|
|
21975
|
+
const text = typeof data2 === "string" ? data2 : JSON.stringify(data2);
|
|
21976
|
+
const blockRegex = /^File: ([^\n]+)/gm;
|
|
21977
|
+
const markers = [];
|
|
21978
|
+
let match2;
|
|
21979
|
+
while ((match2 = blockRegex.exec(text)) !== null) {
|
|
21980
|
+
markers.push({ index: match2.index, file: match2[1].trim() });
|
|
21981
|
+
}
|
|
21982
|
+
if (markers.length === 0) {
|
|
21983
|
+
return globals.chunk(data2, maxTokens);
|
|
21984
|
+
}
|
|
21985
|
+
const chunks = [];
|
|
21986
|
+
let currentChunk = "";
|
|
21987
|
+
let currentSize = 0;
|
|
21988
|
+
let keysInChunk = /* @__PURE__ */ new Set();
|
|
21989
|
+
for (let i = 0; i < markers.length; i++) {
|
|
21990
|
+
const start = markers[i].index;
|
|
21991
|
+
const end = i + 1 < markers.length ? markers[i + 1].index : text.length;
|
|
21992
|
+
const block = text.slice(start, end).trim();
|
|
21993
|
+
const file = markers[i].file;
|
|
21994
|
+
const key = typeof keyFn === "function" ? keyFn(file) : file;
|
|
21995
|
+
const blockSize = block.length + 2;
|
|
21996
|
+
const wouldOverflow = currentSize + blockSize > maxChars;
|
|
21997
|
+
const keyAlreadyInChunk = keysInChunk.has(key);
|
|
21998
|
+
if (!keyAlreadyInChunk && wouldOverflow && currentChunk) {
|
|
21999
|
+
chunks.push(currentChunk.trim());
|
|
22000
|
+
currentChunk = "";
|
|
22001
|
+
currentSize = 0;
|
|
22002
|
+
keysInChunk = /* @__PURE__ */ new Set();
|
|
22003
|
+
}
|
|
22004
|
+
if (currentChunk) currentChunk += "\n\n";
|
|
22005
|
+
currentChunk += block;
|
|
22006
|
+
currentSize += blockSize;
|
|
22007
|
+
keysInChunk.add(key);
|
|
22008
|
+
}
|
|
22009
|
+
if (currentChunk.trim()) {
|
|
22010
|
+
chunks.push(currentChunk.trim());
|
|
22011
|
+
}
|
|
22012
|
+
return chunks.length > 0 ? chunks : [""];
|
|
22013
|
+
};
|
|
22014
|
+
globals.extractPaths = (searchResults) => {
|
|
22015
|
+
const text = typeof searchResults === "string" ? searchResults : JSON.stringify(searchResults);
|
|
22016
|
+
const matches = text.match(/^File: ([^\n]+)/gm) || [];
|
|
22017
|
+
const paths = matches.map((m) => m.replace("File: ", "").trim());
|
|
22018
|
+
return [...new Set(paths)];
|
|
22019
|
+
};
|
|
21905
22020
|
globals.log = (message) => {
|
|
21906
22021
|
if (globals._logs) globals._logs.push(String(message));
|
|
21907
22022
|
};
|
|
@@ -28836,10 +28951,32 @@ var init_esm5 = __esm({
|
|
|
28836
28951
|
|
|
28837
28952
|
// src/tools/executePlan.js
|
|
28838
28953
|
import { tool as tool4 } from "ai";
|
|
28954
|
+
function decodeHtmlEntities(str) {
|
|
28955
|
+
const entities = {
|
|
28956
|
+
"&": "&",
|
|
28957
|
+
"<": "<",
|
|
28958
|
+
">": ">",
|
|
28959
|
+
""": '"',
|
|
28960
|
+
"'": "'",
|
|
28961
|
+
"'": "'",
|
|
28962
|
+
"'": "'"
|
|
28963
|
+
};
|
|
28964
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match2) => {
|
|
28965
|
+
return entities[match2.toLowerCase()] || match2;
|
|
28966
|
+
});
|
|
28967
|
+
result = result.replace(/&#(\d+);/g, (match2, dec) => {
|
|
28968
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
28969
|
+
});
|
|
28970
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match2, hex) => {
|
|
28971
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
28972
|
+
});
|
|
28973
|
+
return result;
|
|
28974
|
+
}
|
|
28839
28975
|
function stripCodeWrapping(code) {
|
|
28840
28976
|
let s = String(code || "");
|
|
28841
28977
|
s = s.replace(/^```(?:javascript|js)?\n?/gm, "").replace(/```$/gm, "");
|
|
28842
28978
|
s = s.replace(/<\/?(?:execute_plan|code)>/g, "");
|
|
28979
|
+
s = decodeHtmlEntities(s);
|
|
28843
28980
|
return s.trim();
|
|
28844
28981
|
}
|
|
28845
28982
|
function buildToolImplementations(configOptions) {
|
|
@@ -29125,6 +29262,26 @@ Last error: ${lastError}`;
|
|
|
29125
29262
|
}
|
|
29126
29263
|
});
|
|
29127
29264
|
}
|
|
29265
|
+
function extractRawOutputBlocks(content, outputBuffer = null) {
|
|
29266
|
+
if (typeof content !== "string") {
|
|
29267
|
+
return { cleanedContent: content, extractedBlocks: [] };
|
|
29268
|
+
}
|
|
29269
|
+
const extractedBlocks = [];
|
|
29270
|
+
const regex = new RegExp(`${RAW_OUTPUT_START}\\n([\\s\\S]*?)\\n${RAW_OUTPUT_END}`, "g");
|
|
29271
|
+
let cleanedContent = content;
|
|
29272
|
+
let match2;
|
|
29273
|
+
while ((match2 = regex.exec(content)) !== null) {
|
|
29274
|
+
extractedBlocks.push(match2[1]);
|
|
29275
|
+
}
|
|
29276
|
+
cleanedContent = content.replace(new RegExp(`${RAW_OUTPUT_START}\\n[\\s\\S]*?\\n${RAW_OUTPUT_END}`, "g"), "").replace(/\n\n\[The above raw output \(\d+ chars\) will be passed directly to the final response\. Do NOT repeat, summarize, or modify it\.\]/g, "").trim();
|
|
29277
|
+
if (outputBuffer && extractedBlocks.length > 0) {
|
|
29278
|
+
for (const block of extractedBlocks) {
|
|
29279
|
+
outputBuffer.items = outputBuffer.items || [];
|
|
29280
|
+
outputBuffer.items.push(block);
|
|
29281
|
+
}
|
|
29282
|
+
}
|
|
29283
|
+
return { cleanedContent, extractedBlocks };
|
|
29284
|
+
}
|
|
29128
29285
|
function formatSuccess(result, description, attempt, outputBuffer) {
|
|
29129
29286
|
let output = "";
|
|
29130
29287
|
if (description) {
|
|
@@ -29161,10 +29318,15 @@ ${JSON.stringify(resultValue, null, 2)}`;
|
|
|
29161
29318
|
}
|
|
29162
29319
|
}
|
|
29163
29320
|
if (outputBuffer && outputBuffer.items && outputBuffer.items.length > 0) {
|
|
29164
|
-
const
|
|
29321
|
+
const rawContent = outputBuffer.items.join("\n");
|
|
29165
29322
|
output += `
|
|
29166
29323
|
|
|
29167
|
-
|
|
29324
|
+
${RAW_OUTPUT_START}
|
|
29325
|
+
${rawContent}
|
|
29326
|
+
${RAW_OUTPUT_END}`;
|
|
29327
|
+
output += `
|
|
29328
|
+
|
|
29329
|
+
[The above raw output (${rawContent.length} chars) will be passed directly to the final response. Do NOT repeat, summarize, or modify it.]`;
|
|
29168
29330
|
}
|
|
29169
29331
|
return output;
|
|
29170
29332
|
}
|
|
@@ -29613,6 +29775,7 @@ Example:
|
|
|
29613
29775
|
<clearOutputBuffer>true</clearOutputBuffer>
|
|
29614
29776
|
</cleanup_execute_plan>`;
|
|
29615
29777
|
}
|
|
29778
|
+
var RAW_OUTPUT_START, RAW_OUTPUT_END;
|
|
29616
29779
|
var init_executePlan = __esm({
|
|
29617
29780
|
"src/tools/executePlan.js"() {
|
|
29618
29781
|
"use strict";
|
|
@@ -29623,6 +29786,8 @@ var init_executePlan = __esm({
|
|
|
29623
29786
|
init_extract();
|
|
29624
29787
|
init_delegate();
|
|
29625
29788
|
init_esm5();
|
|
29789
|
+
RAW_OUTPUT_START = "<<<RAW_OUTPUT>>>";
|
|
29790
|
+
RAW_OUTPUT_END = "<<<END_RAW_OUTPUT>>>";
|
|
29626
29791
|
}
|
|
29627
29792
|
});
|
|
29628
29793
|
|
|
@@ -68204,7 +68369,7 @@ __export(schemaUtils_exports, {
|
|
|
68204
68369
|
createJsonCorrectionPrompt: () => createJsonCorrectionPrompt,
|
|
68205
68370
|
createMermaidCorrectionPrompt: () => createMermaidCorrectionPrompt,
|
|
68206
68371
|
createSchemaDefinitionCorrectionPrompt: () => createSchemaDefinitionCorrectionPrompt,
|
|
68207
|
-
decodeHtmlEntities: () =>
|
|
68372
|
+
decodeHtmlEntities: () => decodeHtmlEntities2,
|
|
68208
68373
|
extractMermaidFromJson: () => extractMermaidFromJson,
|
|
68209
68374
|
extractMermaidFromMarkdown: () => extractMermaidFromMarkdown,
|
|
68210
68375
|
generateExampleFromSchema: () => generateExampleFromSchema,
|
|
@@ -68309,7 +68474,7 @@ function enforceNoAdditionalProperties(schema) {
|
|
|
68309
68474
|
applyRecursively(cloned);
|
|
68310
68475
|
return cloned;
|
|
68311
68476
|
}
|
|
68312
|
-
function
|
|
68477
|
+
function decodeHtmlEntities2(text) {
|
|
68313
68478
|
if (!text || typeof text !== "string") {
|
|
68314
68479
|
return text;
|
|
68315
68480
|
}
|
|
@@ -69752,7 +69917,7 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
69752
69917
|
* @returns {Promise<string>} - The corrected Mermaid diagram
|
|
69753
69918
|
*/
|
|
69754
69919
|
async fixMermaidDiagram(diagramContent, originalErrors = [], diagramInfo = {}) {
|
|
69755
|
-
const decodedContent =
|
|
69920
|
+
const decodedContent = decodeHtmlEntities2(diagramContent);
|
|
69756
69921
|
if (decodedContent !== diagramContent) {
|
|
69757
69922
|
try {
|
|
69758
69923
|
const quickValidation = await validateMermaidDiagram(decodedContent);
|
|
@@ -81068,6 +81233,7 @@ var init_ProbeAgent = __esm({
|
|
|
81068
81233
|
init_path_validation();
|
|
81069
81234
|
init_outputTruncator();
|
|
81070
81235
|
init_delegate();
|
|
81236
|
+
init_executePlan();
|
|
81071
81237
|
init_tasks();
|
|
81072
81238
|
dotenv2.config();
|
|
81073
81239
|
ENGINE_ACTIVITY_TIMEOUT_DEFAULT = 18e4;
|
|
@@ -83799,6 +83965,15 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
83799
83965
|
}
|
|
83800
83966
|
const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
|
|
83801
83967
|
let toolResultContent = typeof executionResult === "string" ? executionResult : JSON.stringify(executionResult, null, 2);
|
|
83968
|
+
if (this._outputBuffer) {
|
|
83969
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent, this._outputBuffer);
|
|
83970
|
+
if (extractedBlocks.length > 0) {
|
|
83971
|
+
toolResultContent = cleanedContent;
|
|
83972
|
+
if (this.debug) {
|
|
83973
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) to output buffer`);
|
|
83974
|
+
}
|
|
83975
|
+
}
|
|
83976
|
+
}
|
|
83802
83977
|
try {
|
|
83803
83978
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
|
83804
83979
|
if (truncateResult.truncated) {
|
|
@@ -84005,6 +84180,15 @@ ${errorXml}
|
|
|
84005
84180
|
const wsPrefix = this.workspaceRoot.endsWith(sep5) ? this.workspaceRoot : this.workspaceRoot + sep5;
|
|
84006
84181
|
toolResultContent = toolResultContent.split(wsPrefix).join("");
|
|
84007
84182
|
}
|
|
84183
|
+
if (this._outputBuffer) {
|
|
84184
|
+
const { cleanedContent, extractedBlocks } = extractRawOutputBlocks(toolResultContent, this._outputBuffer);
|
|
84185
|
+
if (extractedBlocks.length > 0) {
|
|
84186
|
+
toolResultContent = cleanedContent;
|
|
84187
|
+
if (this.debug) {
|
|
84188
|
+
console.log(`[DEBUG] Extracted ${extractedBlocks.length} raw output blocks (${extractedBlocks.reduce((sum, b) => sum + b.length, 0)} chars) to output buffer`);
|
|
84189
|
+
}
|
|
84190
|
+
}
|
|
84191
|
+
}
|
|
84008
84192
|
try {
|
|
84009
84193
|
const truncateResult = await truncateIfNeeded(toolResultContent, this.tokenCounter, this.sessionId, this.maxOutputTokens);
|
|
84010
84194
|
if (truncateResult.truncated) {
|