@probelabs/probe 0.6.0-rc240 → 0.6.0-rc241
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-rc240-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc241-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/probe-v0.6.0-rc241-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc241-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc241-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc241-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/dsl/validator.js +99 -8
- package/build/agent/index.js +92 -12
- package/build/tools/executePlan.js +36 -0
- package/cjs/agent/ProbeAgent.cjs +92 -12
- package/cjs/index.cjs +89 -9
- package/package.json +1 -1
- package/src/agent/dsl/validator.js +99 -8
- package/src/tools/executePlan.js +36 -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
|
|
@@ -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
|
});
|
|
@@ -28836,10 +28894,32 @@ var init_esm5 = __esm({
|
|
|
28836
28894
|
|
|
28837
28895
|
// src/tools/executePlan.js
|
|
28838
28896
|
import { tool as tool4 } from "ai";
|
|
28897
|
+
function decodeHtmlEntities(str) {
|
|
28898
|
+
const entities = {
|
|
28899
|
+
"&": "&",
|
|
28900
|
+
"<": "<",
|
|
28901
|
+
">": ">",
|
|
28902
|
+
""": '"',
|
|
28903
|
+
"'": "'",
|
|
28904
|
+
"'": "'",
|
|
28905
|
+
"'": "'"
|
|
28906
|
+
};
|
|
28907
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match2) => {
|
|
28908
|
+
return entities[match2.toLowerCase()] || match2;
|
|
28909
|
+
});
|
|
28910
|
+
result = result.replace(/&#(\d+);/g, (match2, dec) => {
|
|
28911
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
28912
|
+
});
|
|
28913
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match2, hex) => {
|
|
28914
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
28915
|
+
});
|
|
28916
|
+
return result;
|
|
28917
|
+
}
|
|
28839
28918
|
function stripCodeWrapping(code) {
|
|
28840
28919
|
let s = String(code || "");
|
|
28841
28920
|
s = s.replace(/^```(?:javascript|js)?\n?/gm, "").replace(/```$/gm, "");
|
|
28842
28921
|
s = s.replace(/<\/?(?:execute_plan|code)>/g, "");
|
|
28922
|
+
s = decodeHtmlEntities(s);
|
|
28843
28923
|
return s.trim();
|
|
28844
28924
|
}
|
|
28845
28925
|
function buildToolImplementations(configOptions) {
|
|
@@ -68204,7 +68284,7 @@ __export(schemaUtils_exports, {
|
|
|
68204
68284
|
createJsonCorrectionPrompt: () => createJsonCorrectionPrompt,
|
|
68205
68285
|
createMermaidCorrectionPrompt: () => createMermaidCorrectionPrompt,
|
|
68206
68286
|
createSchemaDefinitionCorrectionPrompt: () => createSchemaDefinitionCorrectionPrompt,
|
|
68207
|
-
decodeHtmlEntities: () =>
|
|
68287
|
+
decodeHtmlEntities: () => decodeHtmlEntities2,
|
|
68208
68288
|
extractMermaidFromJson: () => extractMermaidFromJson,
|
|
68209
68289
|
extractMermaidFromMarkdown: () => extractMermaidFromMarkdown,
|
|
68210
68290
|
generateExampleFromSchema: () => generateExampleFromSchema,
|
|
@@ -68309,7 +68389,7 @@ function enforceNoAdditionalProperties(schema) {
|
|
|
68309
68389
|
applyRecursively(cloned);
|
|
68310
68390
|
return cloned;
|
|
68311
68391
|
}
|
|
68312
|
-
function
|
|
68392
|
+
function decodeHtmlEntities2(text) {
|
|
68313
68393
|
if (!text || typeof text !== "string") {
|
|
68314
68394
|
return text;
|
|
68315
68395
|
}
|
|
@@ -69752,7 +69832,7 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
69752
69832
|
* @returns {Promise<string>} - The corrected Mermaid diagram
|
|
69753
69833
|
*/
|
|
69754
69834
|
async fixMermaidDiagram(diagramContent, originalErrors = [], diagramInfo = {}) {
|
|
69755
|
-
const decodedContent =
|
|
69835
|
+
const decodedContent = decodeHtmlEntities2(diagramContent);
|
|
69756
69836
|
if (decodedContent !== diagramContent) {
|
|
69757
69837
|
try {
|
|
69758
69838
|
const quickValidation = await validateMermaidDiagram(decodedContent);
|
|
@@ -16,8 +16,42 @@ import { glob } from 'glob';
|
|
|
16
16
|
|
|
17
17
|
export { executePlanSchema };
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Decode common HTML entities that LLMs sometimes produce when generating code.
|
|
21
|
+
* This handles entities like && → &&, <= → <=, etc.
|
|
22
|
+
*/
|
|
23
|
+
function decodeHtmlEntities(str) {
|
|
24
|
+
const entities = {
|
|
25
|
+
'&': '&',
|
|
26
|
+
'<': '<',
|
|
27
|
+
'>': '>',
|
|
28
|
+
'"': '"',
|
|
29
|
+
''': "'",
|
|
30
|
+
''': "'",
|
|
31
|
+
''': "'",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Replace named/common entities
|
|
35
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match) => {
|
|
36
|
+
return entities[match.toLowerCase()] || match;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Handle numeric entities (decimal): < → <
|
|
40
|
+
result = result.replace(/&#(\d+);/g, (match, dec) => {
|
|
41
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Handle numeric entities (hex): < → <
|
|
45
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match, hex) => {
|
|
46
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
19
52
|
/**
|
|
20
53
|
* Strip markdown fences and XML tags that LLMs sometimes wrap code in.
|
|
54
|
+
* Also decodes HTML entities that may appear in XML-extracted code.
|
|
21
55
|
*/
|
|
22
56
|
function stripCodeWrapping(code) {
|
|
23
57
|
let s = String(code || '');
|
|
@@ -25,6 +59,8 @@ function stripCodeWrapping(code) {
|
|
|
25
59
|
s = s.replace(/^```(?:javascript|js)?\n?/gm, '').replace(/```$/gm, '');
|
|
26
60
|
// Strip XML-style tags: <execute_plan>, </execute_plan>, <code>, </code>
|
|
27
61
|
s = s.replace(/<\/?(?:execute_plan|code)>/g, '');
|
|
62
|
+
// Decode HTML entities (e.g., && → &&, <= → <=)
|
|
63
|
+
s = decodeHtmlEntities(s);
|
|
28
64
|
return s.trim();
|
|
29
65
|
}
|
|
30
66
|
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -50930,6 +50930,50 @@ var init_walk = __esm({
|
|
|
50930
50930
|
});
|
|
50931
50931
|
|
|
50932
50932
|
// src/agent/dsl/validator.js
|
|
50933
|
+
function offsetToLineColumn(code, offset2) {
|
|
50934
|
+
const lines = code.split("\n");
|
|
50935
|
+
let pos = 0;
|
|
50936
|
+
for (let i5 = 0; i5 < lines.length; i5++) {
|
|
50937
|
+
const lineLength = lines[i5].length + 1;
|
|
50938
|
+
if (pos + lineLength > offset2) {
|
|
50939
|
+
return { line: i5 + 1, column: offset2 - pos + 1 };
|
|
50940
|
+
}
|
|
50941
|
+
pos += lineLength;
|
|
50942
|
+
}
|
|
50943
|
+
return { line: lines.length, column: 1 };
|
|
50944
|
+
}
|
|
50945
|
+
function generateErrorSnippet(code, line, column, contextLines = 2) {
|
|
50946
|
+
const lines = code.split("\n");
|
|
50947
|
+
const startLine = Math.max(0, line - 1 - contextLines);
|
|
50948
|
+
const endLine = Math.min(lines.length, line + contextLines);
|
|
50949
|
+
const snippetLines = [];
|
|
50950
|
+
const lineNumWidth = String(endLine).length;
|
|
50951
|
+
for (let i5 = startLine; i5 < endLine; i5++) {
|
|
50952
|
+
const lineNum = String(i5 + 1).padStart(lineNumWidth, " ");
|
|
50953
|
+
const marker15 = i5 + 1 === line ? ">" : " ";
|
|
50954
|
+
snippetLines.push(`${marker15} ${lineNum} | ${lines[i5]}`);
|
|
50955
|
+
if (i5 + 1 === line) {
|
|
50956
|
+
const padding = " ".repeat(lineNumWidth + 4);
|
|
50957
|
+
const arrow = " ".repeat(Math.max(0, column - 1)) + "^";
|
|
50958
|
+
snippetLines.push(`${padding}${arrow}`);
|
|
50959
|
+
}
|
|
50960
|
+
}
|
|
50961
|
+
return snippetLines.join("\n");
|
|
50962
|
+
}
|
|
50963
|
+
function formatErrorWithSnippet(message, code, offset2 = -1, line = 0, column = 0) {
|
|
50964
|
+
if (offset2 >= 0) {
|
|
50965
|
+
const loc = offsetToLineColumn(code, offset2);
|
|
50966
|
+
line = loc.line;
|
|
50967
|
+
column = loc.column;
|
|
50968
|
+
}
|
|
50969
|
+
if (line <= 0) {
|
|
50970
|
+
return message;
|
|
50971
|
+
}
|
|
50972
|
+
const snippet = generateErrorSnippet(code, line, column);
|
|
50973
|
+
return `${message}
|
|
50974
|
+
|
|
50975
|
+
${snippet}`;
|
|
50976
|
+
}
|
|
50933
50977
|
function validateDSL(code) {
|
|
50934
50978
|
const errors = [];
|
|
50935
50979
|
let ast;
|
|
@@ -50937,40 +50981,54 @@ function validateDSL(code) {
|
|
|
50937
50981
|
ast = parse3(code, {
|
|
50938
50982
|
ecmaVersion: 2022,
|
|
50939
50983
|
sourceType: "script",
|
|
50940
|
-
allowReturnOutsideFunction: true
|
|
50984
|
+
allowReturnOutsideFunction: true,
|
|
50985
|
+
locations: true
|
|
50986
|
+
// Enable location tracking for better error messages
|
|
50941
50987
|
});
|
|
50942
50988
|
} catch (e5) {
|
|
50943
|
-
|
|
50989
|
+
const line = e5.loc?.line || 0;
|
|
50990
|
+
const column = e5.loc?.column ? e5.loc.column + 1 : 0;
|
|
50991
|
+
const formattedError = formatErrorWithSnippet(
|
|
50992
|
+
`Syntax error: ${e5.message}`,
|
|
50993
|
+
code,
|
|
50994
|
+
-1,
|
|
50995
|
+
line,
|
|
50996
|
+
column
|
|
50997
|
+
);
|
|
50998
|
+
return { valid: false, errors: [formattedError] };
|
|
50944
50999
|
}
|
|
51000
|
+
const addError = (message, position) => {
|
|
51001
|
+
errors.push(formatErrorWithSnippet(message, code, position));
|
|
51002
|
+
};
|
|
50945
51003
|
full(ast, (node) => {
|
|
50946
51004
|
if (!ALLOWED_NODE_TYPES.has(node.type)) {
|
|
50947
|
-
|
|
51005
|
+
addError(`Blocked node type: ${node.type}`, node.start);
|
|
50948
51006
|
return;
|
|
50949
51007
|
}
|
|
50950
51008
|
if ((node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && node.async) {
|
|
50951
|
-
|
|
51009
|
+
addError(`Async functions are not allowed. Write synchronous code \u2014 the runtime handles async.`, node.start);
|
|
50952
51010
|
}
|
|
50953
51011
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
50954
|
-
|
|
51012
|
+
addError(`Generator functions are not allowed`, node.start);
|
|
50955
51013
|
}
|
|
50956
51014
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
50957
|
-
|
|
51015
|
+
addError(`Blocked identifier: '${node.name}'`, node.start);
|
|
50958
51016
|
}
|
|
50959
51017
|
if (node.type === "MemberExpression" && !node.computed) {
|
|
50960
51018
|
if (node.property.type === "Identifier" && BLOCKED_PROPERTIES.has(node.property.name)) {
|
|
50961
|
-
|
|
51019
|
+
addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
|
|
50962
51020
|
}
|
|
50963
51021
|
}
|
|
50964
51022
|
if (node.type === "MemberExpression" && node.computed) {
|
|
50965
51023
|
if (node.property.type === "Literal" && typeof node.property.value === "string") {
|
|
50966
51024
|
if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
|
|
50967
|
-
|
|
51025
|
+
addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
|
|
50968
51026
|
}
|
|
50969
51027
|
}
|
|
50970
51028
|
}
|
|
50971
51029
|
if (node.type === "VariableDeclarator" && node.id.type === "Identifier") {
|
|
50972
51030
|
if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
|
|
50973
|
-
|
|
51031
|
+
addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
|
|
50974
51032
|
}
|
|
50975
51033
|
}
|
|
50976
51034
|
});
|
|
@@ -58284,10 +58342,32 @@ var init_esm5 = __esm({
|
|
|
58284
58342
|
});
|
|
58285
58343
|
|
|
58286
58344
|
// src/tools/executePlan.js
|
|
58345
|
+
function decodeHtmlEntities(str) {
|
|
58346
|
+
const entities = {
|
|
58347
|
+
"&": "&",
|
|
58348
|
+
"<": "<",
|
|
58349
|
+
">": ">",
|
|
58350
|
+
""": '"',
|
|
58351
|
+
"'": "'",
|
|
58352
|
+
"'": "'",
|
|
58353
|
+
"'": "'"
|
|
58354
|
+
};
|
|
58355
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match2) => {
|
|
58356
|
+
return entities[match2.toLowerCase()] || match2;
|
|
58357
|
+
});
|
|
58358
|
+
result = result.replace(/&#(\d+);/g, (match2, dec) => {
|
|
58359
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
58360
|
+
});
|
|
58361
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match2, hex) => {
|
|
58362
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
58363
|
+
});
|
|
58364
|
+
return result;
|
|
58365
|
+
}
|
|
58287
58366
|
function stripCodeWrapping(code) {
|
|
58288
58367
|
let s5 = String(code || "");
|
|
58289
58368
|
s5 = s5.replace(/^```(?:javascript|js)?\n?/gm, "").replace(/```$/gm, "");
|
|
58290
58369
|
s5 = s5.replace(/<\/?(?:execute_plan|code)>/g, "");
|
|
58370
|
+
s5 = decodeHtmlEntities(s5);
|
|
58291
58371
|
return s5.trim();
|
|
58292
58372
|
}
|
|
58293
58373
|
function buildToolImplementations(configOptions) {
|
|
@@ -97220,7 +97300,7 @@ __export(schemaUtils_exports, {
|
|
|
97220
97300
|
createJsonCorrectionPrompt: () => createJsonCorrectionPrompt,
|
|
97221
97301
|
createMermaidCorrectionPrompt: () => createMermaidCorrectionPrompt,
|
|
97222
97302
|
createSchemaDefinitionCorrectionPrompt: () => createSchemaDefinitionCorrectionPrompt,
|
|
97223
|
-
decodeHtmlEntities: () =>
|
|
97303
|
+
decodeHtmlEntities: () => decodeHtmlEntities2,
|
|
97224
97304
|
extractMermaidFromJson: () => extractMermaidFromJson,
|
|
97225
97305
|
extractMermaidFromMarkdown: () => extractMermaidFromMarkdown,
|
|
97226
97306
|
generateExampleFromSchema: () => generateExampleFromSchema,
|
|
@@ -97325,7 +97405,7 @@ function enforceNoAdditionalProperties(schema) {
|
|
|
97325
97405
|
applyRecursively(cloned);
|
|
97326
97406
|
return cloned;
|
|
97327
97407
|
}
|
|
97328
|
-
function
|
|
97408
|
+
function decodeHtmlEntities2(text) {
|
|
97329
97409
|
if (!text || typeof text !== "string") {
|
|
97330
97410
|
return text;
|
|
97331
97411
|
}
|
|
@@ -98768,7 +98848,7 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
|
|
|
98768
98848
|
* @returns {Promise<string>} - The corrected Mermaid diagram
|
|
98769
98849
|
*/
|
|
98770
98850
|
async fixMermaidDiagram(diagramContent, originalErrors = [], diagramInfo = {}) {
|
|
98771
|
-
const decodedContent =
|
|
98851
|
+
const decodedContent = decodeHtmlEntities2(diagramContent);
|
|
98772
98852
|
if (decodedContent !== diagramContent) {
|
|
98773
98853
|
try {
|
|
98774
98854
|
const quickValidation = await validateMermaidDiagram(decodedContent);
|
package/cjs/index.cjs
CHANGED
|
@@ -112384,6 +112384,50 @@ var init_walk = __esm({
|
|
|
112384
112384
|
});
|
|
112385
112385
|
|
|
112386
112386
|
// src/agent/dsl/validator.js
|
|
112387
|
+
function offsetToLineColumn(code, offset2) {
|
|
112388
|
+
const lines = code.split("\n");
|
|
112389
|
+
let pos = 0;
|
|
112390
|
+
for (let i5 = 0; i5 < lines.length; i5++) {
|
|
112391
|
+
const lineLength = lines[i5].length + 1;
|
|
112392
|
+
if (pos + lineLength > offset2) {
|
|
112393
|
+
return { line: i5 + 1, column: offset2 - pos + 1 };
|
|
112394
|
+
}
|
|
112395
|
+
pos += lineLength;
|
|
112396
|
+
}
|
|
112397
|
+
return { line: lines.length, column: 1 };
|
|
112398
|
+
}
|
|
112399
|
+
function generateErrorSnippet(code, line, column, contextLines = 2) {
|
|
112400
|
+
const lines = code.split("\n");
|
|
112401
|
+
const startLine = Math.max(0, line - 1 - contextLines);
|
|
112402
|
+
const endLine = Math.min(lines.length, line + contextLines);
|
|
112403
|
+
const snippetLines = [];
|
|
112404
|
+
const lineNumWidth = String(endLine).length;
|
|
112405
|
+
for (let i5 = startLine; i5 < endLine; i5++) {
|
|
112406
|
+
const lineNum = String(i5 + 1).padStart(lineNumWidth, " ");
|
|
112407
|
+
const marker15 = i5 + 1 === line ? ">" : " ";
|
|
112408
|
+
snippetLines.push(`${marker15} ${lineNum} | ${lines[i5]}`);
|
|
112409
|
+
if (i5 + 1 === line) {
|
|
112410
|
+
const padding = " ".repeat(lineNumWidth + 4);
|
|
112411
|
+
const arrow = " ".repeat(Math.max(0, column - 1)) + "^";
|
|
112412
|
+
snippetLines.push(`${padding}${arrow}`);
|
|
112413
|
+
}
|
|
112414
|
+
}
|
|
112415
|
+
return snippetLines.join("\n");
|
|
112416
|
+
}
|
|
112417
|
+
function formatErrorWithSnippet(message, code, offset2 = -1, line = 0, column = 0) {
|
|
112418
|
+
if (offset2 >= 0) {
|
|
112419
|
+
const loc = offsetToLineColumn(code, offset2);
|
|
112420
|
+
line = loc.line;
|
|
112421
|
+
column = loc.column;
|
|
112422
|
+
}
|
|
112423
|
+
if (line <= 0) {
|
|
112424
|
+
return message;
|
|
112425
|
+
}
|
|
112426
|
+
const snippet = generateErrorSnippet(code, line, column);
|
|
112427
|
+
return `${message}
|
|
112428
|
+
|
|
112429
|
+
${snippet}`;
|
|
112430
|
+
}
|
|
112387
112431
|
function validateDSL(code) {
|
|
112388
112432
|
const errors = [];
|
|
112389
112433
|
let ast;
|
|
@@ -112391,40 +112435,54 @@ function validateDSL(code) {
|
|
|
112391
112435
|
ast = parse8(code, {
|
|
112392
112436
|
ecmaVersion: 2022,
|
|
112393
112437
|
sourceType: "script",
|
|
112394
|
-
allowReturnOutsideFunction: true
|
|
112438
|
+
allowReturnOutsideFunction: true,
|
|
112439
|
+
locations: true
|
|
112440
|
+
// Enable location tracking for better error messages
|
|
112395
112441
|
});
|
|
112396
112442
|
} catch (e5) {
|
|
112397
|
-
|
|
112443
|
+
const line = e5.loc?.line || 0;
|
|
112444
|
+
const column = e5.loc?.column ? e5.loc.column + 1 : 0;
|
|
112445
|
+
const formattedError = formatErrorWithSnippet(
|
|
112446
|
+
`Syntax error: ${e5.message}`,
|
|
112447
|
+
code,
|
|
112448
|
+
-1,
|
|
112449
|
+
line,
|
|
112450
|
+
column
|
|
112451
|
+
);
|
|
112452
|
+
return { valid: false, errors: [formattedError] };
|
|
112398
112453
|
}
|
|
112454
|
+
const addError = (message, position) => {
|
|
112455
|
+
errors.push(formatErrorWithSnippet(message, code, position));
|
|
112456
|
+
};
|
|
112399
112457
|
full(ast, (node) => {
|
|
112400
112458
|
if (!ALLOWED_NODE_TYPES.has(node.type)) {
|
|
112401
|
-
|
|
112459
|
+
addError(`Blocked node type: ${node.type}`, node.start);
|
|
112402
112460
|
return;
|
|
112403
112461
|
}
|
|
112404
112462
|
if ((node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") && node.async) {
|
|
112405
|
-
|
|
112463
|
+
addError(`Async functions are not allowed. Write synchronous code \u2014 the runtime handles async.`, node.start);
|
|
112406
112464
|
}
|
|
112407
112465
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
112408
|
-
|
|
112466
|
+
addError(`Generator functions are not allowed`, node.start);
|
|
112409
112467
|
}
|
|
112410
112468
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
112411
|
-
|
|
112469
|
+
addError(`Blocked identifier: '${node.name}'`, node.start);
|
|
112412
112470
|
}
|
|
112413
112471
|
if (node.type === "MemberExpression" && !node.computed) {
|
|
112414
112472
|
if (node.property.type === "Identifier" && BLOCKED_PROPERTIES.has(node.property.name)) {
|
|
112415
|
-
|
|
112473
|
+
addError(`Blocked property access: '.${node.property.name}'`, node.property.start);
|
|
112416
112474
|
}
|
|
112417
112475
|
}
|
|
112418
112476
|
if (node.type === "MemberExpression" && node.computed) {
|
|
112419
112477
|
if (node.property.type === "Literal" && typeof node.property.value === "string") {
|
|
112420
112478
|
if (BLOCKED_PROPERTIES.has(node.property.value) || BLOCKED_IDENTIFIERS.has(node.property.value)) {
|
|
112421
|
-
|
|
112479
|
+
addError(`Blocked computed property access: '["${node.property.value}"]'`, node.property.start);
|
|
112422
112480
|
}
|
|
112423
112481
|
}
|
|
112424
112482
|
}
|
|
112425
112483
|
if (node.type === "VariableDeclarator" && node.id.type === "Identifier") {
|
|
112426
112484
|
if (BLOCKED_IDENTIFIERS.has(node.id.name)) {
|
|
112427
|
-
|
|
112485
|
+
addError(`Cannot declare variable with blocked name: '${node.id.name}'`, node.id.start);
|
|
112428
112486
|
}
|
|
112429
112487
|
}
|
|
112430
112488
|
});
|
|
@@ -113055,10 +113113,32 @@ var init_runtime = __esm({
|
|
|
113055
113113
|
});
|
|
113056
113114
|
|
|
113057
113115
|
// src/tools/executePlan.js
|
|
113116
|
+
function decodeHtmlEntities2(str) {
|
|
113117
|
+
const entities = {
|
|
113118
|
+
"&": "&",
|
|
113119
|
+
"<": "<",
|
|
113120
|
+
">": ">",
|
|
113121
|
+
""": '"',
|
|
113122
|
+
"'": "'",
|
|
113123
|
+
"'": "'",
|
|
113124
|
+
"'": "'"
|
|
113125
|
+
};
|
|
113126
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match2) => {
|
|
113127
|
+
return entities[match2.toLowerCase()] || match2;
|
|
113128
|
+
});
|
|
113129
|
+
result = result.replace(/&#(\d+);/g, (match2, dec) => {
|
|
113130
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
113131
|
+
});
|
|
113132
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match2, hex) => {
|
|
113133
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
113134
|
+
});
|
|
113135
|
+
return result;
|
|
113136
|
+
}
|
|
113058
113137
|
function stripCodeWrapping(code) {
|
|
113059
113138
|
let s5 = String(code || "");
|
|
113060
113139
|
s5 = s5.replace(/^```(?:javascript|js)?\n?/gm, "").replace(/```$/gm, "");
|
|
113061
113140
|
s5 = s5.replace(/<\/?(?:execute_plan|code)>/g, "");
|
|
113141
|
+
s5 = decodeHtmlEntities2(s5);
|
|
113062
113142
|
return s5.trim();
|
|
113063
113143
|
}
|
|
113064
113144
|
function buildToolImplementations(configOptions) {
|
package/package.json
CHANGED
|
@@ -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/src/tools/executePlan.js
CHANGED
|
@@ -16,8 +16,42 @@ import { glob } from 'glob';
|
|
|
16
16
|
|
|
17
17
|
export { executePlanSchema };
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Decode common HTML entities that LLMs sometimes produce when generating code.
|
|
21
|
+
* This handles entities like && → &&, <= → <=, etc.
|
|
22
|
+
*/
|
|
23
|
+
function decodeHtmlEntities(str) {
|
|
24
|
+
const entities = {
|
|
25
|
+
'&': '&',
|
|
26
|
+
'<': '<',
|
|
27
|
+
'>': '>',
|
|
28
|
+
'"': '"',
|
|
29
|
+
''': "'",
|
|
30
|
+
''': "'",
|
|
31
|
+
''': "'",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Replace named/common entities
|
|
35
|
+
let result = str.replace(/&(?:amp|lt|gt|quot|apos|#39|#x27);/gi, (match) => {
|
|
36
|
+
return entities[match.toLowerCase()] || match;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Handle numeric entities (decimal): < → <
|
|
40
|
+
result = result.replace(/&#(\d+);/g, (match, dec) => {
|
|
41
|
+
return String.fromCharCode(parseInt(dec, 10));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Handle numeric entities (hex): < → <
|
|
45
|
+
result = result.replace(/&#x([0-9a-f]+);/gi, (match, hex) => {
|
|
46
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
19
52
|
/**
|
|
20
53
|
* Strip markdown fences and XML tags that LLMs sometimes wrap code in.
|
|
54
|
+
* Also decodes HTML entities that may appear in XML-extracted code.
|
|
21
55
|
*/
|
|
22
56
|
function stripCodeWrapping(code) {
|
|
23
57
|
let s = String(code || '');
|
|
@@ -25,6 +59,8 @@ function stripCodeWrapping(code) {
|
|
|
25
59
|
s = s.replace(/^```(?:javascript|js)?\n?/gm, '').replace(/```$/gm, '');
|
|
26
60
|
// Strip XML-style tags: <execute_plan>, </execute_plan>, <code>, </code>
|
|
27
61
|
s = s.replace(/<\/?(?:execute_plan|code)>/g, '');
|
|
62
|
+
// Decode HTML entities (e.g., && → &&, <= → <=)
|
|
63
|
+
s = decodeHtmlEntities(s);
|
|
28
64
|
return s.trim();
|
|
29
65
|
}
|
|
30
66
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|