@codeworksh/aikit 0.2.0 → 0.3.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @codeworksh/aikit
2
2
 
3
- Toolkit for Building AI Agents and Workflows.
3
+ Toolkit for building AI agents and streaming LLM workflows.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,341 +8,198 @@ Toolkit for Building AI Agents and Workflows.
8
8
  npm install @codeworksh/aikit
9
9
  ```
10
10
 
11
- ## Quick Start
11
+ Node `>=24.14.1` is required.
12
+
13
+ ## What You Get
14
+
15
+ - `llm(...)` to resolve a model from the built-in registry
16
+ - `stream(...)` and `stream.complete(...)` for provider-normalized LLM streaming
17
+ - `Agent.create(...)` for a stateful agent instance
18
+ - `agent.loop(...)` and `agent.loopContinue(...)` for lower-level turn orchestration
19
+ - `Message` helpers for typed messages and generated `messageId` values
20
+
21
+ ## Root API
12
22
 
13
23
  ```ts
14
- import { Message, llm, stream } from "@codeworksh/aikit";
24
+ import { Agent, Message, agent, llm, stream } from "@codeworksh/aikit";
25
+ ```
15
26
 
16
- const model = await llm("anthropic", "claude-sonnet-4-5");
17
- if (!model) throw new Error("Model not found");
27
+ ## Quick Start
18
28
 
19
- const context: Message.Context = {
20
- systemPrompt: "You are a concise assistant.",
21
- messages: [
22
- {
23
- role: "user",
24
- time: { created: Date.now() },
25
- parts: [{ type: "text", text: "Say hello in one line." }],
26
- },
27
- ],
28
- };
29
+ ```ts
30
+ import { Agent } from "@codeworksh/aikit";
29
31
 
