@probelabs/probe 0.6.0-rc237 → 0.6.0-rc239
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-rc239-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc239-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc239-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc239-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc239-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/dsl/environment.js +26 -1
- package/build/agent/dsl/runtime.js +3 -16
- package/build/agent/dsl/validator.js +5 -4
- package/build/agent/index.js +24 -15
- package/cjs/agent/ProbeAgent.cjs +24 -15
- package/cjs/index.cjs +24 -15
- package/package.json +1 -1
- package/src/agent/dsl/environment.js +26 -1
- package/src/agent/dsl/runtime.js +3 -16
- package/src/agent/dsl/validator.js +5 -4
- package/bin/binaries/probe-v0.6.0-rc237-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc237-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc237-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc237-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc237-x86_64-unknown-linux-musl.tar.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -117,6 +117,23 @@ function traceToolCall(toolName, fn, tracer, logFn) {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Try to parse a string as JSON if it looks like a JSON object or array.
|
|
122
|
+
* Checks if the first non-whitespace character is '{' or '[' before attempting parse.
|
|
123
|
+
* Returns the original string if it's not JSON.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} text - The text to try parsing
|
|
126
|
+
* @returns {any} Parsed JSON value, or the original string
|
|
127
|
+
*/
|
|
128
|
+
function tryParseJSONValue(text) {
|
|
129
|
+
if (typeof text !== 'string') return text;
|
|
130
|
+
const firstChar = text.trimStart()[0];
|
|
131
|
+
if (firstChar === '{' || firstChar === '[') {
|
|
132
|
+
try { return JSON.parse(text); } catch (_) { /* not valid JSON */ }
|
|
133
|
+
}
|
|
134
|
+
return text;
|
|
135
|
+
}
|
|
136
|
+
|
|
120
137
|
/**
|
|
121
138
|
* Generate sandbox globals that bridge DSL function calls to real tool implementations.
|
|
122
139
|
*
|
|
@@ -195,7 +212,15 @@ export function generateSandboxGlobals(options) {
|
|
|
195
212
|
if (mcpBridge) {
|
|
196
213
|
for (const [name, tool] of Object.entries(mcpTools)) {
|
|
197
214
|
const rawMcpFn = async (params = {}) => {
|
|
198
|
-
|
|
215
|
+
const result = await tool.execute(params);
|
|
216
|
+
// Extract text from MCP response envelope: { content: [{ type: 'text', text: '...' }] }
|
|
217
|
+
const text = result?.content?.[0]?.text;
|
|
218
|
+
if (text === undefined) {
|
|
219
|
+
// No envelope — if raw result is a JSON-like string, try parsing it
|
|
220
|
+
if (typeof result === 'string') return tryParseJSONValue(result);
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
return tryParseJSONValue(text);
|
|
199
224
|
};
|
|
200
225
|
globals[name] = traceToolCall(name, rawMcpFn, tracer, logFn);
|
|
201
226
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Orchestrates the full pipeline:
|
|
5
5
|
* 1. Validate (AST whitelist)
|
|
6
6
|
* 2. Transform (inject await, wrap in async IIFE)
|
|
7
|
-
* 3. Execute in SandboxJS with tool globals
|
|
7
|
+
* 3. Execute in SandboxJS with tool globals
|
|
8
8
|
*
|
|
9
9
|
* Returns the result or a structured error.
|
|
10
10
|
*/
|
|
@@ -25,7 +25,6 @@ const Sandbox = SandboxModule.default || SandboxModule;
|
|
|
25
25
|
* @param {Object} [options.mcpTools={}] - MCP tool metadata
|
|
26
26
|
* @param {Function} options.llmCall - Function for LLM() calls: (instruction, data, options?) => Promise<any>
|
|
27
27
|
* @param {number} [options.mapConcurrency=3] - Concurrency limit for map()
|
|
28
|
-
* @param {number} [options.timeoutMs=120000] - Execution timeout in milliseconds (default 2 min)
|
|
29
28
|
* @param {number} [options.maxLoopIterations=5000] - Max iterations for while/for loops
|
|
30
29
|
* @param {Object} [options.tracer=null] - SimpleAppTracer instance for OTEL telemetry
|
|
31
30
|
* @returns {Object} Runtime with execute() method
|
|
@@ -37,7 +36,6 @@ export function createDSLRuntime(options) {
|
|
|
37
36
|
mcpTools = {},
|
|
38
37
|
llmCall,
|
|
39
38
|
mapConcurrency = 3,
|
|
40
|
-
timeoutMs = 120000,
|
|
41
39
|
maxLoopIterations = 5000,
|
|
42
40
|
tracer = null,
|
|
43
41
|
sessionStore = {},
|
|
@@ -108,9 +106,8 @@ export function createDSLRuntime(options) {
|
|
|
108
106
|
};
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
// Step 3: Execute in SandboxJS
|
|
109
|
+
// Step 3: Execute in SandboxJS
|
|
112
110
|
tracer?.addEvent?.('dsl.phase.execute_start', {
|
|
113
|
-
'dsl.timeout_ms': timeoutMs,
|
|
114
111
|
'dsl.max_loop_iterations': maxLoopIterations,
|
|
115
112
|
});
|
|
116
113
|
|
|
@@ -147,20 +144,10 @@ export function createDSLRuntime(options) {
|
|
|
147
144
|
};
|
|
148
145
|
process.on('unhandledRejection', rejectionHandler);
|
|
149
146
|
|
|
150
|
-
// Race execution against timeout
|
|
151
|
-
let timeoutHandle;
|
|
152
|
-
const executionPromise = exec().run();
|
|
153
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
154
|
-
timeoutHandle = setTimeout(() => {
|
|
155
|
-
reject(new Error(`Execution timed out after ${Math.round(timeoutMs / 1000)}s. Script took too long — reduce the amount of work (fewer items, smaller data) or increase timeout.`));
|
|
156
|
-
}, timeoutMs);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
147
|
let result;
|
|
160
148
|
try {
|
|
161
|
-
result = await
|
|
149
|
+
result = await exec().run();
|
|
162
150
|
} finally {
|
|
163
|
-
clearTimeout(timeoutHandle);
|
|
164
151
|
// Delay handler removal — SandboxJS can throw async errors after execution completes
|
|
165
152
|
setTimeout(() => {
|
|
166
153
|
process.removeListener('unhandledRejection', rejectionHandler);
|
|
@@ -16,18 +16,23 @@ const ALLOWED_NODE_TYPES = new Set([
|
|
|
16
16
|
'BlockStatement',
|
|
17
17
|
'VariableDeclaration',
|
|
18
18
|
'VariableDeclarator',
|
|
19
|
+
'FunctionDeclaration',
|
|
19
20
|
'ArrowFunctionExpression',
|
|
20
21
|
'FunctionExpression',
|
|
21
22
|
'CallExpression',
|
|
23
|
+
'NewExpression',
|
|
22
24
|
'MemberExpression',
|
|
23
25
|
'Identifier',
|
|
24
26
|
'Literal',
|
|
25
27
|
'TemplateLiteral',
|
|
26
28
|
'TemplateElement',
|
|
29
|
+
'TaggedTemplateExpression',
|
|
27
30
|
'ArrayExpression',
|
|
28
31
|
'ObjectExpression',
|
|
29
32
|
'SpreadElement',
|
|
30
33
|
'IfStatement',
|
|
34
|
+
'SwitchStatement',
|
|
35
|
+
'SwitchCase',
|
|
31
36
|
'ConditionalExpression',
|
|
32
37
|
'ForOfStatement',
|
|
33
38
|
'ForInStatement',
|
|
@@ -127,10 +132,6 @@ export function validateDSL(code) {
|
|
|
127
132
|
errors.push(`Generator functions are not allowed at position ${node.start}`);
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
// Block regex literals — SandboxJS doesn't support them
|
|
131
|
-
if (node.type === 'Literal' && node.regex) {
|
|
132
|
-
errors.push(`Regex literals are not supported at position ${node.start}. Use String methods like indexOf(), includes(), startsWith() instead.`);
|
|
133
|
-
}
|
|
134
135
|
|
|
135
136
|
// Check identifiers against blocklist
|
|
136
137
|
if (node.type === 'Identifier' && BLOCKED_IDENTIFIERS.has(node.name)) {
|
package/build/agent/index.js
CHANGED
|
@@ -21499,9 +21499,6 @@ function validateDSL(code) {
|
|
|
21499
21499
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
21500
21500
|
errors.push(`Generator functions are not allowed at position ${node.start}`);
|
|
21501
21501
|
}
|
|
21502
|
-
if (node.type === "Literal" && node.regex) {
|
|
21503
|
-
errors.push(`Regex literals are not supported at position ${node.start}. Use String methods like indexOf(), includes(), startsWith() instead.`);
|
|
21504
|
-
}
|
|
21505
21502
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
21506
21503
|
errors.push(`Blocked identifier: '${node.name}' at position ${node.start}`);
|
|
21507
21504
|
}
|
|
@@ -21540,18 +21537,23 @@ var init_validator = __esm({
|
|
|
21540
21537
|
"BlockStatement",
|
|
21541
21538
|
"VariableDeclaration",
|
|
21542
21539
|
"VariableDeclarator",
|
|
21540
|
+
"FunctionDeclaration",
|
|
21543
21541
|
"ArrowFunctionExpression",
|
|
21544
21542
|
"FunctionExpression",
|
|
21545
21543
|
"CallExpression",
|
|
21544
|
+
"NewExpression",
|
|
21546
21545
|
"MemberExpression",
|
|
21547
21546
|
"Identifier",
|
|
21548
21547
|
"Literal",
|
|
21549
21548
|
"TemplateLiteral",
|
|
21550
21549
|
"TemplateElement",
|
|
21550
|
+
"TaggedTemplateExpression",
|
|
21551
21551
|
"ArrayExpression",
|
|
21552
21552
|
"ObjectExpression",
|
|
21553
21553
|
"SpreadElement",
|
|
21554
21554
|
"IfStatement",
|
|
21555
|
+
"SwitchStatement",
|
|
21556
|
+
"SwitchCase",
|
|
21555
21557
|
"ConditionalExpression",
|
|
21556
21558
|
"ForOfStatement",
|
|
21557
21559
|
"ForInStatement",
|
|
@@ -21763,6 +21765,17 @@ function traceToolCall(toolName, fn, tracer, logFn) {
|
|
|
21763
21765
|
}
|
|
21764
21766
|
};
|
|
21765
21767
|
}
|
|
21768
|
+
function tryParseJSONValue(text) {
|
|
21769
|
+
if (typeof text !== "string") return text;
|
|
21770
|
+
const firstChar = text.trimStart()[0];
|
|
21771
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
21772
|
+
try {
|
|
21773
|
+
return JSON.parse(text);
|
|
21774
|
+
} catch (_) {
|
|
21775
|
+
}
|
|
21776
|
+
}
|
|
21777
|
+
return text;
|
|
21778
|
+
}
|
|
21766
21779
|
function generateSandboxGlobals(options) {
|
|
21767
21780
|
const {
|
|
21768
21781
|
toolImplementations = {},
|
|
@@ -21814,7 +21827,13 @@ function generateSandboxGlobals(options) {
|
|
|
21814
21827
|
if (mcpBridge) {
|
|
21815
21828
|
for (const [name, tool5] of Object.entries(mcpTools)) {
|
|
21816
21829
|
const rawMcpFn = async (params = {}) => {
|
|
21817
|
-
|
|
21830
|
+
const result = await tool5.execute(params);
|
|
21831
|
+
const text = result?.content?.[0]?.text;
|
|
21832
|
+
if (text === void 0) {
|
|
21833
|
+
if (typeof result === "string") return tryParseJSONValue(result);
|
|
21834
|
+
return result;
|
|
21835
|
+
}
|
|
21836
|
+
return tryParseJSONValue(text);
|
|
21818
21837
|
};
|
|
21819
21838
|
globals[name] = traceToolCall(name, rawMcpFn, tracer, logFn);
|
|
21820
21839
|
}
|
|
@@ -21996,7 +22015,6 @@ function createDSLRuntime(options) {
|
|
|
21996
22015
|
mcpTools = {},
|
|
21997
22016
|
llmCall,
|
|
21998
22017
|
mapConcurrency = 3,
|
|
21999
|
-
timeoutMs = 12e4,
|
|
22000
22018
|
maxLoopIterations = 5e3,
|
|
22001
22019
|
tracer = null,
|
|
22002
22020
|
sessionStore = {},
|
|
@@ -22051,7 +22069,6 @@ ${validation.errors.join("\n")}`,
|
|
|
22051
22069
|
};
|
|
22052
22070
|
}
|
|
22053
22071
|
tracer?.addEvent?.("dsl.phase.execute_start", {
|
|
22054
|
-
"dsl.timeout_ms": timeoutMs,
|
|
22055
22072
|
"dsl.max_loop_iterations": maxLoopIterations
|
|
22056
22073
|
});
|
|
22057
22074
|
try {
|
|
@@ -22079,18 +22096,10 @@ ${validation.errors.join("\n")}`,
|
|
|
22079
22096
|
escapedError = reason;
|
|
22080
22097
|
};
|
|
22081
22098
|
process.on("unhandledRejection", rejectionHandler);
|
|
22082
|
-
let timeoutHandle;
|
|
22083
|
-
const executionPromise = exec6().run();
|
|
22084
|
-
const timeoutPromise = new Promise((_, reject2) => {
|
|
22085
|
-
timeoutHandle = setTimeout(() => {
|
|
22086
|
-
reject2(new Error(`Execution timed out after ${Math.round(timeoutMs / 1e3)}s. Script took too long \u2014 reduce the amount of work (fewer items, smaller data) or increase timeout.`));
|
|
22087
|
-
}, timeoutMs);
|
|
22088
|
-
});
|
|
22089
22099
|
let result;
|
|
22090
22100
|
try {
|
|
22091
|
-
result = await
|
|
22101
|
+
result = await exec6().run();
|
|
22092
22102
|
} finally {
|
|
22093
|
-
clearTimeout(timeoutHandle);
|
|
22094
22103
|
setTimeout(() => {
|
|
22095
22104
|
process.removeListener("unhandledRejection", rejectionHandler);
|
|
22096
22105
|
}, 500);
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -50948,9 +50948,6 @@ function validateDSL(code) {
|
|
|
50948
50948
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
50949
50949
|
errors.push(`Generator functions are not allowed at position ${node.start}`);
|
|
50950
50950
|
}
|
|
50951
|
-
if (node.type === "Literal" && node.regex) {
|
|
50952
|
-
errors.push(`Regex literals are not supported at position ${node.start}. Use String methods like indexOf(), includes(), startsWith() instead.`);
|
|
50953
|
-
}
|
|
50954
50951
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
50955
50952
|
errors.push(`Blocked identifier: '${node.name}' at position ${node.start}`);
|
|
50956
50953
|
}
|
|
@@ -50989,18 +50986,23 @@ var init_validator = __esm({
|
|
|
50989
50986
|
"BlockStatement",
|
|
50990
50987
|
"VariableDeclaration",
|
|
50991
50988
|
"VariableDeclarator",
|
|
50989
|
+
"FunctionDeclaration",
|
|
50992
50990
|
"ArrowFunctionExpression",
|
|
50993
50991
|
"FunctionExpression",
|
|
50994
50992
|
"CallExpression",
|
|
50993
|
+
"NewExpression",
|
|
50995
50994
|
"MemberExpression",
|
|
50996
50995
|
"Identifier",
|
|
50997
50996
|
"Literal",
|
|
50998
50997
|
"TemplateLiteral",
|
|
50999
50998
|
"TemplateElement",
|
|
50999
|
+
"TaggedTemplateExpression",
|
|
51000
51000
|
"ArrayExpression",
|
|
51001
51001
|
"ObjectExpression",
|
|
51002
51002
|
"SpreadElement",
|
|
51003
51003
|
"IfStatement",
|
|
51004
|
+
"SwitchStatement",
|
|
51005
|
+
"SwitchCase",
|
|
51004
51006
|
"ConditionalExpression",
|
|
51005
51007
|
"ForOfStatement",
|
|
51006
51008
|
"ForInStatement",
|
|
@@ -51212,6 +51214,17 @@ function traceToolCall(toolName, fn, tracer, logFn) {
|
|
|
51212
51214
|
}
|
|
51213
51215
|
};
|
|
51214
51216
|
}
|
|
51217
|
+
function tryParseJSONValue(text) {
|
|
51218
|
+
if (typeof text !== "string") return text;
|
|
51219
|
+
const firstChar = text.trimStart()[0];
|
|
51220
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
51221
|
+
try {
|
|
51222
|
+
return JSON.parse(text);
|
|
51223
|
+
} catch (_) {
|
|
51224
|
+
}
|
|
51225
|
+
}
|
|
51226
|
+
return text;
|
|
51227
|
+
}
|
|
51215
51228
|
function generateSandboxGlobals(options) {
|
|
51216
51229
|
const {
|
|
51217
51230
|
toolImplementations = {},
|
|
@@ -51263,7 +51276,13 @@ function generateSandboxGlobals(options) {
|
|
|
51263
51276
|
if (mcpBridge) {
|
|
51264
51277
|
for (const [name14, tool5] of Object.entries(mcpTools)) {
|
|
51265
51278
|
const rawMcpFn = async (params = {}) => {
|
|
51266
|
-
|
|
51279
|
+
const result = await tool5.execute(params);
|
|
51280
|
+
const text = result?.content?.[0]?.text;
|
|
51281
|
+
if (text === void 0) {
|
|
51282
|
+
if (typeof result === "string") return tryParseJSONValue(result);
|
|
51283
|
+
return result;
|
|
51284
|
+
}
|
|
51285
|
+
return tryParseJSONValue(text);
|
|
51267
51286
|
};
|
|
51268
51287
|
globals[name14] = traceToolCall(name14, rawMcpFn, tracer, logFn);
|
|
51269
51288
|
}
|
|
@@ -51445,7 +51464,6 @@ function createDSLRuntime(options) {
|
|
|
51445
51464
|
mcpTools = {},
|
|
51446
51465
|
llmCall,
|
|
51447
51466
|
mapConcurrency = 3,
|
|
51448
|
-
timeoutMs = 12e4,
|
|
51449
51467
|
maxLoopIterations = 5e3,
|
|
51450
51468
|
tracer = null,
|
|
51451
51469
|
sessionStore = {},
|
|
@@ -51500,7 +51518,6 @@ ${validation.errors.join("\n")}`,
|
|
|
51500
51518
|
};
|
|
51501
51519
|
}
|
|
51502
51520
|
tracer?.addEvent?.("dsl.phase.execute_start", {
|
|
51503
|
-
"dsl.timeout_ms": timeoutMs,
|
|
51504
51521
|
"dsl.max_loop_iterations": maxLoopIterations
|
|
51505
51522
|
});
|
|
51506
51523
|
try {
|
|
@@ -51528,18 +51545,10 @@ ${validation.errors.join("\n")}`,
|
|
|
51528
51545
|
escapedError = reason;
|
|
51529
51546
|
};
|
|
51530
51547
|
process.on("unhandledRejection", rejectionHandler);
|
|
51531
|
-
let timeoutHandle;
|
|
51532
|
-
const executionPromise = exec6().run();
|
|
51533
|
-
const timeoutPromise = new Promise((_, reject2) => {
|
|
51534
|
-
timeoutHandle = setTimeout(() => {
|
|
51535
|
-
reject2(new Error(`Execution timed out after ${Math.round(timeoutMs / 1e3)}s. Script took too long \u2014 reduce the amount of work (fewer items, smaller data) or increase timeout.`));
|
|
51536
|
-
}, timeoutMs);
|
|
51537
|
-
});
|
|
51538
51548
|
let result;
|
|
51539
51549
|
try {
|
|
51540
|
-
result = await
|
|
51550
|
+
result = await exec6().run();
|
|
51541
51551
|
} finally {
|
|
51542
|
-
clearTimeout(timeoutHandle);
|
|
51543
51552
|
setTimeout(() => {
|
|
51544
51553
|
process.removeListener("unhandledRejection", rejectionHandler);
|
|
51545
51554
|
}, 500);
|
package/cjs/index.cjs
CHANGED
|
@@ -112363,9 +112363,6 @@ function validateDSL(code) {
|
|
|
112363
112363
|
if (node.type === "FunctionExpression" && node.generator) {
|
|
112364
112364
|
errors.push(`Generator functions are not allowed at position ${node.start}`);
|
|
112365
112365
|
}
|
|
112366
|
-
if (node.type === "Literal" && node.regex) {
|
|
112367
|
-
errors.push(`Regex literals are not supported at position ${node.start}. Use String methods like indexOf(), includes(), startsWith() instead.`);
|
|
112368
|
-
}
|
|
112369
112366
|
if (node.type === "Identifier" && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
112370
112367
|
errors.push(`Blocked identifier: '${node.name}' at position ${node.start}`);
|
|
112371
112368
|
}
|
|
@@ -112404,18 +112401,23 @@ var init_validator = __esm({
|
|
|
112404
112401
|
"BlockStatement",
|
|
112405
112402
|
"VariableDeclaration",
|
|
112406
112403
|
"VariableDeclarator",
|
|
112404
|
+
"FunctionDeclaration",
|
|
112407
112405
|
"ArrowFunctionExpression",
|
|
112408
112406
|
"FunctionExpression",
|
|
112409
112407
|
"CallExpression",
|
|
112408
|
+
"NewExpression",
|
|
112410
112409
|
"MemberExpression",
|
|
112411
112410
|
"Identifier",
|
|
112412
112411
|
"Literal",
|
|
112413
112412
|
"TemplateLiteral",
|
|
112414
112413
|
"TemplateElement",
|
|
112414
|
+
"TaggedTemplateExpression",
|
|
112415
112415
|
"ArrayExpression",
|
|
112416
112416
|
"ObjectExpression",
|
|
112417
112417
|
"SpreadElement",
|
|
112418
112418
|
"IfStatement",
|
|
112419
|
+
"SwitchStatement",
|
|
112420
|
+
"SwitchCase",
|
|
112419
112421
|
"ConditionalExpression",
|
|
112420
112422
|
"ForOfStatement",
|
|
112421
112423
|
"ForInStatement",
|
|
@@ -112627,6 +112629,17 @@ function traceToolCall(toolName, fn, tracer, logFn) {
|
|
|
112627
112629
|
}
|
|
112628
112630
|
};
|
|
112629
112631
|
}
|
|
112632
|
+
function tryParseJSONValue(text) {
|
|
112633
|
+
if (typeof text !== "string") return text;
|
|
112634
|
+
const firstChar = text.trimStart()[0];
|
|
112635
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
112636
|
+
try {
|
|
112637
|
+
return JSON.parse(text);
|
|
112638
|
+
} catch (_) {
|
|
112639
|
+
}
|
|
112640
|
+
}
|
|
112641
|
+
return text;
|
|
112642
|
+
}
|
|
112630
112643
|
function generateSandboxGlobals(options) {
|
|
112631
112644
|
const {
|
|
112632
112645
|
toolImplementations = {},
|
|
@@ -112678,7 +112691,13 @@ function generateSandboxGlobals(options) {
|
|
|
112678
112691
|
if (mcpBridge) {
|
|
112679
112692
|
for (const [name14, tool5] of Object.entries(mcpTools)) {
|
|
112680
112693
|
const rawMcpFn = async (params = {}) => {
|
|
112681
|
-
|
|
112694
|
+
const result = await tool5.execute(params);
|
|
112695
|
+
const text = result?.content?.[0]?.text;
|
|
112696
|
+
if (text === void 0) {
|
|
112697
|
+
if (typeof result === "string") return tryParseJSONValue(result);
|
|
112698
|
+
return result;
|
|
112699
|
+
}
|
|
112700
|
+
return tryParseJSONValue(text);
|
|
112682
112701
|
};
|
|
112683
112702
|
globals[name14] = traceToolCall(name14, rawMcpFn, tracer, logFn);
|
|
112684
112703
|
}
|
|
@@ -112860,7 +112879,6 @@ function createDSLRuntime(options) {
|
|
|
112860
112879
|
mcpTools = {},
|
|
112861
112880
|
llmCall,
|
|
112862
112881
|
mapConcurrency = 3,
|
|
112863
|
-
timeoutMs = 12e4,
|
|
112864
112882
|
maxLoopIterations = 5e3,
|
|
112865
112883
|
tracer = null,
|
|
112866
112884
|
sessionStore = {},
|
|
@@ -112915,7 +112933,6 @@ ${validation.errors.join("\n")}`,
|
|
|
112915
112933
|
};
|
|
112916
112934
|
}
|
|
112917
112935
|
tracer?.addEvent?.("dsl.phase.execute_start", {
|
|
112918
|
-
"dsl.timeout_ms": timeoutMs,
|
|
112919
112936
|
"dsl.max_loop_iterations": maxLoopIterations
|
|
112920
112937
|
});
|
|
112921
112938
|
try {
|
|
@@ -112943,18 +112960,10 @@ ${validation.errors.join("\n")}`,
|
|
|
112943
112960
|
escapedError = reason;
|
|
112944
112961
|
};
|
|
112945
112962
|
process.on("unhandledRejection", rejectionHandler);
|
|
112946
|
-
let timeoutHandle;
|
|
112947
|
-
const executionPromise = exec6().run();
|
|
112948
|
-
const timeoutPromise = new Promise((_, reject2) => {
|
|
112949
|
-
timeoutHandle = setTimeout(() => {
|
|
112950
|
-
reject2(new Error(`Execution timed out after ${Math.round(timeoutMs / 1e3)}s. Script took too long \u2014 reduce the amount of work (fewer items, smaller data) or increase timeout.`));
|
|
112951
|
-
}, timeoutMs);
|
|
112952
|
-
});
|
|
112953
112963
|
let result;
|
|
112954
112964
|
try {
|
|
112955
|
-
result = await
|
|
112965
|
+
result = await exec6().run();
|
|
112956
112966
|
} finally {
|
|
112957
|
-
clearTimeout(timeoutHandle);
|
|
112958
112967
|
setTimeout(() => {
|
|
112959
112968
|
process.removeListener("unhandledRejection", rejectionHandler);
|
|
112960
112969
|
}, 500);
|
package/package.json
CHANGED
|
@@ -117,6 +117,23 @@ function traceToolCall(toolName, fn, tracer, logFn) {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Try to parse a string as JSON if it looks like a JSON object or array.
|
|
122
|
+
* Checks if the first non-whitespace character is '{' or '[' before attempting parse.
|
|
123
|
+
* Returns the original string if it's not JSON.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} text - The text to try parsing
|
|
126
|
+
* @returns {any} Parsed JSON value, or the original string
|
|
127
|
+
*/
|
|
128
|
+
function tryParseJSONValue(text) {
|
|
129
|
+
if (typeof text !== 'string') return text;
|
|
130
|
+
const firstChar = text.trimStart()[0];
|
|
131
|
+
if (firstChar === '{' || firstChar === '[') {
|
|
132
|
+
try { return JSON.parse(text); } catch (_) { /* not valid JSON */ }
|
|
133
|
+
}
|
|
134
|
+
return text;
|
|
135
|
+
}
|
|
136
|
+
|
|
120
137
|
/**
|
|
121
138
|
* Generate sandbox globals that bridge DSL function calls to real tool implementations.
|
|
122
139
|
*
|
|
@@ -195,7 +212,15 @@ export function generateSandboxGlobals(options) {
|
|
|
195
212
|
if (mcpBridge) {
|
|
196
213
|
for (const [name, tool] of Object.entries(mcpTools)) {
|
|
197
214
|
const rawMcpFn = async (params = {}) => {
|
|
198
|
-
|
|
215
|
+
const result = await tool.execute(params);
|
|
216
|
+
// Extract text from MCP response envelope: { content: [{ type: 'text', text: '...' }] }
|
|
217
|
+
const text = result?.content?.[0]?.text;
|
|
218
|
+
if (text === undefined) {
|
|
219
|
+
// No envelope — if raw result is a JSON-like string, try parsing it
|
|
220
|
+
if (typeof result === 'string') return tryParseJSONValue(result);
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
return tryParseJSONValue(text);
|
|
199
224
|
};
|
|
200
225
|
globals[name] = traceToolCall(name, rawMcpFn, tracer, logFn);
|
|
201
226
|
}
|
package/src/agent/dsl/runtime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Orchestrates the full pipeline:
|
|
5
5
|
* 1. Validate (AST whitelist)
|
|
6
6
|
* 2. Transform (inject await, wrap in async IIFE)
|
|
7
|
-
* 3. Execute in SandboxJS with tool globals
|
|
7
|
+
* 3. Execute in SandboxJS with tool globals
|
|
8
8
|
*
|
|
9
9
|
* Returns the result or a structured error.
|
|
10
10
|
*/
|
|
@@ -25,7 +25,6 @@ const Sandbox = SandboxModule.default || SandboxModule;
|
|
|
25
25
|
* @param {Object} [options.mcpTools={}] - MCP tool metadata
|
|
26
26
|
* @param {Function} options.llmCall - Function for LLM() calls: (instruction, data, options?) => Promise<any>
|
|
27
27
|
* @param {number} [options.mapConcurrency=3] - Concurrency limit for map()
|
|
28
|
-
* @param {number} [options.timeoutMs=120000] - Execution timeout in milliseconds (default 2 min)
|
|
29
28
|
* @param {number} [options.maxLoopIterations=5000] - Max iterations for while/for loops
|
|
30
29
|
* @param {Object} [options.tracer=null] - SimpleAppTracer instance for OTEL telemetry
|
|
31
30
|
* @returns {Object} Runtime with execute() method
|
|
@@ -37,7 +36,6 @@ export function createDSLRuntime(options) {
|
|
|
37
36
|
mcpTools = {},
|
|
38
37
|
llmCall,
|
|
39
38
|
mapConcurrency = 3,
|
|
40
|
-
timeoutMs = 120000,
|
|
41
39
|
maxLoopIterations = 5000,
|
|
42
40
|
tracer = null,
|
|
43
41
|
sessionStore = {},
|
|
@@ -108,9 +106,8 @@ export function createDSLRuntime(options) {
|
|
|
108
106
|
};
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
// Step 3: Execute in SandboxJS
|
|
109
|
+
// Step 3: Execute in SandboxJS
|
|
112
110
|
tracer?.addEvent?.('dsl.phase.execute_start', {
|
|
113
|
-
'dsl.timeout_ms': timeoutMs,
|
|
114
111
|
'dsl.max_loop_iterations': maxLoopIterations,
|
|
115
112
|
});
|
|
116
113
|
|
|
@@ -147,20 +144,10 @@ export function createDSLRuntime(options) {
|
|
|
147
144
|
};
|
|
148
145
|
process.on('unhandledRejection', rejectionHandler);
|
|
149
146
|
|
|
150
|
-
// Race execution against timeout
|
|
151
|
-
let timeoutHandle;
|
|
152
|
-
const executionPromise = exec().run();
|
|
153
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
154
|
-
timeoutHandle = setTimeout(() => {
|
|
155
|
-
reject(new Error(`Execution timed out after ${Math.round(timeoutMs / 1000)}s. Script took too long — reduce the amount of work (fewer items, smaller data) or increase timeout.`));
|
|
156
|
-
}, timeoutMs);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
147
|
let result;
|
|
160
148
|
try {
|
|
161
|
-
result = await
|
|
149
|
+
result = await exec().run();
|
|
162
150
|
} finally {
|
|
163
|
-
clearTimeout(timeoutHandle);
|
|
164
151
|
// Delay handler removal — SandboxJS can throw async errors after execution completes
|
|
165
152
|
setTimeout(() => {
|
|
166
153
|
process.removeListener('unhandledRejection', rejectionHandler);
|
|
@@ -16,18 +16,23 @@ const ALLOWED_NODE_TYPES = new Set([
|
|
|
16
16
|
'BlockStatement',
|
|
17
17
|
'VariableDeclaration',
|
|
18
18
|
'VariableDeclarator',
|
|
19
|
+
'FunctionDeclaration',
|
|
19
20
|
'ArrowFunctionExpression',
|
|
20
21
|
'FunctionExpression',
|
|
21
22
|
'CallExpression',
|
|
23
|
+
'NewExpression',
|
|
22
24
|
'MemberExpression',
|
|
23
25
|
'Identifier',
|
|
24
26
|
'Literal',
|
|
25
27
|
'TemplateLiteral',
|
|
26
28
|
'TemplateElement',
|
|
29
|
+
'TaggedTemplateExpression',
|
|
27
30
|
'ArrayExpression',
|
|
28
31
|
'ObjectExpression',
|
|
29
32
|
'SpreadElement',
|
|
30
33
|
'IfStatement',
|
|
34
|
+
'SwitchStatement',
|
|
35
|
+
'SwitchCase',
|
|
31
36
|
'ConditionalExpression',
|
|
32
37
|
'ForOfStatement',
|
|
33
38
|
'ForInStatement',
|
|
@@ -127,10 +132,6 @@ export function validateDSL(code) {
|
|
|
127
132
|
errors.push(`Generator functions are not allowed at position ${node.start}`);
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
// Block regex literals — SandboxJS doesn't support them
|
|
131
|
-
if (node.type === 'Literal' && node.regex) {
|
|
132
|
-
errors.push(`Regex literals are not supported at position ${node.start}. Use String methods like indexOf(), includes(), startsWith() instead.`);
|
|
133
|
-
}
|
|
134
135
|
|
|
135
136
|
// Check identifiers against blocklist
|
|
136
137
|
if (node.type === 'Identifier' && BLOCKED_IDENTIFIERS.has(node.name)) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|