@cogitator-ai/self-modifying 17.0.17 → 17.0.18
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/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -1
- package/dist/architecture-evolution/capability-analyzer.js +6 -3
- package/dist/architecture-evolution/capability-analyzer.js.map +1 -1
- package/dist/architecture-evolution/evolution-strategy.d.ts.map +1 -1
- package/dist/architecture-evolution/evolution-strategy.js +4 -2
- package/dist/architecture-evolution/evolution-strategy.js.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.js +32 -9
- package/dist/architecture-evolution/parameter-optimizer.js.map +1 -1
- package/dist/architecture-evolution/prompts.d.ts.map +1 -1
- package/dist/architecture-evolution/prompts.js +42 -4
- package/dist/architecture-evolution/prompts.js.map +1 -1
- package/dist/constraints/modification-validator.d.ts.map +1 -1
- package/dist/constraints/modification-validator.js +16 -5
- package/dist/constraints/modification-validator.js.map +1 -1
- package/dist/constraints/rollback-manager.d.ts +1 -0
- package/dist/constraints/rollback-manager.d.ts.map +1 -1
- package/dist/constraints/rollback-manager.js +25 -1
- package/dist/constraints/rollback-manager.js.map +1 -1
- package/dist/events/event-emitter.d.ts +0 -1
- package/dist/events/event-emitter.d.ts.map +1 -1
- package/dist/events/event-emitter.js +6 -10
- package/dist/events/event-emitter.js.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.d.ts +1 -1
- package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.js +22 -13
- package/dist/meta-reasoning/meta-reasoner.js.map +1 -1
- package/dist/meta-reasoning/observation-collector.d.ts.map +1 -1
- package/dist/meta-reasoning/observation-collector.js +6 -2
- package/dist/meta-reasoning/observation-collector.js.map +1 -1
- package/dist/meta-reasoning/prompts.d.ts.map +1 -1
- package/dist/meta-reasoning/prompts.js +5 -38
- package/dist/meta-reasoning/prompts.js.map +1 -1
- package/dist/meta-reasoning/strategy-selector.d.ts.map +1 -1
- package/dist/meta-reasoning/strategy-selector.js +6 -0
- package/dist/meta-reasoning/strategy-selector.js.map +1 -1
- package/dist/self-modifying-agent.d.ts +8 -1
- package/dist/self-modifying-agent.d.ts.map +1 -1
- package/dist/self-modifying-agent.js +108 -14
- package/dist/self-modifying-agent.js.map +1 -1
- package/dist/tool-generation/gap-analyzer.d.ts.map +1 -1
- package/dist/tool-generation/gap-analyzer.js +3 -2
- package/dist/tool-generation/gap-analyzer.js.map +1 -1
- package/dist/tool-generation/generated-tool-store.d.ts.map +1 -1
- package/dist/tool-generation/generated-tool-store.js +90 -10
- package/dist/tool-generation/generated-tool-store.js.map +1 -1
- package/dist/tool-generation/index.d.ts +1 -1
- package/dist/tool-generation/index.d.ts.map +1 -1
- package/dist/tool-generation/index.js +1 -1
- package/dist/tool-generation/index.js.map +1 -1
- package/dist/tool-generation/prompts.d.ts +3 -3
- package/dist/tool-generation/prompts.d.ts.map +1 -1
- package/dist/tool-generation/prompts.js +65 -12
- package/dist/tool-generation/prompts.js.map +1 -1
- package/dist/tool-generation/tool-generator.d.ts +1 -1
- package/dist/tool-generation/tool-generator.d.ts.map +1 -1
- package/dist/tool-generation/tool-generator.js +55 -3
- package/dist/tool-generation/tool-generator.js.map +1 -1
- package/dist/tool-generation/tool-sandbox.d.ts +2 -6
- package/dist/tool-generation/tool-sandbox.d.ts.map +1 -1
- package/dist/tool-generation/tool-sandbox.js +220 -130
- package/dist/tool-generation/tool-sandbox.js.map +1 -1
- package/dist/tool-generation/tool-validator.d.ts +1 -0
- package/dist/tool-generation/tool-validator.d.ts.map +1 -1
- package/dist/tool-generation/tool-validator.js +48 -1
- package/dist/tool-generation/tool-validator.js.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +30 -5
- package/dist/utils/index.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/self-modifying-agent.test.ts +42 -10
- package/src/architecture-evolution/capability-analyzer.ts +7 -4
- package/src/architecture-evolution/evolution-strategy.ts +5 -2
- package/src/architecture-evolution/parameter-optimizer.ts +39 -15
- package/src/architecture-evolution/prompts.ts +48 -4
- package/src/constraints/modification-validator.ts +19 -8
- package/src/constraints/rollback-manager.ts +20 -1
- package/src/events/event-emitter.ts +6 -15
- package/src/meta-reasoning/meta-reasoner.ts +22 -15
- package/src/meta-reasoning/observation-collector.ts +6 -2
- package/src/meta-reasoning/prompts.ts +5 -44
- package/src/meta-reasoning/strategy-selector.ts +6 -0
- package/src/self-modifying-agent.ts +123 -14
- package/src/tool-generation/gap-analyzer.ts +3 -2
- package/src/tool-generation/generated-tool-store.ts +97 -10
- package/src/tool-generation/index.ts +1 -1
- package/src/tool-generation/prompts.ts +81 -12
- package/src/tool-generation/tool-generator.ts +71 -3
- package/src/tool-generation/tool-sandbox.ts +241 -170
- package/src/tool-generation/tool-validator.ts +51 -1
- package/src/utils/index.ts +29 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Worker } from 'node:worker_threads';
|
|
1
2
|
import type { ToolSandboxConfig, ToolSandboxResult, GeneratedTool } from '@cogitator-ai/types';
|
|
2
3
|
|
|
3
4
|
export const DEFAULT_SANDBOX_CONFIG: ToolSandboxConfig = {
|
|
@@ -8,32 +9,44 @@ export const DEFAULT_SANDBOX_CONFIG: ToolSandboxConfig = {
|
|
|
8
9
|
isolationLevel: 'strict',
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
Number
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
const WORKER_SOURCE = [
|
|
13
|
+
'const { parentPort, workerData } = require("worker_threads");',
|
|
14
|
+
'const vm = require("vm");',
|
|
15
|
+
'const { code, params } = workerData;',
|
|
16
|
+
'const logs = [];',
|
|
17
|
+
'const sandbox = Object.create(null);',
|
|
18
|
+
'Object.assign(sandbox, {',
|
|
19
|
+
' console: {',
|
|
20
|
+
' log: (...a) => { logs.push("[LOG] " + a.map(String).join(" ")); },',
|
|
21
|
+
' warn: (...a) => { logs.push("[WARN] " + a.map(String).join(" ")); },',
|
|
22
|
+
' error: (...a) => { logs.push("[ERROR] " + a.map(String).join(" ")); },',
|
|
23
|
+
' },',
|
|
24
|
+
' Math, JSON, Date, Array, Object, String, Number, Boolean, RegExp, Map, Set,',
|
|
25
|
+
' Promise, Error, TypeError, RangeError,',
|
|
26
|
+
' parseInt, parseFloat, isNaN, isFinite,',
|
|
27
|
+
' encodeURIComponent, decodeURIComponent, encodeURI, decodeURI,',
|
|
28
|
+
' undefined, NaN, Infinity,',
|
|
29
|
+
' params,',
|
|
30
|
+
'});',
|
|
31
|
+
'const context = vm.createContext(sandbox);',
|
|
32
|
+
'try {',
|
|
33
|
+
' const wrapped = "(async function sandboxedExecution() {\\n" +',
|
|
34
|
+
' code + "\\n" +',
|
|
35
|
+
' "if (typeof execute === \\"function\\") { return await execute(params); }\\n" +',
|
|
36
|
+
' "throw new Error(\\"Implementation must define an execute function\\");\\n" +',
|
|
37
|
+
' "})()";',
|
|
38
|
+
' const promise = vm.runInContext(wrapped, context);',
|
|
39
|
+
' Promise.resolve(promise).then(',
|
|
40
|
+
' (result) => { parentPort.postMessage({ success: true, result, logs }); },',
|
|
41
|
+
' (err) => { parentPort.postMessage({ success: false, error: err instanceof Error ? err.message : String(err), logs }); }',
|
|
42
|
+
' );',
|
|
43
|
+
'} catch (err) {',
|
|
44
|
+
' parentPort.postMessage({ success: false, error: err instanceof Error ? err.message : String(err), logs });',
|
|
45
|
+
'}',
|
|
46
|
+
].join('\n');
|
|
33
47
|
|
|
34
48
|
export class ToolSandbox {
|
|
35
49
|
private readonly config: ToolSandboxConfig;
|
|
36
|
-
private readonly logs: string[] = [];
|
|
37
50
|
|
|
38
51
|
constructor(config: Partial<ToolSandboxConfig> = {}) {
|
|
39
52
|
this.config = { ...DEFAULT_SANDBOX_CONFIG, ...config };
|
|
@@ -41,41 +54,24 @@ export class ToolSandbox {
|
|
|
41
54
|
|
|
42
55
|
async execute(tool: GeneratedTool, params: unknown): Promise<ToolSandboxResult> {
|
|
43
56
|
const startTime = Date.now();
|
|
44
|
-
this.logs.length = 0;
|
|
45
|
-
|
|
46
|
-
if (!this.config.enabled) {
|
|
47
|
-
return this.executeUnsandboxed(tool, params, startTime);
|
|
48
|
-
}
|
|
49
57
|
|
|
50
58
|
try {
|
|
51
59
|
this.validateImplementation(tool.implementation);
|
|
52
|
-
|
|
53
|
-
const context = this.createContext();
|
|
54
|
-
const wrappedCode = this.wrapImplementation(tool.implementation);
|
|
55
|
-
|
|
56
|
-
const result = await this.executeWithTimeout(
|
|
57
|
-
wrappedCode,
|
|
58
|
-
context,
|
|
59
|
-
params,
|
|
60
|
-
this.config.maxExecutionTime
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
success: true,
|
|
65
|
-
result,
|
|
66
|
-
executionTime: Date.now() - startTime,
|
|
67
|
-
memoryUsed: this.estimateMemoryUsage(result),
|
|
68
|
-
logs: [...this.logs],
|
|
69
|
-
};
|
|
70
60
|
} catch (error) {
|
|
71
61
|
return {
|
|
72
62
|
success: false,
|
|
73
63
|
error: error instanceof Error ? error.message : String(error),
|
|
74
64
|
executionTime: Date.now() - startTime,
|
|
75
65
|
memoryUsed: 0,
|
|
76
|
-
logs: [
|
|
66
|
+
logs: [],
|
|
77
67
|
};
|
|
78
68
|
}
|
|
69
|
+
|
|
70
|
+
if (!this.config.enabled) {
|
|
71
|
+
return this.executeUnsandboxed(tool, params, startTime);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return this.executeInWorker(tool.implementation, params, startTime);
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
async testWithCases(
|
|
@@ -107,7 +103,7 @@ export class ToolSandbox {
|
|
|
107
103
|
if (testCase.shouldThrow) {
|
|
108
104
|
passed = !execResult.success;
|
|
109
105
|
} else if (testCase.expectedOutput !== undefined) {
|
|
110
|
-
passed = execResult.success &&
|
|
106
|
+
passed = execResult.success && deepEqual(execResult.result, testCase.expectedOutput);
|
|
111
107
|
} else {
|
|
112
108
|
passed = execResult.success;
|
|
113
109
|
}
|
|
@@ -129,44 +125,68 @@ export class ToolSandbox {
|
|
|
129
125
|
}
|
|
130
126
|
|
|
131
127
|
private validateImplementation(code: string): void {
|
|
132
|
-
const forbidden = [
|
|
133
|
-
/\beval\s*\(/,
|
|
134
|
-
/\
|
|
135
|
-
/\
|
|
136
|
-
/\bimport\s*\(/,
|
|
137
|
-
/\brequire\s*\(/,
|
|
138
|
-
/\bprocess
|
|
139
|
-
/\bglobal
|
|
140
|
-
/\bglobalThis
|
|
141
|
-
/\bwindow
|
|
142
|
-
/\bdocument
|
|
143
|
-
/\bchild_process/,
|
|
144
|
-
/\
|
|
145
|
-
/\
|
|
146
|
-
/\
|
|
147
|
-
/\
|
|
148
|
-
/\
|
|
149
|
-
/\
|
|
150
|
-
/\
|
|
151
|
-
/\
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
128
|
+
const forbidden: Array<{ pattern: RegExp; label: string }> = [
|
|
129
|
+
{ pattern: /\beval\s*\(/, label: 'eval()' },
|
|
130
|
+
{ pattern: /\bnew\s+Function\s*\(/, label: 'new Function()' },
|
|
131
|
+
{ pattern: /\bFunction\s*\.\s*prototype/, label: 'Function.prototype' },
|
|
132
|
+
{ pattern: /\bimport\s*\(/, label: 'dynamic import()' },
|
|
133
|
+
{ pattern: /\brequire\s*\(/, label: 'require()' },
|
|
134
|
+
{ pattern: /\bprocess\s*[[.]/, label: 'process access' },
|
|
135
|
+
{ pattern: /\bglobal\s*[[.]/, label: 'global access' },
|
|
136
|
+
{ pattern: /\bglobalThis\s*[[.]/, label: 'globalThis access' },
|
|
137
|
+
{ pattern: /\bwindow\s*[[.]/, label: 'window access' },
|
|
138
|
+
{ pattern: /\bdocument\s*[[.]/, label: 'document access' },
|
|
139
|
+
{ pattern: /\bchild_process\b/, label: 'child_process' },
|
|
140
|
+
{ pattern: /\brequire\s*\(\s*['"]fs['"]/, label: 'fs module' },
|
|
141
|
+
{ pattern: /\brequire\s*\(\s*['"]net['"]/, label: 'net module' },
|
|
142
|
+
{ pattern: /\brequire\s*\(\s*['"]dns['"]/, label: 'dns module' },
|
|
143
|
+
{ pattern: /\brequire\s*\(\s*['"]http['"]/, label: 'http module' },
|
|
144
|
+
{ pattern: /\brequire\s*\(\s*['"]https['"]/, label: 'https module' },
|
|
145
|
+
{ pattern: /\brequire\s*\(\s*['"]os['"]/, label: 'os module' },
|
|
146
|
+
{ pattern: /\bspawn\s*\(/, label: 'spawn()' },
|
|
147
|
+
{ pattern: /\bexecSync\s*\(/, label: 'execSync()' },
|
|
148
|
+
{ pattern: /\bexecFile\s*\(/, label: 'execFile()' },
|
|
149
|
+
{ pattern: /__proto__/, label: '__proto__' },
|
|
150
|
+
{ pattern: /\.\s*constructor\s*[[.(]/, label: 'constructor access' },
|
|
151
|
+
{ pattern: /\bgetPrototypeOf\s*\(/, label: 'getPrototypeOf()' },
|
|
152
|
+
{ pattern: /\bsetPrototypeOf\s*\(/, label: 'setPrototypeOf()' },
|
|
153
|
+
{ pattern: /\bReflect\s*[[.]/, label: 'Reflect access' },
|
|
154
|
+
{ pattern: /\bProxy\s*[[.(]/, label: 'Proxy access' },
|
|
155
|
+
{ pattern: /\bSymbol\s*\.\s*for\s*\(/, label: 'Symbol.for()' },
|
|
156
|
+
{ pattern: /\bSharedArrayBuffer\b/, label: 'SharedArrayBuffer' },
|
|
157
|
+
{ pattern: /\bAtomics\b/, label: 'Atomics' },
|
|
158
|
+
{ pattern: /\bWeakRef\b/, label: 'WeakRef' },
|
|
159
|
+
{ pattern: /\bFinalizationRegistry\b/, label: 'FinalizationRegistry' },
|
|
155
160
|
];
|
|
156
161
|
|
|
157
162
|
if (this.config.isolationLevel === 'strict') {
|
|
158
163
|
forbidden.push(
|
|
159
|
-
/\bfetch\s*\(/,
|
|
160
|
-
/\bXMLHttpRequest/,
|
|
161
|
-
/\bWebSocket/,
|
|
162
|
-
/\bsetTimeout\s*\(/,
|
|
163
|
-
/\bsetInterval\s*\(
|
|
164
|
+
{ pattern: /\bfetch\s*\(/, label: 'fetch()' },
|
|
165
|
+
{ pattern: /\bXMLHttpRequest\b/, label: 'XMLHttpRequest' },
|
|
166
|
+
{ pattern: /\bWebSocket\b/, label: 'WebSocket' },
|
|
167
|
+
{ pattern: /\bsetTimeout\s*\(/, label: 'setTimeout()' },
|
|
168
|
+
{ pattern: /\bsetInterval\s*\(/, label: 'setInterval()' }
|
|
164
169
|
);
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
for (const pattern of forbidden) {
|
|
172
|
+
for (const { pattern, label } of forbidden) {
|
|
168
173
|
if (pattern.test(code)) {
|
|
169
|
-
throw new Error(`Security violation: forbidden pattern detected - ${
|
|
174
|
+
throw new Error(`Security violation: forbidden pattern detected - ${label}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const bracketGlobalAccess = /\b(?:global|globalThis|process|window|document)\s*\[\s*['"`]/;
|
|
179
|
+
if (bracketGlobalAccess.test(code)) {
|
|
180
|
+
throw new Error('Security violation: bracket notation access to global objects');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const stringConcatEscape = /['"`]\s*\+\s*['"`]/;
|
|
184
|
+
if (stringConcatEscape.test(code)) {
|
|
185
|
+
const concatenated = code.replace(/['"`]\s*\+\s*['"`]/g, '');
|
|
186
|
+
for (const { pattern, label } of forbidden) {
|
|
187
|
+
if (pattern.test(concatenated)) {
|
|
188
|
+
throw new Error(`Security violation: obfuscated forbidden pattern detected - ${label}`);
|
|
189
|
+
}
|
|
170
190
|
}
|
|
171
191
|
}
|
|
172
192
|
|
|
@@ -176,82 +196,103 @@ export class ToolSandbox {
|
|
|
176
196
|
}
|
|
177
197
|
}
|
|
178
198
|
|
|
179
|
-
private
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
Math,
|
|
193
|
-
JSON,
|
|
194
|
-
Date,
|
|
195
|
-
Array,
|
|
196
|
-
Object,
|
|
197
|
-
String,
|
|
198
|
-
Number,
|
|
199
|
-
Boolean,
|
|
200
|
-
RegExp,
|
|
201
|
-
Map,
|
|
202
|
-
Set,
|
|
203
|
-
Promise,
|
|
204
|
-
Error,
|
|
205
|
-
TypeError,
|
|
206
|
-
RangeError,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
private wrapImplementation(code: string): string {
|
|
211
|
-
return `
|
|
212
|
-
"use strict";
|
|
213
|
-
return (async function sandboxedExecution(params, context) {
|
|
214
|
-
const { console, Math, JSON, Date, Array, Object, String, Number, Boolean, RegExp, Map, Set, Promise, Error, TypeError, RangeError } = context;
|
|
199
|
+
private async executeInWorker(
|
|
200
|
+
code: string,
|
|
201
|
+
params: unknown,
|
|
202
|
+
startTime: number
|
|
203
|
+
): Promise<ToolSandboxResult> {
|
|
204
|
+
let serializableParams: unknown;
|
|
205
|
+
try {
|
|
206
|
+
serializableParams = JSON.parse(JSON.stringify(params ?? null));
|
|
207
|
+
} catch {
|
|
208
|
+
serializableParams = null;
|
|
209
|
+
}
|
|
215
210
|
|
|
216
|
-
|
|
211
|
+
return new Promise<ToolSandboxResult>((resolve) => {
|
|
212
|
+
let settled = false;
|
|
217
213
|
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
const settle = (result: ToolSandboxResult) => {
|
|
215
|
+
if (!settled) {
|
|
216
|
+
settled = true;
|
|
217
|
+
resolve(result);
|
|
220
218
|
}
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
`;
|
|
224
|
-
}
|
|
219
|
+
};
|
|
225
220
|
|
|
226
|
-
|
|
227
|
-
wrappedCode: string,
|
|
228
|
-
context: SandboxContext,
|
|
229
|
-
params: unknown,
|
|
230
|
-
timeout: number
|
|
231
|
-
): Promise<unknown> {
|
|
232
|
-
return new Promise((resolve, reject) => {
|
|
233
|
-
const timer = setTimeout(() => {
|
|
234
|
-
reject(new Error(`Execution timeout: exceeded ${timeout}ms`));
|
|
235
|
-
}, timeout);
|
|
221
|
+
const maxMemoryMb = Math.max(4, Math.ceil(this.config.maxMemory / (1024 * 1024)));
|
|
236
222
|
|
|
223
|
+
let worker: Worker;
|
|
237
224
|
try {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
})
|
|
247
|
-
.catch((error: unknown) => {
|
|
248
|
-
clearTimeout(timer);
|
|
249
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
250
|
-
});
|
|
225
|
+
worker = new Worker(WORKER_SOURCE, {
|
|
226
|
+
eval: true,
|
|
227
|
+
workerData: { code, params: serializableParams },
|
|
228
|
+
resourceLimits: {
|
|
229
|
+
maxOldGenerationSizeMb: maxMemoryMb,
|
|
230
|
+
maxYoungGenerationSizeMb: Math.max(2, Math.ceil(maxMemoryMb / 4)),
|
|
231
|
+
},
|
|
232
|
+
});
|
|
251
233
|
} catch (error) {
|
|
252
|
-
|
|
253
|
-
|
|
234
|
+
settle({
|
|
235
|
+
success: false,
|
|
236
|
+
error: `Failed to create sandbox worker: ${error instanceof Error ? error.message : String(error)}`,
|
|
237
|
+
executionTime: Date.now() - startTime,
|
|
238
|
+
memoryUsed: 0,
|
|
239
|
+
logs: [],
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
254
242
|
}
|
|
243
|
+
|
|
244
|
+
const timer = setTimeout(() => {
|
|
245
|
+
void worker.terminate();
|
|
246
|
+
settle({
|
|
247
|
+
success: false,
|
|
248
|
+
error: `Execution timeout: exceeded ${this.config.maxExecutionTime}ms`,
|
|
249
|
+
executionTime: Date.now() - startTime,
|
|
250
|
+
memoryUsed: 0,
|
|
251
|
+
logs: [],
|
|
252
|
+
});
|
|
253
|
+
}, this.config.maxExecutionTime);
|
|
254
|
+
|
|
255
|
+
worker.on(
|
|
256
|
+
'message',
|
|
257
|
+
(msg: { success: boolean; result?: unknown; error?: string; logs?: string[] }) => {
|
|
258
|
+
clearTimeout(timer);
|
|
259
|
+
settle({
|
|
260
|
+
success: msg.success,
|
|
261
|
+
result: msg.result,
|
|
262
|
+
error: msg.error,
|
|
263
|
+
executionTime: Date.now() - startTime,
|
|
264
|
+
memoryUsed: estimateMemoryUsage(msg.result),
|
|
265
|
+
logs: msg.logs ?? [],
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
worker.on('error', (error: Error) => {
|
|
271
|
+
clearTimeout(timer);
|
|
272
|
+
settle({
|
|
273
|
+
success: false,
|
|
274
|
+
error: `Sandbox worker error: ${error.message}`,
|
|
275
|
+
executionTime: Date.now() - startTime,
|
|
276
|
+
memoryUsed: 0,
|
|
277
|
+
logs: [],
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
worker.on('exit', (exitCode: number) => {
|
|
282
|
+
clearTimeout(timer);
|
|
283
|
+
if (exitCode !== 0) {
|
|
284
|
+
settle({
|
|
285
|
+
success: false,
|
|
286
|
+
error:
|
|
287
|
+
exitCode === 134 || exitCode === 137
|
|
288
|
+
? `Execution exceeded memory limit (${this.config.maxMemory} bytes)`
|
|
289
|
+
: `Sandbox worker exited with code ${exitCode}`,
|
|
290
|
+
executionTime: Date.now() - startTime,
|
|
291
|
+
memoryUsed: 0,
|
|
292
|
+
logs: [],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
});
|
|
255
296
|
});
|
|
256
297
|
}
|
|
257
298
|
|
|
@@ -284,7 +325,7 @@ export class ToolSandbox {
|
|
|
284
325
|
success: true,
|
|
285
326
|
result,
|
|
286
327
|
executionTime: Date.now() - startTime,
|
|
287
|
-
memoryUsed:
|
|
328
|
+
memoryUsed: estimateMemoryUsage(result),
|
|
288
329
|
logs: [],
|
|
289
330
|
};
|
|
290
331
|
} catch (error) {
|
|
@@ -297,36 +338,66 @@ export class ToolSandbox {
|
|
|
297
338
|
};
|
|
298
339
|
}
|
|
299
340
|
}
|
|
341
|
+
}
|
|
300
342
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
343
|
+
function estimateMemoryUsage(value: unknown): number {
|
|
344
|
+
try {
|
|
345
|
+
const str = JSON.stringify(value);
|
|
346
|
+
return str ? str.length * 2 : 0;
|
|
347
|
+
} catch {
|
|
348
|
+
return 0;
|
|
308
349
|
}
|
|
350
|
+
}
|
|
309
351
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (typeof a !== typeof b) return false;
|
|
313
|
-
if (a === null || b === null) return a === b;
|
|
352
|
+
export function deepEqual(a: unknown, b: unknown, seen = new WeakSet<object>()): boolean {
|
|
353
|
+
if (Object.is(a, b)) return true;
|
|
314
354
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
355
|
+
if (typeof a !== typeof b) return false;
|
|
356
|
+
if (a === null || b === null) return a === b;
|
|
357
|
+
|
|
358
|
+
if (a instanceof Date && b instanceof Date) {
|
|
359
|
+
return a.getTime() === b.getTime();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (a instanceof RegExp && b instanceof RegExp) {
|
|
363
|
+
return a.source === b.source && a.flags === b.flags;
|
|
364
|
+
}
|
|
319
365
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
366
|
+
if (a instanceof Map && b instanceof Map) {
|
|
367
|
+
if (a.size !== b.size) return false;
|
|
368
|
+
for (const [key, val] of a) {
|
|
369
|
+
if (!b.has(key) || !deepEqual(val, b.get(key), seen)) return false;
|
|
370
|
+
}
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
325
373
|
|
|
326
|
-
|
|
327
|
-
|
|
374
|
+
if (a instanceof Set && b instanceof Set) {
|
|
375
|
+
if (a.size !== b.size) return false;
|
|
376
|
+
for (const val of a) {
|
|
377
|
+
if (!b.has(val)) return false;
|
|
328
378
|
}
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
329
381
|
|
|
330
|
-
|
|
382
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
383
|
+
if (a.length !== b.length) return false;
|
|
384
|
+
return a.every((val, i) => deepEqual(val, b[i], seen));
|
|
331
385
|
}
|
|
386
|
+
|
|
387
|
+
if (typeof a === 'object' && typeof b === 'object') {
|
|
388
|
+
const aObj = a as Record<string, unknown>;
|
|
389
|
+
const bObj = b as Record<string, unknown>;
|
|
390
|
+
|
|
391
|
+
if (seen.has(aObj) || seen.has(bObj)) return false;
|
|
392
|
+
seen.add(aObj);
|
|
393
|
+
seen.add(bObj);
|
|
394
|
+
|
|
395
|
+
const keysA = Object.keys(aObj).filter((k) => aObj[k] !== undefined);
|
|
396
|
+
const keysB = Object.keys(bObj).filter((k) => bObj[k] !== undefined);
|
|
397
|
+
|
|
398
|
+
if (keysA.length !== keysB.length) return false;
|
|
399
|
+
return keysA.every((key) => deepEqual(aObj[key], bObj[key], seen));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
return false;
|
|
332
403
|
}
|
|
@@ -299,13 +299,44 @@ Be thorough but practical - focus on real issues.`,
|
|
|
299
299
|
const params = tool.parameters;
|
|
300
300
|
|
|
301
301
|
if (params.type === 'object' && params.properties) {
|
|
302
|
-
const validInput: Record<string, unknown> = {};
|
|
303
302
|
const properties = params.properties as Record<string, { type?: string; default?: unknown }>;
|
|
303
|
+
const required = Array.isArray(params.required)
|
|
304
|
+
? new Set(params.required as string[])
|
|
305
|
+
: new Set<string>();
|
|
304
306
|
|
|
307
|
+
const validInput: Record<string, unknown> = {};
|
|
305
308
|
for (const [key, schema] of Object.entries(properties)) {
|
|
306
309
|
validInput[key] = this.generateSampleValue(schema.type, schema.default);
|
|
307
310
|
}
|
|
308
311
|
testCases.push({ input: validInput });
|
|
312
|
+
|
|
313
|
+
const emptyInput: Record<string, unknown> = {};
|
|
314
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
315
|
+
if (!required.has(key)) {
|
|
316
|
+
emptyInput[key] = this.generateSampleValue(schema.type, schema.default);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (Object.keys(emptyInput).length > 0) {
|
|
320
|
+
testCases.push({ input: emptyInput });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const edgeInput: Record<string, unknown> = {};
|
|
324
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
325
|
+
edgeInput[key] = this.generateEdgeValue(schema.type);
|
|
326
|
+
}
|
|
327
|
+
testCases.push({ input: edgeInput });
|
|
328
|
+
|
|
329
|
+
if (required.size > 0) {
|
|
330
|
+
const missingRequired: Record<string, unknown> = {};
|
|
331
|
+
for (const [key, schema] of Object.entries(properties)) {
|
|
332
|
+
if (!required.has(key)) {
|
|
333
|
+
missingRequired[key] = this.generateSampleValue(schema.type, schema.default);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
testCases.push({ input: missingRequired, shouldThrow: true });
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
testCases.push({ input: {} });
|
|
309
340
|
}
|
|
310
341
|
|
|
311
342
|
return testCases;
|
|
@@ -331,6 +362,25 @@ Be thorough but practical - focus on real issues.`,
|
|
|
331
362
|
}
|
|
332
363
|
}
|
|
333
364
|
|
|
365
|
+
private generateEdgeValue(type?: string): unknown {
|
|
366
|
+
switch (type) {
|
|
367
|
+
case 'string':
|
|
368
|
+
return '';
|
|
369
|
+
case 'number':
|
|
370
|
+
return 0;
|
|
371
|
+
case 'integer':
|
|
372
|
+
return -1;
|
|
373
|
+
case 'boolean':
|
|
374
|
+
return false;
|
|
375
|
+
case 'array':
|
|
376
|
+
return [null, 0, ''];
|
|
377
|
+
case 'object':
|
|
378
|
+
return { unexpected: true };
|
|
379
|
+
default:
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
334
384
|
private calculateScore(
|
|
335
385
|
securityCount: number,
|
|
336
386
|
logicCount: number,
|
package/src/utils/index.ts
CHANGED
|
@@ -9,8 +9,32 @@ export function extractJson(text: string): string | null {
|
|
|
9
9
|
cleaned = codeBlockMatch[1].trim();
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const objectStart = cleaned.indexOf('{');
|
|
13
|
+
const arrayStart = cleaned.indexOf('[');
|
|
14
|
+
|
|
15
|
+
let start: number;
|
|
16
|
+
let open: string;
|
|
17
|
+
let close: string;
|
|
18
|
+
|
|
19
|
+
if (objectStart === -1 && arrayStart === -1) {
|
|
20
|
+
return null;
|
|
21
|
+
} else if (objectStart === -1) {
|
|
22
|
+
start = arrayStart;
|
|
23
|
+
open = '[';
|
|
24
|
+
close = ']';
|
|
25
|
+
} else if (arrayStart === -1) {
|
|
26
|
+
start = objectStart;
|
|
27
|
+
open = '{';
|
|
28
|
+
close = '}';
|
|
29
|
+
} else if (objectStart < arrayStart) {
|
|
30
|
+
start = objectStart;
|
|
31
|
+
open = '{';
|
|
32
|
+
close = '}';
|
|
33
|
+
} else {
|
|
34
|
+
start = arrayStart;
|
|
35
|
+
open = '[';
|
|
36
|
+
close = ']';
|
|
37
|
+
}
|
|
14
38
|
|
|
15
39
|
let depth = 0;
|
|
16
40
|
let inString = false;
|
|
@@ -36,8 +60,8 @@ export function extractJson(text: string): string | null {
|
|
|
36
60
|
|
|
37
61
|
if (inString) continue;
|
|
38
62
|
|
|
39
|
-
if (ch ===
|
|
40
|
-
else if (ch ===
|
|
63
|
+
if (ch === open) depth++;
|
|
64
|
+
else if (ch === close) {
|
|
41
65
|
depth--;
|
|
42
66
|
if (depth === 0) {
|
|
43
67
|
return cleaned.slice(start, i + 1);
|
|
@@ -48,7 +72,7 @@ export function extractJson(text: string): string | null {
|
|
|
48
72
|
if (depth > 0) {
|
|
49
73
|
let repaired = cleaned.slice(start);
|
|
50
74
|
repaired = repaired.replace(/,\s*$/, '');
|
|
51
|
-
repaired +=
|
|
75
|
+
repaired += close.repeat(depth);
|
|
52
76
|
try {
|
|
53
77
|
JSON.parse(repaired);
|
|
54
78
|
return repaired;
|