30
- const s = stream(model, context, {
31
- apiKey: process.env.ANTHROPIC_API_KEY,
32
+ const agent = await Agent.create({
33
+ provider: "anthropic",
34
+ model: "claude-haiku-4-5-20251001",
35
+ getApiKey: async () => process.env.ANTHROPIC_API_KEY,
32
36
  });
33
37
 
34
- for await (const event of s) {
35
- if (event.type === "text.delta") {
36
- process.stdout.write(event.delta);
37
- }
38
- }
38
+ agent.setSystemPrompt("Be concise.");
39
39
 
40
- const message = await s.result();
41
- console.log(message.parts);
42
- ```
40
+ await agent.prompt([{ type: "text", text: "Reply with exactly: hello" }]);
43
41
 
44
- ## Core Concepts
42
+ const lastMessage = agent.state.messages.at(-1);
43
+ console.log(lastMessage?.messageId);
44
+ console.log(lastMessage?.role);
45
+ ```
45
46
 
46
- ### LLM
47
+ ## Stream Events
47
48
 
48
- `llm(...)` resolves a model definition from the built-in registry. `stream(...)` and `stream.complete(...)` use that model to produce `AssistantMessage` output made of `message.parts`.
49
+ Use `stream(...)` when you want token and part-level events.
49
50
 
50
51
  ```ts
51
- import { llm, stream } from "@codeworksh/aikit";
52
+ import { Message, llm, stream } from "@codeworksh/aikit";
52
53
 
53
54
  const model = await llm("anthropic", "claude-haiku-4-5-20251001");
54
- if (!model) throw new Error("Missing model");
55
+ if (!model) throw new Error("Model not found");
55
56
 
56
- const message = await stream.complete(
57
+ const s = stream(
57
58
  model,
58
59
  {
59
60
  messages: [
60
- {
61
+ Message.createUserMessage({
61
62
  role: "user",
62
63
  time: { created: Date.now() },
63
- parts: [{ type: "text", text: "Reply with exactly: ok" }],
64
- },
64
+ parts: [{ type: "text", text: "Count from 1 to 3" }],
65
+ }),
65
66
  ],
66
67
  },
67
68
  { apiKey: process.env.ANTHROPIC_API_KEY },
68
69
  );
70
+
71
+ for await (const event of s) {
72
+ if (event.type === "text.delta") {
73
+ process.stdout.write(event.delta);
74
+ }
75
+ }
76
+
77
+ const finalMessage = await s.result();
78
+ console.log(finalMessage.messageId);
69
79
  ```
70
80
 
71
- ### Agent
81
+ ## Agent Instance
72
82
 
73
- `agent.run(...)` runs a full assistant turn loop on top of model streaming. It handles tool calls, validates arguments, mutates tool-call parts in-place, and emits higher-level agent events.
83
+ `Agent.create(...)` is the simplest way to work with a stateful agent.
74
84
 
75
85
  ```ts
76
- import { Type } from "@sinclair/typebox";
77
- import { Agent, Message, agent, llm } from "@codeworksh/aikit";
86
+ import { Agent } from "@codeworksh/aikit";
87
+
88
+ const agent = await Agent.create({
89
+ provider: "anthropic",
90
+ model: "claude-haiku-4-5-20251001",
91
+ getApiKey: async () => process.env.ANTHROPIC_API_KEY,
92
+ });
78
93
 
79
- const calculatorParams = Type.Object({
80
- expression: Type.String(),
94
+ agent.setSystemPrompt("Be concise.");
95
+
96
+ agent.subscribe((event) => {
97
+ if (event.type === "message.end") {
98
+ console.log(event.message.role, event.message.messageId);
99
+ }
81
100
  });
82
101
 
102
+ await agent.prompt([{ type: "text", text: "Say hello in one line." }]);
103
+
104
+ console.log(agent.state.messages);
105
+ ```
106
+
107
+ ## With Tools
108
+
109
+ ```ts
110
+ import { Type } from "@sinclair/typebox";
111
+ import { Agent } from "@codeworksh/aikit";
112
+
83
113
  const calculatorTool = Agent.defineTool({
84
114
  name: "calculator",
85
115
  label: "Calculator",
86
- description: "Evaluate a math expression",
87
- parameters: calculatorParams,
88
- async execute(callID, params) {
116
+ description: "Evaluate arithmetic expressions",
117
+ parameters: Type.Object({
118
+ expression: Type.String(),
119
+ }),
120
+ async execute(_callID, params) {
89
121
  return {
90
122
  status: "completed",
91
123
  result: {
92
- content: [{ type: "text", text: `${params.expression} = 4` }],
124
+ content: [{ type: "text", text: `result for ${params.expression}` }],
93
125
  isError: false,
94
126
  },
95
127
  };
96
128
  },
97
129
  });
98
130
 
99
- const model = await llm("anthropic", "claude-haiku-4-5-20251001");
100
- if (!model) throw new Error("Missing model");
101
-
102
- const prompt: Message.UserMessage = {
103
- role: "user",
104
- time: { created: Date.now() },
105
- parts: [{ type: "text", text: "Use the calculator tool for 2 + 2." }],
106
- };
107
-
108
- const run = agent.run(
109
- {
110
- model,
111
- convertToLlm: async (messages) => messages,
112
- apiKey: process.env.ANTHROPIC_API_KEY,
113
- beforeToolExecution: async ({ toolCall }) => {
114
- console.log("validated args", toolCall.args);
115
- },
116
- afterToolExecution: async ({ result }) => result,
117
- },
118
- {
119
- systemPrompt: "Use tools when helpful.",
120
- messages: [],
131
+ const instance = await Agent.create({
132
+ provider: "anthropic",
133
+ model: "claude-haiku-4-5-20251001",
134
+ getApiKey: async () => process.env.ANTHROPIC_API_KEY,
135
+ initialState: {
121
136
  tools: [calculatorTool],
122
137
  },
123
- [prompt],
124
- );
125
-
126
- for await (const event of run) {
127
- if (event.type === "message.part.update" && event.source === "tool") {
128
- console.log(event.part);
129
- }
130
- }
131
-
132
- const messages = await run.result();
133
- console.log(messages.at(-1));
134
- ```
135
-
136
- ### Agent Vs LLM
137
-
138
- Use `stream(...)` when you want raw provider events and manual control over tool handling.
139
-
140
- Use `agent.run(...)` when you want a higher-level runtime that:
141
-
142
- - streams assistant message lifecycle events
143
- - validates tool arguments
144
- - executes tools sequentially or in parallel
145
- - supports `beforeToolExecution` and `afterToolExecution` callbacks
146
-
147
- ## Event Flow
148
-
149
- LLM streams emit low-level provider-normalized events:
138
+ });
150
139
 
