@cogitator-ai/self-modifying 17.0.18 → 18.0.0
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/README.md +112 -48
- package/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -1
- package/dist/architecture-evolution/capability-analyzer.js +38 -37
- package/dist/architecture-evolution/capability-analyzer.js.map +1 -1
- package/dist/architecture-evolution/index.d.ts +2 -2
- package/dist/architecture-evolution/index.d.ts.map +1 -1
- package/dist/architecture-evolution/index.js +1 -1
- package/dist/architecture-evolution/index.js.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.d.ts +5 -4
- package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.js +36 -31
- package/dist/architecture-evolution/parameter-optimizer.js.map +1 -1
- package/dist/architecture-evolution/prompts.d.ts +21 -2
- package/dist/architecture-evolution/prompts.d.ts.map +1 -1
- package/dist/architecture-evolution/prompts.js +150 -36
- package/dist/architecture-evolution/prompts.js.map +1 -1
- package/dist/constraints/modification-validator.d.ts +1 -0
- package/dist/constraints/modification-validator.d.ts.map +1 -1
- package/dist/constraints/modification-validator.js +30 -14
- package/dist/constraints/modification-validator.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.d.ts +3 -0
- package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.js +101 -33
- package/dist/meta-reasoning/meta-reasoner.js.map +1 -1
- package/dist/meta-reasoning/prompts.d.ts +10 -14
- package/dist/meta-reasoning/prompts.d.ts.map +1 -1
- package/dist/meta-reasoning/prompts.js +109 -1
- package/dist/meta-reasoning/prompts.js.map +1 -1
- package/dist/self-modifying-agent.d.ts +83 -17
- package/dist/self-modifying-agent.d.ts.map +1 -1
- package/dist/self-modifying-agent.js +557 -444
- package/dist/self-modifying-agent.js.map +1 -1
- package/dist/tool-generation/gap-analyzer.d.ts +0 -1
- package/dist/tool-generation/gap-analyzer.d.ts.map +1 -1
- package/dist/tool-generation/gap-analyzer.js +14 -14
- package/dist/tool-generation/gap-analyzer.js.map +1 -1
- package/dist/tool-generation/index.d.ts +3 -3
- package/dist/tool-generation/index.d.ts.map +1 -1
- package/dist/tool-generation/index.js +2 -2
- package/dist/tool-generation/index.js.map +1 -1
- package/dist/tool-generation/prompts.d.ts +1 -0
- package/dist/tool-generation/prompts.d.ts.map +1 -1
- package/dist/tool-generation/prompts.js +33 -8
- package/dist/tool-generation/prompts.js.map +1 -1
- package/dist/tool-generation/tool-generator.d.ts +5 -5
- package/dist/tool-generation/tool-generator.d.ts.map +1 -1
- package/dist/tool-generation/tool-generator.js +41 -28
- package/dist/tool-generation/tool-generator.js.map +1 -1
- package/dist/tool-generation/tool-sandbox.d.ts +9 -6
- package/dist/tool-generation/tool-sandbox.d.ts.map +1 -1
- package/dist/tool-generation/tool-sandbox.js +257 -157
- package/dist/tool-generation/tool-sandbox.js.map +1 -1
- package/dist/tool-generation/tool-validator.d.ts +3 -8
- package/dist/tool-generation/tool-validator.d.ts.map +1 -1
- package/dist/tool-generation/tool-validator.js +53 -54
- package/dist/tool-generation/tool-validator.js.map +1 -1
- package/dist/utils/llm-helper.d.ts.map +1 -1
- package/dist/utils/llm-helper.js +1 -0
- package/dist/utils/llm-helper.js.map +1 -1
- package/package.json +3 -12
- package/src/__tests__/component-regressions.test.ts +599 -0
- package/src/__tests__/meta-reasoning.test.ts +5 -1
- package/src/__tests__/sandbox-security.test.ts +186 -0
- package/src/__tests__/self-modifying-agent.test.ts +363 -1
- package/src/architecture-evolution/capability-analyzer.ts +56 -40
- package/src/architecture-evolution/index.ts +2 -0
- package/src/architecture-evolution/parameter-optimizer.ts +52 -34
- package/src/architecture-evolution/prompts.ts +179 -41
- package/src/constraints/modification-validator.ts +30 -14
- package/src/index.ts +14 -0
- package/src/meta-reasoning/meta-reasoner.ts +105 -40
- package/src/meta-reasoning/prompts.ts +129 -15
- package/src/self-modifying-agent.ts +764 -489
- package/src/tool-generation/gap-analyzer.ts +19 -18
- package/src/tool-generation/index.ts +13 -3
- package/src/tool-generation/prompts.ts +41 -8
- package/src/tool-generation/tool-generator.ts +59 -33
- package/src/tool-generation/tool-sandbox.ts +306 -175
- package/src/tool-generation/tool-validator.ts +66 -63
- package/src/utils/llm-helper.ts +1 -0
|
@@ -9,42 +9,141 @@ export const DEFAULT_SANDBOX_CONFIG: ToolSandboxConfig = {
|
|
|
9
9
|
isolationLevel: 'strict',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const WORKER_SOURCE =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
12
|
+
const WORKER_SOURCE = String.raw`
|
|
13
|
+
const { parentPort, workerData } = require('node:worker_threads');
|
|
14
|
+
const vm = require('node:vm');
|
|
15
|
+
const { code, paramsJson, timeout } = workerData;
|
|
16
|
+
|
|
17
|
+
const BOOTSTRAP = [
|
|
18
|
+
'(function () {',
|
|
19
|
+
' "use strict";',
|
|
20
|
+
' const logs = [];',
|
|
21
|
+
' const format = (args) => args.map((v) => {',
|
|
22
|
+
' if (typeof v === "string") return v;',
|
|
23
|
+
' try { const s = JSON.stringify(v); return s === undefined ? String(v) : s; } catch (e) { return String(v); }',
|
|
24
|
+
' }).join(" ");',
|
|
25
|
+
' const logger = (level) => (...args) => { if (logs.length < 1000) logs.push("[" + level + "] " + format(args)); };',
|
|
26
|
+
' Object.defineProperty(globalThis, "console", { value: Object.freeze({',
|
|
27
|
+
' log: logger("LOG"), info: logger("INFO"), debug: logger("DEBUG"), warn: logger("WARN"), error: logger("ERROR"),',
|
|
28
|
+
' }) });',
|
|
29
|
+
' Object.defineProperty(globalThis, "__sandboxState", { value: { status: "pending", payload: "", hasValue: false, logs } });',
|
|
30
|
+
'})();',
|
|
31
|
+
].join('\n');
|
|
32
|
+
|
|
33
|
+
const runner = [
|
|
34
|
+
'(async function sandboxedExecution(params) {',
|
|
35
|
+
code,
|
|
36
|
+
' if (typeof execute !== "function") { throw new Error("Implementation must define an execute function"); }',
|
|
37
|
+
' return await execute(params);',
|
|
38
|
+
'})(JSON.parse(' + JSON.stringify(paramsJson) + ')).then(',
|
|
39
|
+
' (value) => {',
|
|
40
|
+
' const state = globalThis.__sandboxState;',
|
|
41
|
+
' try {',
|
|
42
|
+
' const json = JSON.stringify(value);',
|
|
43
|
+
' state.hasValue = json !== undefined;',
|
|
44
|
+
' state.payload = json === undefined ? "" : json;',
|
|
45
|
+
' state.status = "fulfilled";',
|
|
46
|
+
' } catch (e) {',
|
|
47
|
+
' state.payload = "Tool returned a non-serializable value: " + (e && e.message);',
|
|
48
|
+
' state.status = "failed";',
|
|
49
|
+
' }',
|
|
23
50
|
' },',
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
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
|
-
'}',
|
|
51
|
+
' (err) => {',
|
|
52
|
+
' const state = globalThis.__sandboxState;',
|
|
53
|
+
' state.payload = err && typeof err.message === "string" ? err.message : String(err);',
|
|
54
|
+
' state.status = "rejected";',
|
|
55
|
+
' }',
|
|
56
|
+
');',
|
|
46
57
|
].join('\n');
|
|
47
58
|
|
|
59
|
+
const asString = (value) => (typeof value === 'string' ? value : '');
|
|
60
|
+
const readState = (context, expression) => {
|
|
61
|
+
try {
|
|
62
|
+
return vm.runInContext(expression, context);
|
|
63
|
+
} catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const readLogs = (context) => {
|
|
68
|
+
const raw = readState(context, 'JSON.stringify(__sandboxState.logs)');
|
|
69
|
+
if (typeof raw !== 'string') return [];
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(raw);
|
|
72
|
+
return Array.isArray(parsed) ? parsed.filter((l) => typeof l === 'string') : [];
|
|
73
|
+
} catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const context = vm.createContext(Object.create(null), {
|
|
79
|
+
codeGeneration: { strings: false, wasm: false },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
let started = false;
|
|
83
|
+
try {
|
|
84
|
+
vm.runInContext(BOOTSTRAP, context);
|
|
85
|
+
vm.runInContext(runner, context, { timeout });
|
|
86
|
+
started = true;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
parentPort.postMessage({
|
|
89
|
+
success: false,
|
|
90
|
+
thrown: true,
|
|
91
|
+
error: err && typeof err.message === 'string' ? err.message : String(err),
|
|
92
|
+
logs: readLogs(context),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (started) setImmediate(() => {
|
|
97
|
+
const status = asString(readState(context, '__sandboxState.status'));
|
|
98
|
+
const payload = asString(readState(context, '__sandboxState.payload'));
|
|
99
|
+
const logs = readLogs(context);
|
|
100
|
+
|
|
101
|
+
if (status === 'fulfilled') {
|
|
102
|
+
let result;
|
|
103
|
+
if (readState(context, '__sandboxState.hasValue') === true) {
|
|
104
|
+
try {
|
|
105
|
+
result = JSON.parse(payload);
|
|
106
|
+
} catch {
|
|
107
|
+
parentPort.postMessage({ success: false, thrown: false, error: 'Tool returned an unreadable value', logs });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
parentPort.postMessage({ success: true, result, logs });
|
|
112
|
+
} else if (status === 'rejected') {
|
|
113
|
+
parentPort.postMessage({ success: false, thrown: true, error: payload, logs });
|
|
114
|
+
} else if (status === 'failed') {
|
|
115
|
+
parentPort.postMessage({ success: false, thrown: false, error: payload, logs });
|
|
116
|
+
} else {
|
|
117
|
+
parentPort.postMessage({
|
|
118
|
+
success: false,
|
|
119
|
+
thrown: false,
|
|
120
|
+
error: 'Tool execution never settled: execute() returned a promise that cannot resolve inside the sandbox',
|
|
121
|
+
logs,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
interface WorkerMessage {
|
|
128
|
+
success: boolean;
|
|
129
|
+
result?: unknown;
|
|
130
|
+
error?: string;
|
|
131
|
+
thrown?: boolean;
|
|
132
|
+
logs?: string[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface SandboxRun {
|
|
136
|
+
result: ToolSandboxResult;
|
|
137
|
+
thrownByTool: boolean;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface SandboxTestCase {
|
|
141
|
+
input: unknown;
|
|
142
|
+
expectedOutput?: unknown;
|
|
143
|
+
shouldThrow?: boolean;
|
|
144
|
+
allowThrow?: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
48
147
|
export class ToolSandbox {
|
|
49
148
|
private readonly config: ToolSandboxConfig;
|
|
50
149
|
|
|
@@ -53,30 +152,13 @@ export class ToolSandbox {
|
|
|
53
152
|
}
|
|
54
153
|
|
|
55
154
|
async execute(tool: GeneratedTool, params: unknown): Promise<ToolSandboxResult> {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
this.validateImplementation(tool.implementation);
|
|
60
|
-
} catch (error) {
|
|
61
|
-
return {
|
|
62
|
-
success: false,
|
|
63
|
-
error: error instanceof Error ? error.message : String(error),
|
|
64
|
-
executionTime: Date.now() - startTime,
|
|
65
|
-
memoryUsed: 0,
|
|
66
|
-
logs: [],
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!this.config.enabled) {
|
|
71
|
-
return this.executeUnsandboxed(tool, params, startTime);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return this.executeInWorker(tool.implementation, params, startTime);
|
|
155
|
+
const run = await this.run(tool, params);
|
|
156
|
+
return run.result;
|
|
75
157
|
}
|
|
76
158
|
|
|
77
159
|
async testWithCases(
|
|
78
160
|
tool: GeneratedTool,
|
|
79
|
-
testCases:
|
|
161
|
+
testCases: SandboxTestCase[]
|
|
80
162
|
): Promise<{
|
|
81
163
|
passed: number;
|
|
82
164
|
failed: number;
|
|
@@ -97,13 +179,15 @@ export class ToolSandbox {
|
|
|
97
179
|
}> = [];
|
|
98
180
|
|
|
99
181
|
for (const testCase of testCases) {
|
|
100
|
-
const execResult = await this.
|
|
182
|
+
const { result: execResult, thrownByTool } = await this.run(tool, testCase.input);
|
|
101
183
|
|
|
102
|
-
let passed
|
|
184
|
+
let passed: boolean;
|
|
103
185
|
if (testCase.shouldThrow) {
|
|
104
|
-
passed = !execResult.success;
|
|
186
|
+
passed = !execResult.success && thrownByTool;
|
|
105
187
|
} else if (testCase.expectedOutput !== undefined) {
|
|
106
188
|
passed = execResult.success && deepEqual(execResult.result, testCase.expectedOutput);
|
|
189
|
+
} else if (testCase.allowThrow) {
|
|
190
|
+
passed = execResult.success || thrownByTool;
|
|
107
191
|
} else {
|
|
108
192
|
passed = execResult.success;
|
|
109
193
|
}
|
|
@@ -124,6 +208,38 @@ export class ToolSandbox {
|
|
|
124
208
|
};
|
|
125
209
|
}
|
|
126
210
|
|
|
211
|
+
private async run(tool: GeneratedTool, params: unknown): Promise<SandboxRun> {
|
|
212
|
+
const startTime = Date.now();
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
this.validateImplementation(tool.implementation);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
result: failure(error instanceof Error ? error.message : String(error), startTime),
|
|
219
|
+
thrownByTool: false,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let paramsJson: string;
|
|
224
|
+
try {
|
|
225
|
+
paramsJson = JSON.stringify(params ?? null);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return {
|
|
228
|
+
result: failure(
|
|
229
|
+
`Parameters must be JSON-serializable: ${error instanceof Error ? error.message : String(error)}`,
|
|
230
|
+
startTime
|
|
231
|
+
),
|
|
232
|
+
thrownByTool: false,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!this.config.enabled) {
|
|
237
|
+
return this.executeUnsandboxed(tool, JSON.parse(paramsJson) as unknown, startTime);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return this.executeInWorker(tool.implementation, paramsJson, startTime);
|
|
241
|
+
}
|
|
242
|
+
|
|
127
243
|
private validateImplementation(code: string): void {
|
|
128
244
|
const forbidden: Array<{ pattern: RegExp; label: string }> = [
|
|
129
245
|
{ pattern: /\beval\s*\(/, label: 'eval()' },
|
|
@@ -196,102 +312,84 @@ export class ToolSandbox {
|
|
|
196
312
|
}
|
|
197
313
|
}
|
|
198
314
|
|
|
199
|
-
private
|
|
315
|
+
private executeInWorker(
|
|
200
316
|
code: string,
|
|
201
|
-
|
|
317
|
+
paramsJson: string,
|
|
202
318
|
startTime: number
|
|
203
|
-
): Promise<
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
serializableParams = JSON.parse(JSON.stringify(params ?? null));
|
|
207
|
-
} catch {
|
|
208
|
-
serializableParams = null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return new Promise<ToolSandboxResult>((resolve) => {
|
|
319
|
+
): Promise<SandboxRun> {
|
|
320
|
+
return new Promise<SandboxRun>((resolve) => {
|
|
212
321
|
let settled = false;
|
|
322
|
+
let worker: Worker | undefined;
|
|
213
323
|
|
|
214
|
-
const settle = (result: ToolSandboxResult) => {
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
324
|
+
const settle = (result: ToolSandboxResult, thrownByTool = false) => {
|
|
325
|
+
if (settled) return;
|
|
326
|
+
settled = true;
|
|
327
|
+
clearTimeout(timer);
|
|
328
|
+
if (worker) void worker.terminate();
|
|
329
|
+
resolve({ result, thrownByTool });
|
|
219
330
|
};
|
|
220
331
|
|
|
332
|
+
const timer = setTimeout(() => {
|
|
333
|
+
settle(failure(`Execution timeout: exceeded ${this.config.maxExecutionTime}ms`, startTime));
|
|
334
|
+
}, this.config.maxExecutionTime);
|
|
335
|
+
|
|
221
336
|
const maxMemoryMb = Math.max(4, Math.ceil(this.config.maxMemory / (1024 * 1024)));
|
|
222
337
|
|
|
223
|
-
let worker: Worker;
|
|
224
338
|
try {
|
|
225
339
|
worker = new Worker(WORKER_SOURCE, {
|
|
226
340
|
eval: true,
|
|
227
|
-
|
|
341
|
+
env: {},
|
|
342
|
+
workerData: { code, paramsJson, timeout: this.config.maxExecutionTime },
|
|
228
343
|
resourceLimits: {
|
|
229
344
|
maxOldGenerationSizeMb: maxMemoryMb,
|
|
230
345
|
maxYoungGenerationSizeMb: Math.max(2, Math.ceil(maxMemoryMb / 4)),
|
|
231
346
|
},
|
|
232
347
|
});
|
|
233
348
|
} catch (error) {
|
|
234
|
-
settle(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
349
|
+
settle(
|
|
350
|
+
failure(
|
|
351
|
+
`Failed to create sandbox worker: ${error instanceof Error ? error.message : String(error)}`,
|
|
352
|
+
startTime
|
|
353
|
+
)
|
|
354
|
+
);
|
|
241
355
|
return;
|
|
242
356
|
}
|
|
243
357
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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);
|
|
358
|
+
worker.on('message', (msg: WorkerMessage) => {
|
|
359
|
+
const logs = msg.logs ?? [];
|
|
360
|
+
if (msg.success) {
|
|
259
361
|
settle({
|
|
260
|
-
success:
|
|
362
|
+
success: true,
|
|
261
363
|
result: msg.result,
|
|
262
|
-
error: msg.error,
|
|
263
364
|
executionTime: Date.now() - startTime,
|
|
264
365
|
memoryUsed: estimateMemoryUsage(msg.result),
|
|
265
|
-
logs
|
|
366
|
+
logs,
|
|
266
367
|
});
|
|
368
|
+
} else {
|
|
369
|
+
settle({ ...failure(msg.error ?? 'Tool execution failed', startTime), logs }, msg.thrown);
|
|
267
370
|
}
|
|
268
|
-
);
|
|
371
|
+
});
|
|
269
372
|
|
|
270
|
-
worker.on('error', (error: Error) => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
373
|
+
worker.on('error', (error: Error & { code?: string }) => {
|
|
374
|
+
settle(
|
|
375
|
+
failure(
|
|
376
|
+
error.code === 'ERR_WORKER_OUT_OF_MEMORY'
|
|
377
|
+
? `Execution exceeded memory limit (${this.config.maxMemory} bytes)`
|
|
378
|
+
: `Sandbox worker error: ${error.message}`,
|
|
379
|
+
startTime
|
|
380
|
+
)
|
|
381
|
+
);
|
|
279
382
|
});
|
|
280
383
|
|
|
281
384
|
worker.on('exit', (exitCode: number) => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
executionTime: Date.now() - startTime,
|
|
291
|
-
memoryUsed: 0,
|
|
292
|
-
logs: [],
|
|
293
|
-
});
|
|
294
|
-
}
|
|
385
|
+
settle(
|
|
386
|
+
failure(
|
|
387
|
+
exitCode === 134 || exitCode === 137
|
|
388
|
+
? `Execution exceeded memory limit (${this.config.maxMemory} bytes)`
|
|
389
|
+
: `Sandbox worker exited with code ${exitCode} before reporting a result`,
|
|
390
|
+
startTime
|
|
391
|
+
)
|
|
392
|
+
);
|
|
295
393
|
});
|
|
296
394
|
});
|
|
297
395
|
}
|
|
@@ -300,46 +398,72 @@ export class ToolSandbox {
|
|
|
300
398
|
tool: GeneratedTool,
|
|
301
399
|
params: unknown,
|
|
302
400
|
startTime: number
|
|
303
|
-
): Promise<
|
|
401
|
+
): Promise<SandboxRun> {
|
|
402
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
304
403
|
try {
|
|
305
404
|
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
306
405
|
const factory = new Function(`
|
|
307
406
|
"use strict";
|
|
308
407
|
${tool.implementation}
|
|
309
408
|
return execute;
|
|
310
|
-
`);
|
|
409
|
+
`) as () => unknown;
|
|
311
410
|
const execute = factory();
|
|
411
|
+
if (typeof execute !== 'function') {
|
|
412
|
+
return {
|
|
413
|
+
result: failure('Implementation must define an execute function', startTime),
|
|
414
|
+
thrownByTool: false,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
312
417
|
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
418
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
419
|
+
timer = setTimeout(
|
|
420
|
+
() => reject(new SandboxTimeoutError(this.config.maxExecutionTime)),
|
|
421
|
+
this.config.maxExecutionTime
|
|
422
|
+
);
|
|
423
|
+
});
|
|
424
|
+
const result: unknown = await Promise.race([
|
|
425
|
+
(execute as (p: unknown) => unknown)(params),
|
|
426
|
+
timeout,
|
|
322
427
|
]);
|
|
323
428
|
|
|
324
429
|
return {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
430
|
+
result: {
|
|
431
|
+
success: true,
|
|
432
|
+
result,
|
|
433
|
+
executionTime: Date.now() - startTime,
|
|
434
|
+
memoryUsed: estimateMemoryUsage(result),
|
|
435
|
+
logs: [],
|
|
436
|
+
},
|
|
437
|
+
thrownByTool: false,
|
|
330
438
|
};
|
|
331
439
|
} catch (error) {
|
|
332
440
|
return {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
executionTime: Date.now() - startTime,
|
|
336
|
-
memoryUsed: 0,
|
|
337
|
-
logs: [],
|
|
441
|
+
result: failure(error instanceof Error ? error.message : String(error), startTime),
|
|
442
|
+
thrownByTool: !(error instanceof SandboxTimeoutError),
|
|
338
443
|
};
|
|
444
|
+
} finally {
|
|
445
|
+
clearTimeout(timer);
|
|
339
446
|
}
|
|
340
447
|
}
|
|
341
448
|
}
|
|
342
449
|
|
|
450
|
+
class SandboxTimeoutError extends Error {
|
|
451
|
+
constructor(limit: number) {
|
|
452
|
+
super(`Execution timeout: exceeded ${limit}ms`);
|
|
453
|
+
this.name = 'SandboxTimeoutError';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function failure(error: string, startTime: number): ToolSandboxResult {
|
|
458
|
+
return {
|
|
459
|
+
success: false,
|
|
460
|
+
error,
|
|
461
|
+
executionTime: Date.now() - startTime,
|
|
462
|
+
memoryUsed: 0,
|
|
463
|
+
logs: [],
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
343
467
|
function estimateMemoryUsage(value: unknown): number {
|
|
344
468
|
try {
|
|
345
469
|
const str = JSON.stringify(value);
|
|
@@ -349,55 +473,62 @@ function estimateMemoryUsage(value: unknown): number {
|
|
|
349
473
|
}
|
|
350
474
|
}
|
|
351
475
|
|
|
352
|
-
export function deepEqual(a: unknown, b: unknown
|
|
476
|
+
export function deepEqual(a: unknown, b: unknown): boolean {
|
|
477
|
+
return deepEqualInner(a, b, new Map<object, Set<object>>());
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function deepEqualInner(a: unknown, b: unknown, visited: Map<object, Set<object>>): boolean {
|
|
353
481
|
if (Object.is(a, b)) return true;
|
|
482
|
+
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) return false;
|
|
354
483
|
|
|
355
|
-
|
|
356
|
-
if (
|
|
484
|
+
const pairs = visited.get(a);
|
|
485
|
+
if (pairs?.has(b)) return true;
|
|
486
|
+
if (pairs) pairs.add(b);
|
|
487
|
+
else visited.set(a, new Set([b]));
|
|
357
488
|
|
|
358
|
-
if (a instanceof Date
|
|
359
|
-
return a.getTime() === b.getTime();
|
|
489
|
+
if (a instanceof Date || b instanceof Date) {
|
|
490
|
+
return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
|
|
360
491
|
}
|
|
361
492
|
|
|
362
|
-
if (a instanceof RegExp
|
|
363
|
-
return
|
|
493
|
+
if (a instanceof RegExp || b instanceof RegExp) {
|
|
494
|
+
return (
|
|
495
|
+
a instanceof RegExp && b instanceof RegExp && a.source === b.source && a.flags === b.flags
|
|
496
|
+
);
|
|
364
497
|
}
|
|
365
498
|
|
|
366
|
-
if (a instanceof Map
|
|
367
|
-
if (a.size !== b.size) return false;
|
|
499
|
+
if (a instanceof Map || b instanceof Map) {
|
|
500
|
+
if (!(a instanceof Map && b instanceof Map) || a.size !== b.size) return false;
|
|
368
501
|
for (const [key, val] of a) {
|
|
369
|
-
if (!b.has(key) || !
|
|
502
|
+
if (!b.has(key) || !deepEqualInner(val, b.get(key), visited)) return false;
|
|
370
503
|
}
|
|
371
504
|
return true;
|
|
372
505
|
}
|
|
373
506
|
|
|
374
|
-
if (a instanceof Set
|
|
375
|
-
if (a.size !== b.size) return false;
|
|
507
|
+
if (a instanceof Set || b instanceof Set) {
|
|
508
|
+
if (!(a instanceof Set && b instanceof Set) || a.size !== b.size) return false;
|
|
509
|
+
const unmatched = [...b];
|
|
376
510
|
for (const val of a) {
|
|
377
|
-
|
|
511
|
+
const idx = unmatched.findIndex((candidate) => deepEqualInner(val, candidate, visited));
|
|
512
|
+
if (idx === -1) return false;
|
|
513
|
+
unmatched.splice(idx, 1);
|
|
378
514
|
}
|
|
379
515
|
return true;
|
|
380
516
|
}
|
|
381
517
|
|
|
382
|
-
if (Array.isArray(a)
|
|
383
|
-
if (a.length !== b.length) return false;
|
|
384
|
-
return a.every((val, i) =>
|
|
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));
|
|
518
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
519
|
+
if (!(Array.isArray(a) && Array.isArray(b)) || a.length !== b.length) return false;
|
|
520
|
+
return a.every((val, i) => deepEqualInner(val, b[i], visited));
|
|
400
521
|
}
|
|
401
522
|
|
|
402
|
-
|
|
523
|
+
const aObj = a as Record<string, unknown>;
|
|
524
|
+
const bObj = b as Record<string, unknown>;
|
|
525
|
+
const keysA = Object.keys(aObj).filter((k) => aObj[k] !== undefined);
|
|
526
|
+
const keysB = Object.keys(bObj).filter((k) => bObj[k] !== undefined);
|
|
527
|
+
|
|
528
|
+
if (keysA.length !== keysB.length) return false;
|
|
529
|
+
return keysA.every(
|
|
530
|
+
(key) =>
|
|
531
|
+
Object.prototype.hasOwnProperty.call(bObj, key) &&
|
|
532
|
+
deepEqualInner(aObj[key], bObj[key], visited)
|
|
533
|
+
);
|
|
403
534
|
}
|