@langchain/quickjs 0.2.6 → 0.4.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/dist/index.cjs +149 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -10
- package/dist/index.d.ts +52 -10
- package/dist/index.js +146 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { StructuredToolInterface } from "@langchain/core/tools";
|
|
|
6
6
|
|
|
7
7
|
//#region src/types.d.ts
|
|
8
8
|
/**
|
|
9
|
-
* Configuration options for the
|
|
9
|
+
* Configuration options for the Code Interpreter middleware.
|
|
10
10
|
*/
|
|
11
|
-
interface
|
|
11
|
+
interface CodeInterpreterMiddlewareOptions {
|
|
12
12
|
/**
|
|
13
13
|
* Enable programmatic tool calling from within the REPL.
|
|
14
14
|
*
|
|
@@ -20,7 +20,7 @@ interface QuickJSMiddlewareOptions {
|
|
|
20
20
|
ptc?: (string | StructuredToolInterface)[];
|
|
21
21
|
/**
|
|
22
22
|
* Memory limit in bytes.
|
|
23
|
-
* @default
|
|
23
|
+
* @default 67108864 (64MB)
|
|
24
24
|
*/
|
|
25
25
|
memoryLimitBytes?: number;
|
|
26
26
|
/**
|
|
@@ -31,7 +31,7 @@ interface QuickJSMiddlewareOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* Execution timeout in milliseconds per evaluation.
|
|
33
33
|
* Set to a negative value to disable the timeout entirely.
|
|
34
|
-
* @default
|
|
34
|
+
* @default 5000 (5s)
|
|
35
35
|
*/
|
|
36
36
|
executionTimeoutMs?: number;
|
|
37
37
|
/**
|
|
@@ -66,6 +66,18 @@ interface QuickJSMiddlewareOptions {
|
|
|
66
66
|
* @default 4000
|
|
67
67
|
*/
|
|
68
68
|
maxResultChars?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Name of the tool exposed to the model.
|
|
71
|
+
* @default "eval"
|
|
72
|
+
*/
|
|
73
|
+
toolName?: string;
|
|
74
|
+
/**
|
|
75
|
+
* If true, install a `console` object that buffers `console.log/warn/error`
|
|
76
|
+
* calls and emits them alongside the result. If false, console output is
|
|
77
|
+
* silently discarded.
|
|
78
|
+
* @default true
|
|
79
|
+
*/
|
|
80
|
+
captureConsole?: boolean;
|
|
69
81
|
}
|
|
70
82
|
/**
|
|
71
83
|
* Options for creating a ReplSession.
|
|
@@ -77,6 +89,7 @@ interface ReplSessionOptions {
|
|
|
77
89
|
skillsEnabled?: boolean;
|
|
78
90
|
maxPtcCalls?: number | null;
|
|
79
91
|
maxResultChars?: number;
|
|
92
|
+
captureConsole?: boolean;
|
|
80
93
|
}
|
|
81
94
|
/**
|
|
82
95
|
* Result of a single REPL evaluation.
|
|
@@ -108,15 +121,15 @@ interface SkillsContext {
|
|
|
108
121
|
//#endregion
|
|
109
122
|
//#region src/middleware.d.ts
|
|
110
123
|
/**
|
|
111
|
-
* Create the
|
|
124
|
+
* Create the Code Interpreter middleware.
|
|
112
125
|
*/
|
|
113
|
-
declare function
|
|
126
|
+
declare function createCodeInterpreterMiddleware(options?: CodeInterpreterMiddlewareOptions): AgentMiddleware<undefined, undefined, unknown, readonly [_$langchain.DynamicStructuredTool<z.ZodObject<{
|
|
114
127
|
code: z.ZodString;
|
|
115
128
|
}, z.core.$strip>, {
|
|
116
129
|
code: string;
|
|
117
130
|
}, {
|
|
118
131
|
code: string;
|
|
119
|
-
}, string, unknown,
|
|
132
|
+
}, string, unknown, string>]>;
|
|
120
133
|
//#endregion
|
|
121
134
|
//#region src/errors.d.ts
|
|
122
135
|
/**
|
|
@@ -149,7 +162,7 @@ declare class PTCCallBudgetExceededError extends Error {
|
|
|
149
162
|
//#region src/session.d.ts
|
|
150
163
|
declare const DEFAULT_MEMORY_LIMIT: number;
|
|
151
164
|
declare const DEFAULT_MAX_STACK_SIZE: number;
|
|
152
|
-
declare const DEFAULT_EXECUTION_TIMEOUT =
|
|
165
|
+
declare const DEFAULT_EXECUTION_TIMEOUT = 5000;
|
|
153
166
|
declare const DEFAULT_MAX_PTC_CALLS = 256;
|
|
154
167
|
/**
|
|
155
168
|
* Sandboxed JavaScript REPL session backed by QuickJS WASM.
|
|
@@ -171,12 +184,35 @@ declare class ReplSession {
|
|
|
171
184
|
private skillsFailed;
|
|
172
185
|
private readonly maxPtcCalls;
|
|
173
186
|
private ptcCallsRemaining;
|
|
187
|
+
/**
|
|
188
|
+
* Reset the shared WASM module. Forces the next session to instantiate
|
|
189
|
+
* a fresh module. Only needed in tests where module state must be
|
|
190
|
+
* isolated between test files.
|
|
191
|
+
*
|
|
192
|
+
* @internal
|
|
193
|
+
*/
|
|
194
|
+
static resetSharedModule(): void;
|
|
174
195
|
constructor(id: string, options?: ReplSessionOptions);
|
|
175
196
|
private ensureStarted;
|
|
176
197
|
/**
|
|
177
198
|
* Load the skill into cache on first access and replay cached errors.
|
|
178
199
|
*/
|
|
179
200
|
private ensureSkillLoaded;
|
|
201
|
+
/**
|
|
202
|
+
* Pre-load all skills referenced in source code into the in-memory
|
|
203
|
+
* cache. Must be called before `evalCodeAsync` so the module loader
|
|
204
|
+
* can resolve synchronously. An async loader would cause asyncify
|
|
205
|
+
* suspensions on each import, which is incompatible with the shared
|
|
206
|
+
* WASM module used by all sessions.
|
|
207
|
+
*/
|
|
208
|
+
preloadReferencedSkills(code: string): Promise<void>;
|
|
209
|
+
/**
|
|
210
|
+
* Resolve a module specifier to source code. Strictly synchronous —
|
|
211
|
+
* only reads from the in-memory skill cache populated by
|
|
212
|
+
* `preloadReferencedSkills`. Returns error source (not a thrown
|
|
213
|
+
* exception) for missing or failed skills so QuickJS reports the
|
|
214
|
+
* error inside the VM.
|
|
215
|
+
*/
|
|
180
216
|
private resolveSpecifier;
|
|
181
217
|
/**
|
|
182
218
|
* Canonicalize an `import` specifier. Bare specifiers pass through;
|
|
@@ -186,6 +222,12 @@ declare class ReplSession {
|
|
|
186
222
|
private normalizeSpecifier;
|
|
187
223
|
/**
|
|
188
224
|
* Wire the QuickJS module loader and normalizer on this session's runtime.
|
|
225
|
+
*
|
|
226
|
+
* The loader is strictly synchronous — it reads from the in-memory skill
|
|
227
|
+
* cache populated by `preloadReferencedSkills`. This is critical: an async
|
|
228
|
+
* module loader causes asyncify suspensions on each import, and disposing
|
|
229
|
+
* a runtime after multi-file imports corrupts the shared module's asyncify
|
|
230
|
+
* state, silently breaking the loader for all subsequent sessions.
|
|
189
231
|
*/
|
|
190
232
|
private installModuleLoader;
|
|
191
233
|
/**
|
|
@@ -221,7 +263,7 @@ declare class ReplSession {
|
|
|
221
263
|
static deleteSession(key: string): void;
|
|
222
264
|
/**
|
|
223
265
|
* Push the current skills metadata + backend into the session.
|
|
224
|
-
* Called by the middleware once per `
|
|
266
|
+
* Called by the middleware once per `eval` invocation, before eval runs.
|
|
225
267
|
* Pass `undefined` to clear the context (no skill imports will resolve).
|
|
226
268
|
*/
|
|
227
269
|
setSkillsContext(ctx?: SkillsContext): void;
|
|
@@ -343,5 +385,5 @@ declare function loadSkill(metadata: SkillMetadata, backend: AnyBackendProtocol)
|
|
|
343
385
|
*/
|
|
344
386
|
declare function scanSkillReferences(source: string): Set<string>;
|
|
345
387
|
//#endregion
|
|
346
|
-
export { DEFAULT_EXECUTION_TIMEOUT, DEFAULT_MAX_PTC_CALLS, DEFAULT_MAX_STACK_SIZE, DEFAULT_MEMORY_LIMIT, type LoadedSkill, MAX_SKILL_BUNDLE_BYTES, PTCCallBudgetExceededError, type
|
|
388
|
+
export { type CodeInterpreterMiddlewareOptions, DEFAULT_EXECUTION_TIMEOUT, DEFAULT_MAX_PTC_CALLS, DEFAULT_MAX_STACK_SIZE, DEFAULT_MEMORY_LIMIT, type LoadedSkill, MAX_SKILL_BUNDLE_BYTES, PTCCallBudgetExceededError, type ReplResult, ReplSession, type ReplSessionOptions, SKILL_MODULE_EXTENSIONS, createCodeInterpreterMiddleware, formatReplResult, formatSkillNotAvailable, loadSkill, scanSkillReferences, stripTypeSyntax, toCamelCase, transformForEval };
|
|
347
389
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -427,6 +427,38 @@ function formatSkillNotAvailable(missing) {
|
|
|
427
427
|
return `Skills unavailable: ${[...missing].sort().join(", ")}`;
|
|
428
428
|
}
|
|
429
429
|
//#endregion
|
|
430
|
+
//#region src/eval-queue.ts
|
|
431
|
+
/**
|
|
432
|
+
* Serializes async operations on a shared WASM module.
|
|
433
|
+
*
|
|
434
|
+
* The quickjs-emscripten asyncify variant allows only one concurrent
|
|
435
|
+
* async call per module instance. This queue enforces that constraint
|
|
436
|
+
* by chaining operations into a promise queue — each caller waits for
|
|
437
|
+
* the previous one to finish before executing.
|
|
438
|
+
*/
|
|
439
|
+
var AsyncEvalQueue = class {
|
|
440
|
+
tail = Promise.resolve();
|
|
441
|
+
/**
|
|
442
|
+
* Enqueue an async operation. The operation will not start until all
|
|
443
|
+
* previously enqueued operations have completed.
|
|
444
|
+
*/
|
|
445
|
+
async enqueue(fn) {
|
|
446
|
+
let release;
|
|
447
|
+
const gate = new Promise((r) => {
|
|
448
|
+
release = r;
|
|
449
|
+
});
|
|
450
|
+
const prev = this.tail;
|
|
451
|
+
this.tail = gate;
|
|
452
|
+
return prev.then(async () => {
|
|
453
|
+
try {
|
|
454
|
+
return await fn();
|
|
455
|
+
} finally {
|
|
456
|
+
release();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
//#endregion
|
|
430
462
|
//#region src/session.ts
|
|
431
463
|
/**
|
|
432
464
|
* Core REPL engine built on quickjs-emscripten (asyncify variant).
|
|
@@ -446,15 +478,41 @@ function formatSkillNotAvailable(missing) {
|
|
|
446
478
|
* runtime is lazily started on the first `.eval()` call, making the session
|
|
447
479
|
* safe across graph interrupts and checkpointing.
|
|
448
480
|
*/
|
|
449
|
-
const DEFAULT_MEMORY_LIMIT =
|
|
481
|
+
const DEFAULT_MEMORY_LIMIT = 64 * 1024 * 1024;
|
|
450
482
|
const DEFAULT_MAX_STACK_SIZE = 320 * 1024;
|
|
451
|
-
const DEFAULT_EXECUTION_TIMEOUT =
|
|
483
|
+
const DEFAULT_EXECUTION_TIMEOUT = 5e3;
|
|
452
484
|
const DEFAULT_MAX_PTC_CALLS = 256;
|
|
453
485
|
const DEFAULT_MAX_RESULTS_CHARS = 4e3;
|
|
454
486
|
const variantImport = import("@jitl/quickjs-ng-wasmfile-release-asyncify");
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Process-global eval queue. Serializes all evalCodeAsync calls across
|
|
489
|
+
* sessions to enforce the asyncify one-at-a-time constraint.
|
|
490
|
+
*/
|
|
491
|
+
const sharedEvalQueue = new AsyncEvalQueue();
|
|
492
|
+
/**
|
|
493
|
+
* Process-global WASM module shared by all sessions.
|
|
494
|
+
*
|
|
495
|
+
* Each session creates its own runtime and context on this module,
|
|
496
|
+
* providing full isolation for globals, heap, and stack. The module
|
|
497
|
+
* itself is stateless between runtimes — only the compiled WASM code
|
|
498
|
+
* and Emscripten infrastructure are shared.
|
|
499
|
+
*
|
|
500
|
+
* This is safe because:
|
|
501
|
+
* - The module loader is synchronous (preloaded skill cache), so
|
|
502
|
+
* imports don't cause asyncify suspensions.
|
|
503
|
+
* - Tool injection uses the promise-based pattern (newFunction +
|
|
504
|
+
* newPromise), not newAsyncifiedFunction, so tool calls don't
|
|
505
|
+
* cause asyncify suspensions.
|
|
506
|
+
* - The eval queue serializes evalCodeAsync calls to satisfy the
|
|
507
|
+
* one-concurrent-async-call-per-module constraint.
|
|
508
|
+
*/
|
|
509
|
+
let sharedModulePromise;
|
|
510
|
+
function getSharedModule() {
|
|
511
|
+
if (!sharedModulePromise) sharedModulePromise = (async () => {
|
|
512
|
+
const variant = await variantImport;
|
|
513
|
+
return newQuickJSAsyncWASMModuleFromVariant(variant.default ?? variant);
|
|
514
|
+
})();
|
|
515
|
+
return sharedModulePromise;
|
|
458
516
|
}
|
|
459
517
|
function makeErrorSource(message) {
|
|
460
518
|
return `throw { name: "Error", message: ${JSON.stringify(message)} };`;
|
|
@@ -575,6 +633,16 @@ var ReplSession = class ReplSession {
|
|
|
575
633
|
skillsFailed = /* @__PURE__ */ new Map();
|
|
576
634
|
maxPtcCalls;
|
|
577
635
|
ptcCallsRemaining = null;
|
|
636
|
+
/**
|
|
637
|
+
* Reset the shared WASM module. Forces the next session to instantiate
|
|
638
|
+
* a fresh module. Only needed in tests where module state must be
|
|
639
|
+
* isolated between test files.
|
|
640
|
+
*
|
|
641
|
+
* @internal
|
|
642
|
+
*/
|
|
643
|
+
static resetSharedModule() {
|
|
644
|
+
sharedModulePromise = void 0;
|
|
645
|
+
}
|
|
578
646
|
constructor(id, options = {}) {
|
|
579
647
|
this.id = id;
|
|
580
648
|
this.options = options;
|
|
@@ -582,15 +650,15 @@ var ReplSession = class ReplSession {
|
|
|
582
650
|
}
|
|
583
651
|
async ensureStarted() {
|
|
584
652
|
if (this.runtime) return;
|
|
585
|
-
const { memoryLimitBytes = DEFAULT_MEMORY_LIMIT, maxStackSizeBytes = DEFAULT_MAX_STACK_SIZE, tools, skillsEnabled = false, maxResultChars = DEFAULT_MAX_RESULTS_CHARS } = this.options;
|
|
586
|
-
const runtime = (await
|
|
653
|
+
const { memoryLimitBytes = DEFAULT_MEMORY_LIMIT, maxStackSizeBytes = DEFAULT_MAX_STACK_SIZE, tools, skillsEnabled = false, maxResultChars = DEFAULT_MAX_RESULTS_CHARS, captureConsole = true } = this.options;
|
|
654
|
+
const runtime = (await getSharedModule()).newRuntime();
|
|
587
655
|
runtime.setMemoryLimit(memoryLimitBytes);
|
|
588
656
|
runtime.setMaxStackSize(maxStackSizeBytes);
|
|
589
657
|
const context = runtime.newContext();
|
|
590
658
|
this.runtime = runtime;
|
|
591
659
|
this.context = context;
|
|
592
660
|
this.consoleBuffer = new ConsoleBuffer(maxResultChars);
|
|
593
|
-
this.setupConsole();
|
|
661
|
+
if (captureConsole) this.setupConsole();
|
|
594
662
|
if (tools !== void 0 && tools.length > 0) this.injectTools(tools);
|
|
595
663
|
if (skillsEnabled) this.installModuleLoader();
|
|
596
664
|
}
|
|
@@ -615,21 +683,45 @@ var ReplSession = class ReplSession {
|
|
|
615
683
|
throw err;
|
|
616
684
|
}
|
|
617
685
|
}
|
|
618
|
-
|
|
686
|
+
/**
|
|
687
|
+
* Pre-load all skills referenced in source code into the in-memory
|
|
688
|
+
* cache. Must be called before `evalCodeAsync` so the module loader
|
|
689
|
+
* can resolve synchronously. An async loader would cause asyncify
|
|
690
|
+
* suspensions on each import, which is incompatible with the shared
|
|
691
|
+
* WASM module used by all sessions.
|
|
692
|
+
*/
|
|
693
|
+
async preloadReferencedSkills(code) {
|
|
694
|
+
const refs = scanSkillReferences(code);
|
|
695
|
+
for (const name of refs) {
|
|
696
|
+
if (this.skillsLoaded.has(name) || this.skillsFailed.has(name)) continue;
|
|
697
|
+
try {
|
|
698
|
+
await this.ensureSkillLoaded(name);
|
|
699
|
+
} catch (err) {
|
|
700
|
+
if (!this.skillsFailed.has(name)) this.skillsFailed.set(name, err);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Resolve a module specifier to source code. Strictly synchronous —
|
|
706
|
+
* only reads from the in-memory skill cache populated by
|
|
707
|
+
* `preloadReferencedSkills`. Returns error source (not a thrown
|
|
708
|
+
* exception) for missing or failed skills so QuickJS reports the
|
|
709
|
+
* error inside the VM.
|
|
710
|
+
*/
|
|
711
|
+
resolveSpecifier(specifier) {
|
|
619
712
|
const parsed = parseSkillSpecifier(specifier);
|
|
620
713
|
if (parsed === void 0) return makeErrorSource(`Module not found: ${specifier}`);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
return makeErrorSource(err.message ?? String(err));
|
|
626
|
-
}
|
|
714
|
+
const cachedError = this.skillsFailed.get(parsed.name);
|
|
715
|
+
if (cachedError !== void 0) return makeErrorSource(cachedError.message ?? String(cachedError));
|
|
716
|
+
const loaded = this.skillsLoaded.get(parsed.name);
|
|
717
|
+
if (loaded === void 0) return makeErrorSource(`Skill '${parsed.name}' was not preloaded. Ensure the import specifier is a static string literal (dynamic specifiers like \`import("@/skills/" + name)\` are not supported).`);
|
|
627
718
|
if (parsed.rel === void 0) {
|
|
628
719
|
const source = loaded.files.get(loaded.entryRel);
|
|
629
720
|
if (source === void 0) return makeErrorSource(`Skill '${parsed.name}': entrypoint '${loaded.entryRel}' missing from bundle`);
|
|
630
721
|
return source;
|
|
631
722
|
}
|
|
632
|
-
|
|
723
|
+
let source = loaded.files.get(parsed.rel);
|
|
724
|
+
if (source === void 0 && parsed.rel.endsWith(".js")) source = loaded.files.get(parsed.rel.slice(0, -3) + ".ts");
|
|
633
725
|
if (source === void 0) return makeErrorSource(`Skill '${parsed.name}': '${parsed.rel}' not found in bundle`);
|
|
634
726
|
return source;
|
|
635
727
|
}
|
|
@@ -649,10 +741,16 @@ var ReplSession = class ReplSession {
|
|
|
649
741
|
}
|
|
650
742
|
/**
|
|
651
743
|
* Wire the QuickJS module loader and normalizer on this session's runtime.
|
|
744
|
+
*
|
|
745
|
+
* The loader is strictly synchronous — it reads from the in-memory skill
|
|
746
|
+
* cache populated by `preloadReferencedSkills`. This is critical: an async
|
|
747
|
+
* module loader causes asyncify suspensions on each import, and disposing
|
|
748
|
+
* a runtime after multi-file imports corrupts the shared module's asyncify
|
|
749
|
+
* state, silently breaking the loader for all subsequent sessions.
|
|
652
750
|
*/
|
|
653
751
|
installModuleLoader() {
|
|
654
752
|
if (this.runtime === null) return;
|
|
655
|
-
this.runtime.setModuleLoader(
|
|
753
|
+
this.runtime.setModuleLoader((specifier) => this.resolveSpecifier(specifier), (base, requested) => this.normalizeSpecifier(base, requested));
|
|
656
754
|
}
|
|
657
755
|
/**
|
|
658
756
|
* Initialise the per-eval PTC counter. Called at the top of every `eval()`.
|
|
@@ -716,7 +814,7 @@ var ReplSession = class ReplSession {
|
|
|
716
814
|
}
|
|
717
815
|
/**
|
|
718
816
|
* Push the current skills metadata + backend into the session.
|
|
719
|
-
* Called by the middleware once per `
|
|
817
|
+
* Called by the middleware once per `eval` invocation, before eval runs.
|
|
720
818
|
* Pass `undefined` to clear the context (no skill imports will resolve).
|
|
721
819
|
*/
|
|
722
820
|
setSkillsContext(ctx) {
|
|
@@ -735,6 +833,7 @@ var ReplSession = class ReplSession {
|
|
|
735
833
|
await this.ensureStarted();
|
|
736
834
|
const runtime = this.runtime;
|
|
737
835
|
const context = this.context;
|
|
836
|
+
await this.preloadReferencedSkills(code);
|
|
738
837
|
const drainLogs = () => {
|
|
739
838
|
const [raw, dropped] = this.consoleBuffer.drain();
|
|
740
839
|
return {
|
|
@@ -747,7 +846,7 @@ var ReplSession = class ReplSession {
|
|
|
747
846
|
if (timeoutMs >= 0) runtime.setInterruptHandler(shouldInterruptAfterDeadline(Date.now() + timeoutMs));
|
|
748
847
|
else runtime.setInterruptHandler(() => false);
|
|
749
848
|
const transformed = transformForEval(code);
|
|
750
|
-
const result = await context.evalCodeAsync(transformed);
|
|
849
|
+
const result = await sharedEvalQueue.enqueue(() => context.evalCodeAsync(transformed));
|
|
751
850
|
if (result.error) {
|
|
752
851
|
const error = context.dump(result.error);
|
|
753
852
|
result.error.dispose();
|
|
@@ -906,33 +1005,26 @@ var ReplSession = class ReplSession {
|
|
|
906
1005
|
//#endregion
|
|
907
1006
|
//#region src/middleware.ts
|
|
908
1007
|
/**
|
|
909
|
-
*
|
|
1008
|
+
* Code Interpreter middleware for deepagents.
|
|
910
1009
|
*
|
|
911
|
-
* Provides
|
|
1010
|
+
* Provides an `eval` tool that runs JavaScript in a WASM-sandboxed QuickJS
|
|
912
1011
|
* interpreter. Supports:
|
|
913
1012
|
* - Persistent state across evaluations (true REPL)
|
|
914
1013
|
* - Programmatic tool calling (PTC) — expose agent or custom tools inside the REPL
|
|
915
1014
|
*/
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
TypeScript syntax (type annotations, interfaces, generics, \`as\` casts) is supported and stripped at evaluation time.
|
|
921
|
-
Variables, functions, and closures persist across calls within the same session.
|
|
922
|
-
|
|
923
|
-
### Hard rules
|
|
924
|
-
|
|
925
|
-
- **No network, no direct filesystem** — only through tools provided in the \`tools\` namespace below.
|
|
926
|
-
- **Cite your sources** — when reporting values from files, include the path and key/index so the user can verify.
|
|
927
|
-
- **Use console.log()** for output — it is captured and returned. \`console.warn()\` and \`console.error()\` are also available.
|
|
928
|
-
- **Reuse state from previous cells** — variables, functions, and results from earlier \`js_eval\` calls persist across calls. Reference them by name in follow-up cells instead of re-embedding data as inline JSON literals.
|
|
929
|
-
|
|
930
|
-
### Limitations
|
|
1015
|
+
const DEFAULT_TOOL_NAME = "eval";
|
|
1016
|
+
function renderReplSystemPrompt(opts) {
|
|
1017
|
+
return dedent`
|
|
1018
|
+
### Interpreter
|
|
931
1019
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1020
|
+
An \`${opts.toolName}\` tool is available. It runs JavaScript in a persistent REPL.
|
|
1021
|
+
- State (variables, functions) persists across tool calls within a single turn of conversation. They DO NOT persist across multiple turns.
|
|
1022
|
+
- Top-level \`await\` works; Promises resolve before the call returns.
|
|
1023
|
+
- Sandboxed: no filesystem, no stdlib, no network, no real clock, no \`fetch\`, no \`require\`.
|
|
1024
|
+
- Timeout: ${opts.timeout}s per call. Memory: ${opts.memoryLimitMb} MB total.
|
|
1025
|
+
- \`console.log\` output is captured and returned alongside the result.
|
|
1026
|
+
`;
|
|
1027
|
+
}
|
|
936
1028
|
/**
|
|
937
1029
|
* Generate the PTC API Reference section for the system prompt.
|
|
938
1030
|
*/
|
|
@@ -1009,21 +1101,25 @@ async function prepareSkillsForEval(session, skillsBackend, code) {
|
|
|
1009
1101
|
});
|
|
1010
1102
|
}
|
|
1011
1103
|
/**
|
|
1012
|
-
* Create the
|
|
1104
|
+
* Create the Code Interpreter middleware.
|
|
1013
1105
|
*/
|
|
1014
|
-
function
|
|
1015
|
-
const { ptc, memoryLimitBytes = DEFAULT_MEMORY_LIMIT, maxStackSizeBytes = DEFAULT_MAX_STACK_SIZE, executionTimeoutMs = DEFAULT_EXECUTION_TIMEOUT, systemPrompt: customSystemPrompt = null, skillsBackend, maxPtcCalls = 256, maxResultChars = DEFAULT_MAX_RESULTS_CHARS } = options;
|
|
1106
|
+
function createCodeInterpreterMiddleware(options = {}) {
|
|
1107
|
+
const { ptc, memoryLimitBytes = DEFAULT_MEMORY_LIMIT, maxStackSizeBytes = DEFAULT_MAX_STACK_SIZE, executionTimeoutMs = DEFAULT_EXECUTION_TIMEOUT, systemPrompt: customSystemPrompt = null, skillsBackend, maxPtcCalls = 256, maxResultChars = DEFAULT_MAX_RESULTS_CHARS, toolName = DEFAULT_TOOL_NAME, captureConsole = true } = options;
|
|
1016
1108
|
if (maxPtcCalls !== null && maxPtcCalls !== void 0 && maxPtcCalls < 1) throw new Error("`maxPtcCalls` must be >= 1 or null");
|
|
1017
|
-
const baseSystemPrompt = customSystemPrompt ||
|
|
1109
|
+
const baseSystemPrompt = customSystemPrompt || renderReplSystemPrompt({
|
|
1110
|
+
toolName,
|
|
1111
|
+
timeout: executionTimeoutMs / 1e3,
|
|
1112
|
+
memoryLimitMb: Math.floor(memoryLimitBytes / (1024 * 1024))
|
|
1113
|
+
});
|
|
1018
1114
|
const middlewareId = crypto.randomUUID();
|
|
1019
1115
|
let cachedPtcPrompt = null;
|
|
1020
1116
|
let ptcTools = [];
|
|
1021
1117
|
function filterToolsForPtc(allTools) {
|
|
1022
1118
|
if (!ptc) return [];
|
|
1023
|
-
return resolveToolList(ptc, allTools.filter((t) => t.name !==
|
|
1119
|
+
return resolveToolList(ptc, allTools.filter((t) => t.name !== toolName));
|
|
1024
1120
|
}
|
|
1025
1121
|
return createMiddleware({
|
|
1026
|
-
name: "
|
|
1122
|
+
name: "CodeInterpreterMiddleware",
|
|
1027
1123
|
tools: [tool(async (input, config) => {
|
|
1028
1124
|
const sessionKey = `${config.configurable?.thread_id || "__default__"}:${middlewareId}`;
|
|
1029
1125
|
const session = ReplSession.getOrCreate(sessionKey, {
|
|
@@ -1032,7 +1128,8 @@ function createQuickJSMiddleware(options = {}) {
|
|
|
1032
1128
|
maxPtcCalls,
|
|
1033
1129
|
tools: ptcTools,
|
|
1034
1130
|
skillsEnabled: skillsBackend !== void 0,
|
|
1035
|
-
maxResultChars
|
|
1131
|
+
maxResultChars,
|
|
1132
|
+
captureConsole
|
|
1036
1133
|
});
|
|
1037
1134
|
if (skillsBackend !== void 0) {
|
|
1038
1135
|
const setupError = await prepareSkillsForEval(session, skillsBackend, input.code);
|
|
@@ -1040,7 +1137,7 @@ function createQuickJSMiddleware(options = {}) {
|
|
|
1040
1137
|
}
|
|
1041
1138
|
return formatReplResult(await session.eval(input.code, executionTimeoutMs));
|
|
1042
1139
|
}, {
|
|
1043
|
-
name:
|
|
1140
|
+
name: toolName,
|
|
1044
1141
|
description: dedent`
|
|
1045
1142
|
Evaluate TypeScript/JavaScript code in a sandboxed REPL. State persists across calls.
|
|
1046
1143
|
Use console.log() for output. Returns the result of the last expression.
|
|
@@ -1066,6 +1163,6 @@ function createQuickJSMiddleware(options = {}) {
|
|
|
1066
1163
|
});
|
|
1067
1164
|
}
|
|
1068
1165
|
//#endregion
|
|
1069
|
-
export { DEFAULT_EXECUTION_TIMEOUT, DEFAULT_MAX_PTC_CALLS, DEFAULT_MAX_STACK_SIZE, DEFAULT_MEMORY_LIMIT, MAX_SKILL_BUNDLE_BYTES, PTCCallBudgetExceededError, ReplSession, SKILL_MODULE_EXTENSIONS,
|
|
1166
|
+
export { DEFAULT_EXECUTION_TIMEOUT, DEFAULT_MAX_PTC_CALLS, DEFAULT_MAX_STACK_SIZE, DEFAULT_MEMORY_LIMIT, MAX_SKILL_BUNDLE_BYTES, PTCCallBudgetExceededError, ReplSession, SKILL_MODULE_EXTENSIONS, createCodeInterpreterMiddleware, formatReplResult, formatSkillNotAvailable, loadSkill, scanSkillReferences, stripTypeSyntax, toCamelCase, transformForEval };
|
|
1070
1167
|
|
|
1071
1168
|
//# sourceMappingURL=index.js.map
|