151
- ```text
152
- start
153
- text.start / thinking.start / toolcall.start
154
- text.delta / thinking.delta / toolcall.delta
155
- text.end / thinking.end / toolcall.end
156
- done | error
140
+ await instance.prompt([{ type: "text", text: "Use the calculator tool for 25 * 18." }]);
157
141
  ```
158
142
 
159
- ## With Tools
160
-
161
- Agent loops emit higher-level lifecycle events around messages, parts, turns, and tools:
162
-
163
- ```text
164
- agent.start
165
- turn.start
166
- message.start
167
- message.part.start
168
- message.part.update
169
- message.part.end
170
- message.update
171
- message.end
172
- tool.execution.start
173
- tool.execution.update
174
- tool.execution.end
175
- turn.end
176
- agent.end
177
- ```
143
+ ## Lower-Level Loop
178
144
 
179
- Example:
145
+ Use `agent.loop(...)` when you want loop execution without creating a stateful instance.
180
146
 
181
147
  ```ts
182
- import { Type } from "@sinclair/typebox";
183
- import { Agent, Message, agent, llm } from "@codeworksh/aikit";
184
-
185
- const searchParams = Type.Object({
186
- query: Type.String(),
187
- limit: Type.Optional(Type.Number()),
188
- });
189
-
190
- const searchTool: Agent.AgentTool<typeof searchParams, { progress: number }, { hits: number }> = {
191
- name: "search",
192
- label: "Search",
193
- description: "Search indexed documents",
194
- parameters: searchParams,
195
- async execute(callID, params, signal, onUpdate) {
196
- await onUpdate?.({
197
- status: "running",
198
- partial: {
199
- content: [{ type: "text", text: `Searching for ${params.query}` }],
200
- details: { progress: 50 },
201
- },
202
- });
203
-
204
- return {
205
- status: "completed",
206
- result: {
207
- content: [{ type: "text", text: `Found results for ${params.query}` }],
208
- details: { hits: 3 },
209
- isError: false,
210
- },
211
- };
212
- },
213
- };
148
+ import { Message, agent, llm } from "@codeworksh/aikit";
214
149
 
215
150
  const model = await llm("anthropic", "claude-haiku-4-5-20251001");
216
- if (!model) throw new Error("Missing model");
217
-
218
- const prompt: Message.UserMessage = {
219
- role: "user",
220
- time: { created: Date.now() },
221
- parts: [{ type: "text", text: "Search for agent loop documentation." }],
222
- };
151
+ if (!model) throw new Error("Model not found");
223
152
 
224
- const run = agent.run(
153
+ const run = agent.loop(
225
154
  {
226
155
  model,
227
156
  apiKey: process.env.ANTHROPIC_API_KEY,
228
157
  convertToLlm: async (messages) => messages,
229
- toolExecution: "parallel",
230
158
  },
231
159
  {
232
- systemPrompt: "Use tools when useful.",
160
+ systemPrompt: "Be concise.",
233
161
  messages: [],
234
- tools: [searchTool],
162
+ tools: [],
235
163
  },
236
- [prompt],
164
+ [
165
+ Message.createUserMessage({
166
+ role: "user",
167
+ time: { created: Date.now() },
168
+ parts: [{ type: "text", text: "Reply with exactly: ok" }],
169
+ }),
170
+ ],
237
171
  );
238
172
 
239
- for await (const event of run) {
240
- if (event.type === "tool.execution.start") {
241
- console.log("tool started", event.name, event.args ?? event.rawArgs);
242
- }
243
-
244
- if (event.type === "tool.execution.update") {
245
- console.log("tool update", event.partial?.details);
246
- }
247
-
248
- if (event.type === "tool.execution.end") {
249
- console.log("tool finished", event.result.details);
250
- }
251
- }
173
+ const messages = await run.result();
174
+ console.log(messages.at(-1)?.role);
252
175
  ```
253
176
 
254
- ### Tool Execution Modes
177
+ ## Message IDs
255
178
 
256
- Tool execution mode is configurable with `toolExecution`:
179
+ Every user and assistant message has a `messageId`.
257
180
 
258
- - `parallel`: prepares tool calls in assistant order, then executes allowed calls concurrently. This is the default and is usually the right choice for independent tools.
259
- - `sequential`: prepares and executes each tool call one by one. Use this when tools depend on shared state, rate limits, or side effects that should not overlap.
181
+ Use the helpers when constructing messages yourself:
260
182
 
261
183
  ```ts
262
- const run = agent.run(
263
- {
264
- model,
265
- convertToLlm: async (messages) => messages,
266
- toolExecution: "sequential",
267
- },
268
- context,
269
- [prompt],
270
- );
271
- ```
184
+ import { Message } from "@codeworksh/aikit";
272
185
 
273
- ### beforeToolExecution
274
-
275
- `beforeToolExecution` runs after tool arguments have been validated and before the tool executes.
276
-
277
- Use it to inspect validated params, enforce policy, or block execution.
186
+ const userMessage = Message.createUserMessage({
187
+ role: "user",
188
+ time: { created: Date.now() },
189
+ parts: [{ type: "text", text: "hello" }],
190
+ });
278
191
 
279
- ```ts
280
- const run = agent.run(
281
- {
282
- model,
283
- convertToLlm: async (messages) => messages,
284
- beforeToolExecution: async ({ toolCall }) => {
285
- if (toolCall.name === "search" && toolCall.args?.query === "secret") {
286
- return {
287
- block: true,
288
- reason: "This query is not allowed",
289
- };
290
- }
291
- },
292
- },
293
- context,
294
- [prompt],
295
- );
192
+ console.log(userMessage.messageId);
296
193
  ```
297
194
 
298
- Returning `{ block: true }` prevents the tool from running and turns that assistant tool-call part into an error result.
299
-
300
- ### afterToolExecution
301
-
302
- `afterToolExecution` runs after the tool finishes and before the terminal tool result is emitted.
303
-
304
- Use it to normalize output, attach metadata, or override the final tool result.
305
-
306
- ```ts
307
- const run = agent.run(
308
- {
309
- model,
310
- convertToLlm: async (messages) => messages,
311
- afterToolExecution: async ({ result }) => {
312
- if (result.status === "completed") {
313
- return {
314
- status: "completed",
315
- result: {
316
- ...result.result,
317
- details: {
318
- ...result.result.details,
319
- source: "post-processed",
320
- },
321
- },
322
- };
323
- }
324
- },
325
- },
326
- context,
327
- [prompt],
328
- );
329
- ```
195
+ This is useful for persistence layers like SQLite, event reconciliation, and updating stored messages by identity instead of array position.
330
196
 
331
- ## Public API
197
+ ## Lower-Level APIs
332
198
 
333
- The root package exports the facades and core namespaces used to build on top of `aikit`:
199
+ - `stream(...)`: raw provider-normalized stream
200
+ - `stream.complete(...)`: final assistant message without manual iteration
201
+ - `Agent.create(...)`: stateful agent instance
202
+ - `agent.loop(...)`: lower-level agent loop
203
+ - `agent.loopContinue(...)`: continue from an existing context
334
204
 
335
- ```ts
336
- import {
337
- agent,
338
- llm,
339
- stream,
340
- Agent,
341
- Event,
342
- Message,
343
- Model,
344
- Stream,
345
- validateToolArguments,
346
- validateToolCall,
347
- } from "@codeworksh/aikit";
348
- ```
205
+ API docs can be added later. For now, the tests under `packages/aikit/test/` are the best reference for concrete usage.
@@ -0,0 +1,7 @@
1
+ import { t as CodeMode } from "../../../codemode-z3ZIw-5q.mjs";
2
+
3
+ //#region src/agent/codemode/drivers/quickjs-wasi-driver.d.ts
4
+ declare function createQuickJSWasiDriver(): CodeMode.Driver;
5
+ //#endregion
6
+ export { createQuickJSWasiDriver };
7
+ //# sourceMappingURL=drivers.d.mts.map
@@ -0,0 +1,122 @@
1
+ import { QuickJS } from "quickjs-wasi";
2
+ //#region src/agent/codemode/drivers/quickjs-wasi-driver.ts
3
+ const DEFAULT_TIMEOUT_MS = 3e4;
4
+ const DEFAULT_MEMORY_LIMIT_MB = 128;
5
+ function withMessageInStack(message, stack) {
6
+ if (!stack) return void 0;
7
+ return stack.includes(message) ? stack : `${message}\n${stack}`;
8
+ }
9
+ function parseErrorLine(stack) {
10
+ if (typeof stack !== "string") return void 0;
11
+ const lineMatch = stack.match(/:(\d+)(?::\d+)?\)?$/m);
12
+ return lineMatch ? Number(lineMatch[1]) : void 0;
13
+ }
14
+ function normalizeExecutionError(error) {
15
+ if (error instanceof Error) return {
16
+ name: error.name,
17
+ message: error.message,
18
+ stack: withMessageInStack(error.message, error.stack),
19
+ line: parseErrorLine(error.stack)
20
+ };
21
+ if (typeof error === "object" && error !== null) {
22
+ const candidate = error;
23
+ const message = typeof candidate.message === "string" ? candidate.message : JSON.stringify(candidate) || "Unknown Execution Error";
24
+ return {
25
+ name: typeof candidate.name === "string" ? candidate.name : void 0,
26
+ message,
27
+ stack: typeof candidate.stack === "string" ? withMessageInStack(message, candidate.stack) : void 0,
28
+ line: typeof candidate.line === "number" ? candidate.line : parseErrorLine(candidate.stack)
29
+ };
30
+ }
31
+ return {
32
+ name: "Error",
33
+ message: String(error)
34
+ };
35
+ }
36
+ function toMegabytes(value) {
37
+ return (value ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
38
+ }
39
+ function createConsole(vm, logs) {
40
+ const consoleObject = vm.newObject();
41
+ for (const [name, prefix] of [
42
+ ["log", ""],
43
+ ["info", "INFO: "],
44
+ ["warn", "WARN: "],
45
+ ["error", "ERROR: "]
46
+ ]) {
47
+ const fn = vm.newFunction(name, (...args) => {
48
+ const message = args.map((arg) => String(vm.dump(arg))).join(" ");
49
+ logs.push(`${prefix}${message}`);
50
+ return vm.undefined;
51
+ });
52
+ consoleObject.setProp(name, fn);
53
+ }
54
+ vm.setProp(vm.global, "console", consoleObject);
55
+ }
56
+ function registerBindings(vm, bindings, signal) {
57
+ let callCounter = 0;
58
+ for (const [name, binding] of Object.entries(bindings)) {
59
+ const fn = vm.newFunction(name, (...args) => {
60
+ const input = args[0] ? vm.dump(args[0]) : void 0;
61
+ const result = Promise.resolve(binding.execute(`${name}:${++callCounter}`, input, signal));
62
+ return vm.hostToHandle(result);
63
+ });
64
+ vm.setProp(vm.global, name, fn);
65
+ }
66
+ }
67
+ function createQuickJSWasiDriver() {
68
+ return { async createContext(config) {
69
+ const timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
70
+ const deadline = Date.now() + timeout;
71
+ const logs = [];
72
+ const vm = await QuickJS.create({
73
+ memoryLimit: toMegabytes(config.memoryLimit),
74
+ interruptHandler: () => Boolean(config.signal?.aborted) || Date.now() > deadline
75
+ });
76
+ createConsole(vm, logs);
77
+ registerBindings(vm, config.bindings, config.signal);
78
+ return {
79
+ async execute(code) {
80
+ let resultHandle;
81
+ try {
82
+ resultHandle = vm.evalCode(code, "script.js");
83
+ vm.executePendingJobs();
84
+ const settled = await vm.resolvePromise(resultHandle);
85
+ if ("error" in settled) try {
86
+ return {
87
+ success: false,
88
+ error: normalizeExecutionError(vm.dump(settled.error)),
89
+ logs: [...logs]
90
+ };
91
+ } finally {
92
+ settled.error.dispose();
93
+ }
94
+ try {
95
+ return {
96
+ success: true,
97
+ value: vm.dump(settled.value),
98
+ logs: [...logs]
99
+ };
100
+ } finally {
101
+ settled.value.dispose();
102
+ }
103
+ } catch (error) {
104
+ return {
105
+ success: false,
106
+ error: normalizeExecutionError(error),
107
+ logs: [...logs]
108
+ };
109
+ } finally {
110
+ resultHandle?.dispose();
111
+ }
112
+ },
113
+ async dispose() {
114
+ vm.dispose();
115
+ }
116
+ };
117
+ } };
118
+ }
119
+ //#endregion
120
+ export { createQuickJSWasiDriver };
121
+
122
+ //# sourceMappingURL=drivers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drivers.mjs","names":[],"sources":["../../../../../src/agent/codemode/drivers/quickjs-wasi-driver.ts"],"sourcesContent":["import { QuickJS } from \"quickjs-wasi\";\nimport type { CodeMode } from \"../codemode\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst DEFAULT_MEMORY_LIMIT_MB = 128;\n\nfunction withMessageInStack(message: string, stack: string | undefined): string | undefined {\n\tif (!stack) return undefined;\n\treturn stack.includes(message) ? stack : `${message}\\n${stack}`;\n}\n\nfunction parseErrorLine(stack: unknown): number | undefined {\n\tif (typeof stack !== \"string\") return undefined;\n\tconst lineMatch = stack.match(/:(\\d+)(?::\\d+)?\\)?$/m);\n\treturn lineMatch ? Number(lineMatch[1]) : undefined;\n}\n\nfunction normalizeExecutionError(error: unknown): CodeMode.NormalizedError {\n\tif (error instanceof Error) {\n\t\treturn {\n\t\t\tname: error.name,\n\t\t\tmessage: error.message,\n\t\t\tstack: withMessageInStack(error.message, error.stack),\n\t\t\tline: parseErrorLine(error.stack),\n\t\t};\n\t}\n\n\tif (typeof error === \"object\" && error !== null) {\n\t\tconst candidate = error as Record<string, unknown>;\n\t\tconst message =\n\t\t\ttypeof candidate.message === \"string\"\n\t\t\t\t? candidate.message\n\t\t\t\t: JSON.stringify(candidate) || \"Unknown Execution Error\";\n\t\treturn {\n\t\t\tname: typeof candidate.name === \"string\" ? candidate.name : undefined,\n\t\t\tmessage,\n\t\t\tstack: typeof candidate.stack === \"string\" ? withMessageInStack(message, candidate.stack) : undefined,\n\t\t\tline: typeof candidate.line === \"number\" ? candidate.line : parseErrorLine(candidate.stack),\n\t\t};\n\t}\n\n\treturn {\n\t\tname: \"Error\",\n\t\tmessage: String(error),\n\t};\n}\n\nfunction toMegabytes(value: number | undefined): number {\n\treturn (value ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;\n}\n\nfunction createConsole(vm: QuickJS, logs: string[]) {\n\tconst consoleObject = vm.newObject();\n\tconst methods = [\n\t\t[\"log\", \"\"],\n\t\t[\"info\", \"INFO: \"],\n\t\t[\"warn\", \"WARN: \"],\n\t\t[\"error\", \"ERROR: \"],\n\t] as const;\n\n\tfor (const [name, prefix] of methods) {\n\t\tconst fn = vm.newFunction(name, (...args) => {\n\t\t\tconst message = args.map((arg) => String(vm.dump(arg))).join(\" \");\n\t\t\tlogs.push(`${prefix}${message}`);\n\t\t\treturn vm.undefined;\n\t\t});\n\t\tconsoleObject.setProp(name, fn);\n\t}\n\n\tvm.setProp(vm.global, \"console\", consoleObject);\n}\n\nfunction registerBindings(vm: QuickJS, bindings: Record<string, CodeMode.ToolBinding>, signal?: AbortSignal) {\n\tlet callCounter = 0;\n\n\tfor (const [name, binding] of Object.entries(bindings)) {\n\t\tconst fn = vm.newFunction(name, (...args) => {\n\t\t\tconst input = args[0] ? vm.dump(args[0]) : undefined;\n\t\t\tconst result = Promise.resolve(binding.execute(`${name}:${++callCounter}`, input, signal));\n\t\t\treturn vm.hostToHandle(result);\n\t\t});\n\t\tvm.setProp(vm.global, name, fn);\n\t}\n}\n\nexport function createQuickJSWasiDriver(): CodeMode.Driver {\n\treturn {\n\t\tasync createContext(config: CodeMode.DriverContextConfig): Promise<CodeMode.Context> {\n\t\t\tconst timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n\t\t\tconst deadline = Date.now() + timeout;\n\t\t\tconst logs: string[] = [];\n\t\t\t// create VM for execution sandbox\n\t\t\tconst vm = await QuickJS.create({\n\t\t\t\tmemoryLimit: toMegabytes(config.memoryLimit),\n\t\t\t\tinterruptHandler: () => Boolean(config.signal?.aborted) || Date.now() > deadline,\n\t\t\t});\n\n\t\t\t// create console log bindings\n\t\t\tcreateConsole(vm, logs);\n\t\t\t// register tool fn bindings\n\t\t\tregisterBindings(vm, config.bindings, config.signal);\n\n\t\t\treturn {\n\t\t\t\tasync execute<T = unknown>(code: string) {\n\t\t\t\t\tlet resultHandle: ReturnType<typeof vm.evalCode> | undefined;\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tresultHandle = vm.evalCode(code, \"script.js\");\n\t\t\t\t\t\tvm.executePendingJobs();\n\n\t\t\t\t\t\tconst settled = await vm.resolvePromise(resultHandle);\n\t\t\t\t\t\tif (\"error\" in settled) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\t\t\terror: normalizeExecutionError(vm.dump(settled.error)),\n\t\t\t\t\t\t\t\t\tlogs: [...logs],\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\tsettled.error.dispose();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tsuccess: true,\n\t\t\t\t\t\t\t\tvalue: vm.dump(settled.value) as T,\n\t\t\t\t\t\t\t\tlogs: [...logs],\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tsettled.value.dispose();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: normalizeExecutionError(error),\n\t\t\t\t\t\t\tlogs: [...logs],\n\t\t\t\t\t\t};\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tresultHandle?.dispose();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tasync dispose() {\n\t\t\t\t\tvm.dispose();\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n"],"mappings":"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"}