@samrahimi/smol-js 0.1.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.js ADDED
@@ -0,0 +1,1771 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ Agent: () => Agent,
34
+ AgentLogger: () => AgentLogger,
35
+ AgentMemory: () => AgentMemory,
36
+ CodeAgent: () => CodeAgent,
37
+ FINAL_ANSWER_PROMPT: () => FINAL_ANSWER_PROMPT,
38
+ FinalAnswerTool: () => FinalAnswerTool,
39
+ LocalExecutor: () => LocalExecutor,
40
+ LogLevel: () => LogLevel,
41
+ Model: () => Model,
42
+ OpenAIModel: () => OpenAIModel,
43
+ Tool: () => Tool,
44
+ UserInputTool: () => UserInputTool,
45
+ createTool: () => createTool,
46
+ finalAnswerTool: () => finalAnswerTool,
47
+ generateSystemPrompt: () => generateSystemPrompt,
48
+ getErrorRecoveryPrompt: () => getErrorRecoveryPrompt
49
+ });
50
+ module.exports = __toCommonJS(index_exports);
51
+
52
+ // src/types.ts
53
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
54
+ LogLevel2[LogLevel2["OFF"] = -1] = "OFF";
55
+ LogLevel2[LogLevel2["ERROR"] = 0] = "ERROR";
56
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
57
+ LogLevel2[LogLevel2["DEBUG"] = 2] = "DEBUG";
58
+ return LogLevel2;
59
+ })(LogLevel || {});
60
+
61
+ // src/memory/AgentMemory.ts
62
+ var AgentMemory = class {
63
+ /**
64
+ * System prompt step (always first)
65
+ */
66
+ systemPrompt;
67
+ /**
68
+ * All execution steps
69
+ */
70
+ steps = [];
71
+ constructor(systemPrompt) {
72
+ this.systemPrompt = {
73
+ type: "system",
74
+ content: systemPrompt,
75
+ timestamp: Date.now()
76
+ };
77
+ }
78
+ /**
79
+ * Reset memory, keeping only the system prompt.
80
+ */
81
+ reset() {
82
+ this.steps = [];
83
+ }
84
+ /**
85
+ * Add a task step.
86
+ */
87
+ addTask(task) {
88
+ const step = {
89
+ type: "task",
90
+ task,
91
+ timestamp: Date.now()
92
+ };
93
+ this.steps.push(step);
94
+ return step;
95
+ }
96
+ /**
97
+ * Create a new action step.
98
+ */
99
+ createActionStep(stepNumber) {
100
+ const step = {
101
+ type: "action",
102
+ stepNumber,
103
+ timing: {
104
+ startTime: Date.now()
105
+ },
106
+ modelInputMessages: [],
107
+ timestamp: Date.now()
108
+ };
109
+ this.steps.push(step);
110
+ return step;
111
+ }
112
+ /**
113
+ * Add a final answer step.
114
+ */
115
+ addFinalAnswer(answer) {
116
+ const step = {
117
+ type: "final",
118
+ answer,
119
+ timestamp: Date.now()
120
+ };
121
+ this.steps.push(step);
122
+ return step;
123
+ }
124
+ /**
125
+ * Get the last step.
126
+ */
127
+ getLastStep() {
128
+ return this.steps[this.steps.length - 1];
129
+ }
130
+ /**
131
+ * Get all action steps.
132
+ */
133
+ getActionSteps() {
134
+ return this.steps.filter((s) => s.type === "action");
135
+ }
136
+ /**
137
+ * Convert memory to messages for LLM context.
138
+ */
139
+ toMessages() {
140
+ const messages = [];
141
+ messages.push({
142
+ role: "system",
143
+ content: this.systemPrompt.content
144
+ });
145
+ for (const step of this.steps) {
146
+ switch (step.type) {
147
+ case "task":
148
+ messages.push({
149
+ role: "user",
150
+ content: `Task: ${step.task}`
151
+ });
152
+ break;
153
+ case "action":
154
+ if (step.modelOutputMessage) {
155
+ messages.push({
156
+ role: "assistant",
157
+ content: step.modelOutputMessage.content
158
+ });
159
+ }
160
+ if (step.observation) {
161
+ messages.push({
162
+ role: "user",
163
+ content: step.observation
164
+ });
165
+ }
166
+ if (step.error) {
167
+ messages.push({
168
+ role: "user",
169
+ content: `Error: ${step.error.message}`
170
+ });
171
+ }
172
+ break;
173
+ case "final":
174
+ break;
175
+ }
176
+ }
177
+ return messages;
178
+ }
179
+ /**
180
+ * Get total token usage across all steps.
181
+ */
182
+ getTotalTokenUsage() {
183
+ let inputTokens = 0;
184
+ let outputTokens = 0;
185
+ for (const step of this.steps) {
186
+ if (step.type === "action" && step.tokenUsage) {
187
+ inputTokens += step.tokenUsage.inputTokens;
188
+ outputTokens += step.tokenUsage.outputTokens;
189
+ }
190
+ }
191
+ return {
192
+ inputTokens,
193
+ outputTokens,
194
+ totalTokens: inputTokens + outputTokens
195
+ };
196
+ }
197
+ /**
198
+ * Get a summary of the memory for logging.
199
+ */
200
+ getSummary() {
201
+ const actionSteps = this.getActionSteps();
202
+ const lines = [
203
+ `System Prompt: ${this.systemPrompt.content.slice(0, 100)}...`,
204
+ `Total Steps: ${this.steps.length}`,
205
+ `Action Steps: ${actionSteps.length}`
206
+ ];
207
+ const tokenUsage = this.getTotalTokenUsage();
208
+ if (tokenUsage.totalTokens > 0) {
209
+ lines.push(`Total Tokens: ${tokenUsage.totalTokens}`);
210
+ }
211
+ return lines.join("\n");
212
+ }
213
+ /**
214
+ * Serialize memory to JSON.
215
+ */
216
+ toJSON() {
217
+ return {
218
+ systemPrompt: this.systemPrompt,
219
+ steps: this.steps
220
+ };
221
+ }
222
+ };
223
+
224
+ // src/logging/AgentLogger.ts
225
+ var import_chalk = __toESM(require("chalk"));
226
+ var fs = __toESM(require("fs"));
227
+ var path = __toESM(require("path"));
228
+ var os = __toESM(require("os"));
229
+ var LOG_DIR = path.join(os.homedir(), ".smol-js");
230
+ var AgentLogger = class {
231
+ level;
232
+ logFile;
233
+ sessionId;
234
+ constructor(level = 1 /* INFO */) {
235
+ this.level = level;
236
+ this.sessionId = this.generateSessionId();
237
+ if (level > -1 /* OFF */) {
238
+ this.initLogFile();
239
+ }
240
+ }
241
+ /**
242
+ * Generate a unique session ID.
243
+ */
244
+ generateSessionId() {
245
+ const now = /* @__PURE__ */ new Date();
246
+ const timestamp = now.toISOString().replace(/[:.]/g, "-");
247
+ return `session-${timestamp}`;
248
+ }
249
+ /**
250
+ * Initialize the log file.
251
+ */
252
+ initLogFile() {
253
+ try {
254
+ if (!fs.existsSync(LOG_DIR)) {
255
+ fs.mkdirSync(LOG_DIR, { recursive: true });
256
+ }
257
+ const logPath = path.join(LOG_DIR, `${this.sessionId}.log`);
258
+ this.logFile = fs.createWriteStream(logPath, { flags: "a" });
259
+ this.writeToFile(`=== Session Started: ${(/* @__PURE__ */ new Date()).toISOString()} ===
260
+
261
+ `);
262
+ } catch (error) {
263
+ console.warn("Could not create log file:", error.message);
264
+ }
265
+ }
266
+ /**
267
+ * Write to the log file.
268
+ */
269
+ writeToFile(content) {
270
+ if (this.logFile) {
271
+ const cleanContent = content.replace(/\x1b\[[0-9;]*m/g, "");
272
+ this.logFile.write(cleanContent);
273
+ }
274
+ }
275
+ /**
276
+ * Set the log level.
277
+ */
278
+ setLevel(level) {
279
+ this.level = level;
280
+ }
281
+ /**
282
+ * Log a header (task start, step start, etc.)
283
+ */
284
+ header(message, level = 1 /* INFO */) {
285
+ if (this.level < level) return;
286
+ const line = "\u2550".repeat(60);
287
+ const output = `
288
+ ${import_chalk.default.cyan(line)}
289
+ ${import_chalk.default.cyan.bold(message)}
290
+ ${import_chalk.default.cyan(line)}
291
+ `;
292
+ console.log(output);
293
+ this.writeToFile(`
294
+ ${"\u2550".repeat(60)}
295
+ ${message}
296
+ ${"\u2550".repeat(60)}
297
+ `);
298
+ }
299
+ /**
300
+ * Log a subheader.
301
+ */
302
+ subheader(message, level = 1 /* INFO */) {
303
+ if (this.level < level) return;
304
+ const output = `
305
+ ${import_chalk.default.cyan("\u2500".repeat(40))}
306
+ ${import_chalk.default.cyan(message)}
307
+ `;
308
+ console.log(output);
309
+ this.writeToFile(`
310
+ ${"\u2500".repeat(40)}
311
+ ${message}
312
+ `);
313
+ }
314
+ /**
315
+ * Log reasoning/thought from the agent.
316
+ */
317
+ reasoning(content, level = 1 /* INFO */) {
318
+ if (this.level < level) return;
319
+ const output = `${import_chalk.default.yellow.bold("\u{1F4AD} Reasoning:")}
320
+ ${import_chalk.default.yellow(content)}
321
+ `;
322
+ console.log(output);
323
+ this.writeToFile(`
324
+ \u{1F4AD} Reasoning:
325
+ ${content}
326
+ `);
327
+ }
328
+ /**
329
+ * Log code block.
330
+ */
331
+ code(content, language = "javascript", level = 1 /* INFO */) {
332
+ if (this.level < level) return;
333
+ const output = `${import_chalk.default.green.bold("\u{1F4DD} Code:")}
334
+ ${import_chalk.default.green("```" + language)}
335
+ ${import_chalk.default.green(content)}
336
+ ${import_chalk.default.green("```")}
337
+ `;
338
+ console.log(output);
339
+ this.writeToFile(`
340
+ \u{1F4DD} Code:
341
+ \`\`\`${language}
342
+ ${content}
343
+ \`\`\`
344
+ `);
345
+ }
346
+ /**
347
+ * Log execution output.
348
+ */
349
+ output(content, level = 1 /* INFO */) {
350
+ if (this.level < level) return;
351
+ const output = `${import_chalk.default.blue.bold("\u{1F4E4} Output:")}
352
+ ${import_chalk.default.blue(content)}
353
+ `;
354
+ console.log(output);
355
+ this.writeToFile(`
356
+ \u{1F4E4} Output:
357
+ ${content}
358
+ `);
359
+ }
360
+ /**
361
+ * Log execution logs (print statements).
362
+ */
363
+ logs(content, level = 1 /* INFO */) {
364
+ if (this.level < level || !content.trim()) return;
365
+ const output = `${import_chalk.default.gray.bold("\u{1F4CB} Logs:")}
366
+ ${import_chalk.default.gray(content)}
367
+ `;
368
+ console.log(output);
369
+ this.writeToFile(`
370
+ \u{1F4CB} Logs:
371
+ ${content}
372
+ `);
373
+ }
374
+ /**
375
+ * Log an error.
376
+ */
377
+ error(message, error, level = 0 /* ERROR */) {
378
+ if (this.level < level) return;
379
+ const errorMessage = error ? `${message}: ${error.message}` : message;
380
+ const output = `${import_chalk.default.red.bold("\u274C Error:")}
381
+ ${import_chalk.default.red(errorMessage)}
382
+ `;
383
+ console.error(output);
384
+ this.writeToFile(`
385
+ \u274C Error:
386
+ ${errorMessage}
387
+ `);
388
+ if (error?.stack && this.level >= 2 /* DEBUG */) {
389
+ console.error(import_chalk.default.red.dim(error.stack));
390
+ this.writeToFile(`Stack: ${error.stack}
391
+ `);
392
+ }
393
+ }
394
+ /**
395
+ * Log a warning.
396
+ */
397
+ warn(message, level = 1 /* INFO */) {
398
+ if (this.level < level) return;
399
+ const output = `${import_chalk.default.yellow.bold("\u26A0\uFE0F Warning:")} ${import_chalk.default.yellow(message)}
400
+ `;
401
+ console.warn(output);
402
+ this.writeToFile(`
403
+ \u26A0\uFE0F Warning: ${message}
404
+ `);
405
+ }
406
+ /**
407
+ * Log general info.
408
+ */
409
+ info(message, level = 1 /* INFO */) {
410
+ if (this.level < level) return;
411
+ const output = `${import_chalk.default.white(message)}`;
412
+ console.log(output);
413
+ this.writeToFile(`${message}
414
+ `);
415
+ }
416
+ /**
417
+ * Log debug info.
418
+ */
419
+ debug(message) {
420
+ if (this.level < 2 /* DEBUG */) return;
421
+ const output = `${import_chalk.default.dim("[DEBUG]")} ${import_chalk.default.dim(message)}`;
422
+ console.log(output);
423
+ this.writeToFile(`[DEBUG] ${message}
424
+ `);
425
+ }
426
+ /**
427
+ * Log final answer.
428
+ */
429
+ finalAnswer(answer, level = 1 /* INFO */) {
430
+ if (this.level < level) return;
431
+ const line = "\u2550".repeat(60);
432
+ const answerStr = typeof answer === "string" ? answer : JSON.stringify(answer, null, 2);
433
+ const output = `
434
+ ${import_chalk.default.magenta(line)}
435
+ ${import_chalk.default.magenta.bold("\u2705 Final Answer:")}
436
+ ${import_chalk.default.magenta(answerStr)}
437
+ ${import_chalk.default.magenta(line)}
438
+ `;
439
+ console.log(output);
440
+ this.writeToFile(`
441
+ ${"\u2550".repeat(60)}
442
+ \u2705 Final Answer:
443
+ ${answerStr}
444
+ ${"\u2550".repeat(60)}
445
+ `);
446
+ }
447
+ /**
448
+ * Log step progress.
449
+ */
450
+ stepProgress(current, max, level = 1 /* INFO */) {
451
+ if (this.level < level) return;
452
+ const output = `${import_chalk.default.cyan.bold(`
453
+ \u{1F504} Step ${current}/${max}`)}
454
+ `;
455
+ console.log(output);
456
+ this.writeToFile(`
457
+ \u{1F504} Step ${current}/${max}
458
+ `);
459
+ }
460
+ /**
461
+ * Log waiting message for code execution delay.
462
+ */
463
+ waiting(seconds, level = 1 /* INFO */) {
464
+ if (this.level < level) return;
465
+ const output = `${import_chalk.default.yellow(`\u23F3 Waiting ${seconds}s before code execution (Ctrl+C to abort)...`)}`;
466
+ console.log(output);
467
+ this.writeToFile(`\u23F3 Waiting ${seconds}s before code execution...
468
+ `);
469
+ }
470
+ /**
471
+ * Stream content character by character.
472
+ */
473
+ streamChar(char) {
474
+ if (this.level < 1 /* INFO */) return;
475
+ process.stdout.write(import_chalk.default.yellow(char));
476
+ }
477
+ /**
478
+ * End streaming (add newline).
479
+ */
480
+ streamEnd() {
481
+ if (this.level < 1 /* INFO */) return;
482
+ console.log();
483
+ }
484
+ /**
485
+ * Close the log file.
486
+ */
487
+ close() {
488
+ if (this.logFile) {
489
+ this.writeToFile(`
490
+ === Session Ended: ${(/* @__PURE__ */ new Date()).toISOString()} ===
491
+ `);
492
+ this.logFile.end();
493
+ }
494
+ }
495
+ /**
496
+ * Get the log file path.
497
+ */
498
+ getLogPath() {
499
+ return this.logFile ? path.join(LOG_DIR, `${this.sessionId}.log`) : void 0;
500
+ }
501
+ };
502
+
503
+ // src/agents/Agent.ts
504
+ var Agent = class {
505
+ /**
506
+ * The LLM model for generation
507
+ */
508
+ model;
509
+ /**
510
+ * Available tools mapped by name
511
+ */
512
+ tools = /* @__PURE__ */ new Map();
513
+ /**
514
+ * Agent memory tracking all steps
515
+ */
516
+ memory;
517
+ /**
518
+ * Logger for formatted output
519
+ */
520
+ logger;
521
+ /**
522
+ * Configuration options
523
+ */
524
+ config;
525
+ /**
526
+ * Current step number
527
+ */
528
+ currentStep = 0;
529
+ /**
530
+ * Whether the agent is currently running
531
+ */
532
+ isRunning = false;
533
+ constructor(config) {
534
+ this.model = config.model;
535
+ this.logger = new AgentLogger(config.verboseLevel ?? 1 /* INFO */);
536
+ this.config = {
537
+ maxSteps: config.maxSteps ?? 20,
538
+ codeExecutionDelay: config.codeExecutionDelay ?? 5e3,
539
+ customInstructions: config.customInstructions ?? "",
540
+ verboseLevel: config.verboseLevel ?? 1 /* INFO */,
541
+ streamOutputs: config.streamOutputs ?? true
542
+ };
543
+ if (config.tools) {
544
+ for (const tool of config.tools) {
545
+ this.tools.set(tool.name, tool);
546
+ }
547
+ }
548
+ }
549
+ /**
550
+ * Run the agent on a task.
551
+ *
552
+ * @param task - The task description
553
+ * @param reset - Whether to reset memory before running
554
+ * @returns The final result
555
+ */
556
+ async run(task, reset = true) {
557
+ const startTime = Date.now();
558
+ if (reset || !this.memory) {
559
+ const systemPrompt = this.initializeSystemPrompt();
560
+ this.memory = new AgentMemory(systemPrompt);
561
+ this.currentStep = 0;
562
+ }
563
+ this.memory.addTask(task);
564
+ this.isRunning = true;
565
+ this.logger.header(`\u{1F680} Starting Agent: ${task.slice(0, 50)}${task.length > 50 ? "..." : ""}`);
566
+ let finalOutput = null;
567
+ let isFinalAnswer = false;
568
+ try {
569
+ while (this.currentStep < this.config.maxSteps && this.isRunning) {
570
+ this.currentStep++;
571
+ this.logger.stepProgress(this.currentStep, this.config.maxSteps);
572
+ const memoryStep = this.memory.createActionStep(this.currentStep);
573
+ try {
574
+ const actionOutput = await this.executeStep(memoryStep);
575
+ memoryStep.timing.endTime = Date.now();
576
+ memoryStep.timing.duration = memoryStep.timing.endTime - memoryStep.timing.startTime;
577
+ memoryStep.actionOutput = actionOutput;
578
+ memoryStep.isFinalAnswer = actionOutput.isFinalAnswer;
579
+ if (actionOutput.isFinalAnswer) {
580
+ finalOutput = actionOutput.output;
581
+ isFinalAnswer = true;
582
+ this.logger.finalAnswer(finalOutput);
583
+ break;
584
+ }
585
+ } catch (error) {
586
+ memoryStep.error = error;
587
+ memoryStep.timing.endTime = Date.now();
588
+ memoryStep.timing.duration = memoryStep.timing.endTime - memoryStep.timing.startTime;
589
+ this.logger.error("Step execution failed", error);
590
+ }
591
+ }
592
+ if (!isFinalAnswer && this.currentStep >= this.config.maxSteps) {
593
+ this.logger.warn(`Max steps (${this.config.maxSteps}) reached without final answer`);
594
+ finalOutput = await this.provideFinalAnswer(task);
595
+ }
596
+ } finally {
597
+ this.isRunning = false;
598
+ }
599
+ const duration = Date.now() - startTime;
600
+ const tokenUsage = this.memory.getTotalTokenUsage();
601
+ this.memory.addFinalAnswer(finalOutput);
602
+ this.logger.info(`
603
+ \u23F1\uFE0F Total time: ${(duration / 1e3).toFixed(2)}s`);
604
+ this.logger.info(`\u{1F4CA} Total tokens: ${tokenUsage.totalTokens}`);
605
+ const logPath = this.logger.getLogPath();
606
+ if (logPath) {
607
+ this.logger.info(`\u{1F4C1} Log file: ${logPath}`);
608
+ }
609
+ return {
610
+ output: finalOutput,
611
+ steps: this.memory.steps,
612
+ tokenUsage,
613
+ duration
614
+ };
615
+ }
616
+ /**
617
+ * Generate a final answer when max steps is reached.
618
+ */
619
+ async provideFinalAnswer(task) {
620
+ this.logger.subheader("Generating final answer from accumulated context");
621
+ const messages = this.memory.toMessages();
622
+ messages.push({
623
+ role: "user",
624
+ content: `You have reached the maximum number of steps. Based on your work so far, provide the best answer you can for the original task: "${task}"
625
+
626
+ Summarize what you accomplished and provide a final answer. Call final_answer() with your response.`
627
+ });
628
+ const response = await this.model.generate(messages);
629
+ return response.content;
630
+ }
631
+ /**
632
+ * Stop the agent.
633
+ */
634
+ stop() {
635
+ this.isRunning = false;
636
+ this.logger.info("Agent stopped by user");
637
+ }
638
+ /**
639
+ * Get the current memory.
640
+ */
641
+ getMemory() {
642
+ return this.memory;
643
+ }
644
+ /**
645
+ * Get registered tools.
646
+ */
647
+ getTools() {
648
+ return this.tools;
649
+ }
650
+ /**
651
+ * Add a tool to the agent.
652
+ */
653
+ addTool(tool) {
654
+ this.tools.set(tool.name, tool);
655
+ }
656
+ /**
657
+ * Remove a tool from the agent.
658
+ */
659
+ removeTool(name) {
660
+ return this.tools.delete(name);
661
+ }
662
+ /**
663
+ * Sleep for a specified duration.
664
+ */
665
+ sleep(ms) {
666
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
667
+ }
668
+ };
669
+
670
+ // src/executor/LocalExecutor.ts
671
+ var vm = __toESM(require("vm"));
672
+ var fs2 = __toESM(require("fs"));
673
+ var path2 = __toESM(require("path"));
674
+ var os2 = __toESM(require("os"));
675
+ var DEFAULT_TIMEOUT_MS = 3e4;
676
+ var MAX_OUTPUT_LENGTH = 5e4;
677
+ var PACKAGE_CACHE_DIR = path2.join(os2.homedir(), ".smol-js", "packages");
678
+ var LocalExecutor = class {
679
+ context;
680
+ state = {};
681
+ tools = /* @__PURE__ */ new Map();
682
+ config;
683
+ capturedLogs = [];
684
+ constructor(config = {}) {
685
+ this.config = {
686
+ timeout: DEFAULT_TIMEOUT_MS,
687
+ allowFs: true,
688
+ workingDirectory: process.cwd(),
689
+ ...config
690
+ };
691
+ this.context = this.createContext();
692
+ }
693
+ /**
694
+ * Create the VM context with available globals.
695
+ */
696
+ createContext() {
697
+ const consoleProxy = {
698
+ log: (...args) => {
699
+ const output = args.map((arg) => this.stringify(arg)).join(" ");
700
+ this.capturedLogs.push(output);
701
+ },
702
+ error: (...args) => {
703
+ const output = args.map((arg) => this.stringify(arg)).join(" ");
704
+ this.capturedLogs.push(`[ERROR] ${output}`);
705
+ },
706
+ warn: (...args) => {
707
+ const output = args.map((arg) => this.stringify(arg)).join(" ");
708
+ this.capturedLogs.push(`[WARN] ${output}`);
709
+ },
710
+ info: (...args) => {
711
+ const output = args.map((arg) => this.stringify(arg)).join(" ");
712
+ this.capturedLogs.push(output);
713
+ },
714
+ debug: (...args) => {
715
+ const output = args.map((arg) => this.stringify(arg)).join(" ");
716
+ this.capturedLogs.push(`[DEBUG] ${output}`);
717
+ }
718
+ };
719
+ const print = (...args) => consoleProxy.log(...args);
720
+ const dynamicImport = async (packageName) => {
721
+ const authorized = this.config.authorizedImports ?? [];
722
+ const basePackage = packageName.split("/")[0];
723
+ if (!authorized.includes(basePackage) && !authorized.includes(packageName)) {
724
+ throw new Error(
725
+ `Import not authorized: ${packageName}. Add it to authorizedImports to allow.`
726
+ );
727
+ }
728
+ try {
729
+ if (!fs2.existsSync(PACKAGE_CACHE_DIR)) {
730
+ fs2.mkdirSync(PACKAGE_CACHE_DIR, { recursive: true });
731
+ }
732
+ const safeFileName = packageName.replace(/[/@]/g, "_") + ".mjs";
733
+ const cachedPath = path2.join(PACKAGE_CACHE_DIR, safeFileName);
734
+ let needsFetch = !fs2.existsSync(cachedPath);
735
+ if (!needsFetch) {
736
+ const content = fs2.readFileSync(cachedPath, "utf-8");
737
+ if (content.includes('export * from "/') || content.includes("export * from '/")) {
738
+ needsFetch = true;
739
+ fs2.unlinkSync(cachedPath);
740
+ }
741
+ }
742
+ if (needsFetch) {
743
+ this.capturedLogs.push(`[import] Fetching ${packageName}...`);
744
+ const jsdelivrUrl = `https://cdn.jsdelivr.net/npm/${packageName}/+esm`;
745
+ const response = await fetch(jsdelivrUrl);
746
+ if (!response.ok) {
747
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
748
+ }
749
+ let code = await response.text();
750
+ const importMatches = code.matchAll(/from\s+["'](https:\/\/cdn\.jsdelivr\.net\/[^"']+)["']/g);
751
+ for (const match of importMatches) {
752
+ const depUrl = match[1];
753
+ const depName = depUrl.split("/npm/")[1]?.split("/")[0] || "dep";
754
+ const depFileName = depName.replace(/[/@]/g, "_") + "_dep.mjs";
755
+ const depCachedPath = path2.join(PACKAGE_CACHE_DIR, depFileName);
756
+ if (!fs2.existsSync(depCachedPath)) {
757
+ this.capturedLogs.push(`[import] Fetching dependency from ${depUrl}...`);
758
+ const depResponse = await fetch(depUrl);
759
+ if (depResponse.ok) {
760
+ const depCode = await depResponse.text();
761
+ fs2.writeFileSync(depCachedPath, depCode, "utf-8");
762
+ }
763
+ }
764
+ code = code.replace(depUrl, `file://${depCachedPath}`);
765
+ }
766
+ fs2.writeFileSync(cachedPath, code, "utf-8");
767
+ this.capturedLogs.push(`[import] Cached ${packageName} to ${cachedPath}`);
768
+ } else {
769
+ this.capturedLogs.push(`[import] Using cached ${packageName}`);
770
+ }
771
+ const fileUrl = `file://${cachedPath}`;
772
+ const module2 = await import(fileUrl);
773
+ return module2.default ?? module2;
774
+ } catch (error) {
775
+ throw new Error(`Failed to import ${packageName}: ${error.message}`);
776
+ }
777
+ };
778
+ const contextObj = {
779
+ // Console and print
780
+ console: consoleProxy,
781
+ print,
782
+ // Built-in objects
783
+ Object,
784
+ Array,
785
+ String,
786
+ Number,
787
+ Boolean,
788
+ Date,
789
+ Math,
790
+ JSON,
791
+ RegExp,
792
+ Error,
793
+ Map,
794
+ Set,
795
+ WeakMap,
796
+ WeakSet,
797
+ Promise,
798
+ Symbol,
799
+ Proxy,
800
+ Reflect,
801
+ // Type checking
802
+ parseInt,
803
+ parseFloat,
804
+ isNaN,
805
+ isFinite,
806
+ typeof: (v) => typeof v,
807
+ // Timers (promisified for async support)
808
+ setTimeout: global.setTimeout,
809
+ clearTimeout: global.clearTimeout,
810
+ setInterval: global.setInterval,
811
+ clearInterval: global.clearInterval,
812
+ // Async utilities
813
+ fetch: global.fetch,
814
+ // Dynamic import for npm packages
815
+ importPackage: dynamicImport,
816
+ // URL handling
817
+ URL,
818
+ URLSearchParams,
819
+ // Text encoding
820
+ TextEncoder,
821
+ TextDecoder,
822
+ // Buffer (useful for many operations)
823
+ Buffer,
824
+ // State reference (variables persist here)
825
+ __state__: this.state,
826
+ // Final answer marker
827
+ __final_answer__: null,
828
+ __is_final_answer__: false
829
+ };
830
+ if (this.config.allowFs) {
831
+ contextObj.fs = {
832
+ readFileSync: (filePath, encoding) => {
833
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), filePath);
834
+ return fs2.readFileSync(resolvedPath, encoding ?? "utf-8");
835
+ },
836
+ writeFileSync: (filePath, data) => {
837
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), filePath);
838
+ return fs2.writeFileSync(resolvedPath, data);
839
+ },
840
+ existsSync: (filePath) => {
841
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), filePath);
842
+ return fs2.existsSync(resolvedPath);
843
+ },
844
+ readdirSync: (dirPath) => {
845
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), dirPath);
846
+ return fs2.readdirSync(resolvedPath);
847
+ },
848
+ mkdirSync: (dirPath, options) => {
849
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), dirPath);
850
+ return fs2.mkdirSync(resolvedPath, options);
851
+ },
852
+ unlinkSync: (filePath) => {
853
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), filePath);
854
+ return fs2.unlinkSync(resolvedPath);
855
+ },
856
+ statSync: (filePath) => {
857
+ const resolvedPath = path2.resolve(this.config.workingDirectory ?? process.cwd(), filePath);
858
+ return fs2.statSync(resolvedPath);
859
+ }
860
+ };
861
+ contextObj.path = {
862
+ join: path2.join,
863
+ resolve: (...paths) => path2.resolve(this.config.workingDirectory ?? process.cwd(), ...paths),
864
+ dirname: path2.dirname,
865
+ basename: path2.basename,
866
+ extname: path2.extname
867
+ };
868
+ }
869
+ return vm.createContext(contextObj);
870
+ }
871
+ /**
872
+ * Add tools to the executor context.
873
+ */
874
+ sendTools(tools) {
875
+ for (const [name, tool] of Object.entries(tools)) {
876
+ this.tools.set(name, tool);
877
+ this.context[name] = async (...args) => {
878
+ let callArgs;
879
+ if (args.length === 1 && typeof args[0] === "object" && args[0] !== null) {
880
+ callArgs = args[0];
881
+ } else {
882
+ const inputNames = Object.keys(tool.inputs);
883
+ callArgs = {};
884
+ args.forEach((arg, i) => {
885
+ if (i < inputNames.length) {
886
+ callArgs[inputNames[i]] = arg;
887
+ }
888
+ });
889
+ }
890
+ return tool.call(callArgs);
891
+ };
892
+ }
893
+ }
894
+ /**
895
+ * Send variables to the executor state.
896
+ */
897
+ sendVariables(variables) {
898
+ Object.assign(this.state, variables);
899
+ Object.assign(this.context, variables);
900
+ }
901
+ /**
902
+ * Execute JavaScript code and return the result.
903
+ */
904
+ async execute(code) {
905
+ this.capturedLogs = [];
906
+ this.context.__is_final_answer__ = false;
907
+ this.context.__final_answer__ = null;
908
+ Object.assign(this.context, this.state);
909
+ const wrappedCode = this.wrapCode(code);
910
+ try {
911
+ const script = new vm.Script(wrappedCode, {
912
+ filename: "agent-code.js"
913
+ });
914
+ const result = await script.runInContext(this.context, {
915
+ timeout: this.config.timeout,
916
+ displayErrors: true
917
+ });
918
+ const output = result instanceof Promise ? await result : result;
919
+ this.updateStateFromContext();
920
+ const isFinalAnswer = this.context.__is_final_answer__;
921
+ const finalOutput = isFinalAnswer ? this.context.__final_answer__ : output;
922
+ const logs = this.capturedLogs.join("\n").slice(0, MAX_OUTPUT_LENGTH);
923
+ return {
924
+ output: finalOutput,
925
+ logs,
926
+ isFinalAnswer
927
+ };
928
+ } catch (error) {
929
+ const logs = this.capturedLogs.join("\n").slice(0, MAX_OUTPUT_LENGTH);
930
+ return {
931
+ output: null,
932
+ logs,
933
+ isFinalAnswer: false,
934
+ error
935
+ };
936
+ }
937
+ }
938
+ /**
939
+ * Wrap code to handle async execution and final_answer calls.
940
+ */
941
+ wrapCode(code) {
942
+ const finalAnswerFunc = `
943
+ function final_answer(answer) {
944
+ __is_final_answer__ = true;
945
+ __final_answer__ = answer;
946
+ return answer;
947
+ }
948
+ `;
949
+ return `
950
+ ${finalAnswerFunc}
951
+ (async () => {
952
+ let __last_result__;
953
+ ${this.instrumentCode(code)}
954
+ return __last_result__;
955
+ })()
956
+ `;
957
+ }
958
+ /**
959
+ * Instrument code to capture the last expression value and convert
960
+ * let/const/var declarations to global assignments for state persistence.
961
+ */
962
+ instrumentCode(code) {
963
+ const lines = code.trim().split("\n");
964
+ if (lines.length === 0) {
965
+ return code;
966
+ }
967
+ const processedLines = lines.map((line, index) => {
968
+ const trimmed = line.trim();
969
+ if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*") || trimmed.endsWith("*/")) {
970
+ return line;
971
+ }
972
+ let transformed = line;
973
+ let hasDeclaration = false;
974
+ transformed = transformed.replace(
975
+ /\b(let|const|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
976
+ (_match, _keyword, varName) => {
977
+ hasDeclaration = true;
978
+ return `${varName} =`;
979
+ }
980
+ );
981
+ transformed = transformed.replace(
982
+ /\b(let|const|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*(?=[;,]|$)/g,
983
+ (_match, _keyword, varName) => {
984
+ hasDeclaration = true;
985
+ return `${varName} = undefined`;
986
+ }
987
+ );
988
+ if (hasDeclaration) {
989
+ return transformed;
990
+ }
991
+ if (trimmed.startsWith("if") || trimmed.startsWith("else") || trimmed.startsWith("for") || trimmed.startsWith("while") || trimmed.startsWith("do") || trimmed.startsWith("switch") || trimmed.startsWith("case") || trimmed.startsWith("default") || trimmed.startsWith("try") || trimmed.startsWith("catch") || trimmed.startsWith("finally") || trimmed.startsWith("return") || trimmed.startsWith("throw") || trimmed.startsWith("break") || trimmed.startsWith("continue") || trimmed.startsWith("function") || trimmed.startsWith("class") || trimmed.startsWith("import") || trimmed.startsWith("export") || trimmed === "{" || trimmed === "}" || trimmed.endsWith("{") || trimmed.endsWith("}")) {
992
+ return line;
993
+ }
994
+ if (index === lines.length - 1 || this.isLastMeaningfulLine(lines, index)) {
995
+ if (!trimmed.endsWith(";")) {
996
+ return `__last_result__ = ${line}`;
997
+ } else {
998
+ const withoutSemi = trimmed.slice(0, -1);
999
+ if (!withoutSemi.endsWith("}") && !withoutSemi.endsWith(")")) {
1000
+ return `__last_result__ = ${withoutSemi};`;
1001
+ }
1002
+ }
1003
+ }
1004
+ return line;
1005
+ });
1006
+ return processedLines.join("\n");
1007
+ }
1008
+ /**
1009
+ * Check if this is the last meaningful line of code.
1010
+ */
1011
+ isLastMeaningfulLine(lines, currentIndex) {
1012
+ for (let i = currentIndex + 1; i < lines.length; i++) {
1013
+ const trimmed = lines[i].trim();
1014
+ if (trimmed && !trimmed.startsWith("//") && !trimmed.startsWith("/*")) {
1015
+ return false;
1016
+ }
1017
+ }
1018
+ return true;
1019
+ }
1020
+ /**
1021
+ * Update internal state from context after execution.
1022
+ */
1023
+ updateStateFromContext() {
1024
+ const excludeKeys = /* @__PURE__ */ new Set([
1025
+ "console",
1026
+ "print",
1027
+ "Object",
1028
+ "Array",
1029
+ "String",
1030
+ "Number",
1031
+ "Boolean",
1032
+ "Date",
1033
+ "Math",
1034
+ "JSON",
1035
+ "RegExp",
1036
+ "Error",
1037
+ "Map",
1038
+ "Set",
1039
+ "WeakMap",
1040
+ "WeakSet",
1041
+ "Promise",
1042
+ "Symbol",
1043
+ "Proxy",
1044
+ "Reflect",
1045
+ "parseInt",
1046
+ "parseFloat",
1047
+ "isNaN",
1048
+ "isFinite",
1049
+ "typeof",
1050
+ "setTimeout",
1051
+ "clearTimeout",
1052
+ "setInterval",
1053
+ "clearInterval",
1054
+ "fetch",
1055
+ "importPackage",
1056
+ "URL",
1057
+ "URLSearchParams",
1058
+ "TextEncoder",
1059
+ "TextDecoder",
1060
+ "Buffer",
1061
+ "__state__",
1062
+ "__final_answer__",
1063
+ "__is_final_answer__",
1064
+ "fs",
1065
+ "path",
1066
+ "final_answer",
1067
+ // Exclude tools
1068
+ ...this.tools.keys()
1069
+ ]);
1070
+ for (const key of Object.keys(this.context)) {
1071
+ if (!excludeKeys.has(key)) {
1072
+ this.state[key] = this.context[key];
1073
+ }
1074
+ }
1075
+ }
1076
+ /**
1077
+ * Stringify a value for logging.
1078
+ */
1079
+ stringify(value) {
1080
+ if (value === void 0) return "undefined";
1081
+ if (value === null) return "null";
1082
+ if (typeof value === "string") return value;
1083
+ if (typeof value === "function") return `[Function: ${value.name || "anonymous"}]`;
1084
+ try {
1085
+ return JSON.stringify(value, null, 2);
1086
+ } catch {
1087
+ return String(value);
1088
+ }
1089
+ }
1090
+ /**
1091
+ * Reset the executor state.
1092
+ */
1093
+ reset() {
1094
+ this.state = {};
1095
+ this.capturedLogs = [];
1096
+ this.context = this.createContext();
1097
+ const tools = Object.fromEntries(this.tools);
1098
+ this.sendTools(tools);
1099
+ }
1100
+ /**
1101
+ * Get the current state.
1102
+ */
1103
+ getState() {
1104
+ return { ...this.state };
1105
+ }
1106
+ };
1107
+
1108
+ // src/tools/Tool.ts
1109
+ var Tool = class {
1110
+ /**
1111
+ * Whether the tool has been set up
1112
+ */
1113
+ isSetup = false;
1114
+ /**
1115
+ * Optional setup method called before first use.
1116
+ * Override this for expensive initialization (loading models, etc.)
1117
+ */
1118
+ async setup() {
1119
+ this.isSetup = true;
1120
+ }
1121
+ /**
1122
+ * Call the tool, ensuring setup is complete and validating arguments.
1123
+ */
1124
+ async call(args) {
1125
+ if (!this.isSetup) {
1126
+ await this.setup();
1127
+ }
1128
+ this.validateArguments(args);
1129
+ return this.execute(args);
1130
+ }
1131
+ /**
1132
+ * Validate that provided arguments match the input schema.
1133
+ */
1134
+ validateArguments(args) {
1135
+ const providedKeys = new Set(Object.keys(args));
1136
+ for (const [key, input] of Object.entries(this.inputs)) {
1137
+ if (input.required !== false && !providedKeys.has(key)) {
1138
+ throw new Error(`Missing required argument: ${key}`);
1139
+ }
1140
+ if (providedKeys.has(key) && args[key] !== void 0 && args[key] !== null) {
1141
+ const value = args[key];
1142
+ if (!this.checkType(value, input.type)) {
1143
+ throw new Error(
1144
+ `Argument '${key}' has invalid type. Expected ${input.type}, got ${typeof value}`
1145
+ );
1146
+ }
1147
+ }
1148
+ providedKeys.delete(key);
1149
+ }
1150
+ if (providedKeys.size > 0) {
1151
+ console.warn(`Unknown arguments provided to ${this.name}: ${[...providedKeys].join(", ")}`);
1152
+ }
1153
+ }
1154
+ /**
1155
+ * Check if a value matches the expected type.
1156
+ */
1157
+ checkType(value, expectedType) {
1158
+ switch (expectedType) {
1159
+ case "string":
1160
+ return typeof value === "string";
1161
+ case "number":
1162
+ return typeof value === "number";
1163
+ case "boolean":
1164
+ return typeof value === "boolean";
1165
+ case "array":
1166
+ return Array.isArray(value);
1167
+ case "object":
1168
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1169
+ case "any":
1170
+ return true;
1171
+ default:
1172
+ return false;
1173
+ }
1174
+ }
1175
+ /**
1176
+ * Generate a code-friendly prompt representation of this tool.
1177
+ * Used in the CodeAgent system prompt.
1178
+ */
1179
+ toCodePrompt() {
1180
+ const argsSignature = Object.entries(this.inputs).map(([name, input]) => {
1181
+ const optional = input.required === false ? "?" : "";
1182
+ return `${name}${optional}: ${this.typeToJsType(input.type)}`;
1183
+ }).join(", ");
1184
+ const argsDoc = Object.entries(this.inputs).map(([name, input]) => ` * @param ${name} - ${input.description}`).join("\n");
1185
+ return `
1186
+ /**
1187
+ * ${this.description}
1188
+ *
1189
+ ${argsDoc}
1190
+ * @returns ${this.outputType}
1191
+ */
1192
+ async function ${this.name}(${argsSignature}): Promise<${this.typeToJsType(this.outputType)}> { ... }
1193
+ `.trim();
1194
+ }
1195
+ /**
1196
+ * Convert tool input type to JS/TS type string.
1197
+ */
1198
+ typeToJsType(type) {
1199
+ switch (type) {
1200
+ case "string":
1201
+ return "string";
1202
+ case "number":
1203
+ return "number";
1204
+ case "boolean":
1205
+ return "boolean";
1206
+ case "array":
1207
+ return "any[]";
1208
+ case "object":
1209
+ return "Record<string, any>";
1210
+ case "any":
1211
+ return "any";
1212
+ default:
1213
+ return type;
1214
+ }
1215
+ }
1216
+ /**
1217
+ * Serialize the tool to a JSON-compatible object.
1218
+ */
1219
+ toJSON() {
1220
+ return {
1221
+ name: this.name,
1222
+ description: this.description,
1223
+ inputs: this.inputs,
1224
+ outputType: this.outputType
1225
+ };
1226
+ }
1227
+ };
1228
+ function createTool(config) {
1229
+ return new class extends Tool {
1230
+ name = config.name;
1231
+ description = config.description;
1232
+ inputs = config.inputs;
1233
+ outputType = config.outputType;
1234
+ async execute(args) {
1235
+ return config.execute(args);
1236
+ }
1237
+ }();
1238
+ }
1239
+
1240
+ // src/tools/defaultTools.ts
1241
+ var FinalAnswerTool = class extends Tool {
1242
+ name = "final_answer";
1243
+ description = "Returns the final answer to the user query. Use this when you have completed the task and have the final result.";
1244
+ inputs = {
1245
+ answer: {
1246
+ type: "any",
1247
+ description: "The final answer to return. Can be any type (string, number, object, etc.)",
1248
+ required: true
1249
+ }
1250
+ };
1251
+ outputType = "any";
1252
+ async execute(args) {
1253
+ return args.answer;
1254
+ }
1255
+ };
1256
+ var UserInputTool = class extends Tool {
1257
+ name = "user_input";
1258
+ description = "Asks the user for additional input or clarification.";
1259
+ inputs = {
1260
+ question: {
1261
+ type: "string",
1262
+ description: "The question to ask the user",
1263
+ required: true
1264
+ }
1265
+ };
1266
+ outputType = "string";
1267
+ inputHandler;
1268
+ constructor(inputHandler) {
1269
+ super();
1270
+ this.inputHandler = inputHandler;
1271
+ }
1272
+ async execute(args) {
1273
+ const question = args.question;
1274
+ if (this.inputHandler) {
1275
+ return this.inputHandler(question);
1276
+ }
1277
+ const readline = await import("readline");
1278
+ const rl = readline.createInterface({
1279
+ input: process.stdin,
1280
+ output: process.stdout
1281
+ });
1282
+ return new Promise((resolve2) => {
1283
+ rl.question(`
1284
+ [Agent asks]: ${question}
1285
+ Your response: `, (answer) => {
1286
+ rl.close();
1287
+ resolve2(answer);
1288
+ });
1289
+ });
1290
+ }
1291
+ };
1292
+ var finalAnswerTool = new FinalAnswerTool();
1293
+
1294
+ // src/prompts/codeAgent.ts
1295
+ function generateSystemPrompt(variables) {
1296
+ const { tools, authorizedImports, customInstructions } = variables;
1297
+ return `You are an expert JavaScript developer and problem-solving agent. Your role is to solve tasks by writing and executing JavaScript code step by step.
1298
+
1299
+ ## How You Work
1300
+
1301
+ You follow a ReAct (Reasoning + Acting) framework:
1302
+ 1. **Thought**: Analyze the current situation and decide what to do next
1303
+ 2. **Code**: Write JavaScript code to perform the action
1304
+ 3. **Observation**: See the result of your code execution
1305
+ 4. Repeat until you have the final answer
1306
+
1307
+ ## Available Tools
1308
+
1309
+ You have access to the following tools as async functions:
1310
+
1311
+ ${tools}
1312
+
1313
+ ## Available Imports
1314
+
1315
+ You can dynamically import the following npm packages using \`await importPackage('package-name')\`:
1316
+ ${authorizedImports || "(No additional packages authorized)"}
1317
+
1318
+ ## Built-in Capabilities
1319
+
1320
+ The following are available in your execution environment:
1321
+ - \`console.log()\` / \`print()\` - Output text (captured in logs)
1322
+ - \`fs\` - File system operations (readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync)
1323
+ - \`path\` - Path utilities (join, resolve, dirname, basename)
1324
+ - \`fetch()\` - HTTP requests
1325
+ - \`Buffer\` - Binary data handling
1326
+ - \`JSON\` - JSON parsing/stringifying
1327
+ - Standard JavaScript globals (Math, Date, Array methods, etc.)
1328
+
1329
+ ## Response Format
1330
+
1331
+ Always respond with your thought process followed by a code block:
1332
+
1333
+ Thought: [Your reasoning about what to do]
1334
+
1335
+ \`\`\`javascript
1336
+ // Your code here
1337
+ \`\`\`
1338
+
1339
+ ## Rules
1340
+
1341
+ 1. **Always use final_answer()**: When you have the complete answer, call \`final_answer(yourResult)\` to return it.
1342
+
1343
+ 2. **One action per step**: Execute one logical action per code block. Don't try to do everything at once.
1344
+
1345
+ 3. **Handle errors gracefully**: If something fails, explain what went wrong and try a different approach.
1346
+
1347
+ 4. **Use async/await**: All tool calls and imports are async. Always use await.
1348
+
1349
+ 5. **Variables persist**: Variables you define in one step are available in the next step.
1350
+
1351
+ 6. **Be concise**: Write clean, minimal code. Don't over-engineer.
1352
+
1353
+ 7. **Print for debugging**: Use console.log() to output intermediate results you want to see.
1354
+
1355
+ 8. **No require()**: Use \`await importPackage('name')\` for npm packages instead of require().
1356
+
1357
+ ## Examples
1358
+
1359
+ ### Example 1: Simple calculation
1360
+ Thought: I need to calculate the sum of squares from 1 to 10.
1361
+
1362
+ \`\`\`javascript
1363
+ let sum = 0;
1364
+ for (let i = 1; i <= 10; i++) {
1365
+ sum += i * i;
1366
+ }
1367
+ console.log("Sum of squares:", sum);
1368
+ final_answer(sum);
1369
+ \`\`\`
1370
+
1371
+ ### Example 2: Using a tool
1372
+ Thought: I need to search the web for current information.
1373
+
1374
+ \`\`\`javascript
1375
+ const results = await web_search({ query: "latest JavaScript features 2024" });
1376
+ console.log("Search results:", results);
1377
+ \`\`\`
1378
+
1379
+ ### Example 3: Reading a file
1380
+ Thought: I need to read the contents of package.json.
1381
+
1382
+ \`\`\`javascript
1383
+ const content = fs.readFileSync('package.json', 'utf-8');
1384
+ const pkg = JSON.parse(content);
1385
+ console.log("Package name:", pkg.name);
1386
+ final_answer(pkg);
1387
+ \`\`\`
1388
+
1389
+ ### Example 4: Using dynamic imports
1390
+ Thought: I need to use lodash for array manipulation.
1391
+
1392
+ \`\`\`javascript
1393
+ const _ = await importPackage('lodash');
1394
+ const numbers = [1, 2, 3, 4, 5];
1395
+ const chunked = _.chunk(numbers, 2);
1396
+ final_answer(chunked);
1397
+ \`\`\`
1398
+
1399
+ ### Example 5: Multi-step task
1400
+ Thought: First, I'll fetch the data from the API.
1401
+
1402
+ \`\`\`javascript
1403
+ const response = await fetch('https://api.example.com/data');
1404
+ const data = await response.json();
1405
+ console.log("Fetched items:", data.length);
1406
+ \`\`\`
1407
+
1408
+ (Observation: Fetched items: 42)
1409
+
1410
+ Thought: Now I'll process the data and return the result.
1411
+
1412
+ \`\`\`javascript
1413
+ const processed = data.filter(item => item.active).map(item => item.name);
1414
+ final_answer(processed);
1415
+ \`\`\`
1416
+
1417
+ ${customInstructions ? `
1418
+ ## Additional Instructions
1419
+
1420
+ ${customInstructions}` : ""}
1421
+
1422
+ Now, let's solve the task step by step. Remember to always call final_answer() when you have the complete answer.`;
1423
+ }
1424
+ var FINAL_ANSWER_PROMPT = `Based on the steps you've taken so far, provide the best answer you can to the original task.
1425
+ If you couldn't fully complete the task, explain what you accomplished and what remains to be done.
1426
+ Call final_answer() with your response.`;
1427
+ function getErrorRecoveryPrompt(error) {
1428
+ return `Your previous code encountered an error:
1429
+
1430
+ ${error}
1431
+
1432
+ Please analyze the error and try a different approach. Fix the issue and continue working on the task.`;
1433
+ }
1434
+
1435
+ // src/agents/CodeAgent.ts
1436
+ var CODE_BLOCK_REGEX = /```(?:javascript|js)?\n([\s\S]*?)```/;
1437
+ var THOUGHT_REGEX = /(?:Thought|Reasoning):\s*([\s\S]*?)(?=```|$)/i;
1438
+ var CodeAgent = class extends Agent {
1439
+ /**
1440
+ * The JavaScript code executor
1441
+ */
1442
+ executor;
1443
+ /**
1444
+ * Authorized imports for dynamic npm package loading
1445
+ */
1446
+ authorizedImports;
1447
+ constructor(config) {
1448
+ super(config);
1449
+ this.authorizedImports = config.additionalAuthorizedImports ?? [];
1450
+ this.executor = new LocalExecutor({
1451
+ ...config.executorConfig,
1452
+ authorizedImports: this.authorizedImports,
1453
+ workingDirectory: config.workingDirectory
1454
+ });
1455
+ if (!this.tools.has("final_answer")) {
1456
+ this.tools.set("final_answer", new FinalAnswerTool());
1457
+ }
1458
+ this.executor.sendTools(Object.fromEntries(this.tools));
1459
+ }
1460
+ /**
1461
+ * Initialize the system prompt with tool definitions.
1462
+ */
1463
+ initializeSystemPrompt() {
1464
+ const toolDocs = Array.from(this.tools.values()).filter((tool) => tool.name !== "final_answer").map((tool) => tool.toCodePrompt()).join("\n\n");
1465
+ const finalAnswerDoc = `
1466
+ /**
1467
+ * Returns the final answer to the user. Call this when you have completed the task.
1468
+ * @param answer - The final answer (can be any type)
1469
+ */
1470
+ function final_answer(answer: any): void { ... }
1471
+ `.trim();
1472
+ const allTools = toolDocs ? `${toolDocs}
1473
+
1474
+ ${finalAnswerDoc}` : finalAnswerDoc;
1475
+ const importsDoc = this.authorizedImports.length > 0 ? this.authorizedImports.map((pkg) => `- ${pkg}`).join("\n") : "None (use built-in capabilities only)";
1476
+ return generateSystemPrompt({
1477
+ tools: allTools,
1478
+ authorizedImports: importsDoc,
1479
+ customInstructions: this.config.customInstructions
1480
+ });
1481
+ }
1482
+ /**
1483
+ * Execute a single step: get LLM response, extract code, execute it.
1484
+ */
1485
+ async executeStep(memoryStep) {
1486
+ const messages = this.memory.toMessages();
1487
+ memoryStep.modelInputMessages = [...messages];
1488
+ const lastStep = this.memory.getActionSteps().slice(-2)[0];
1489
+ if (lastStep?.error) {
1490
+ messages.push({
1491
+ role: "user",
1492
+ content: getErrorRecoveryPrompt(lastStep.error.message)
1493
+ });
1494
+ }
1495
+ const response = await this.generateResponse(messages);
1496
+ memoryStep.modelOutputMessage = response;
1497
+ memoryStep.tokenUsage = response.tokenUsage;
1498
+ const content = response.content ?? "";
1499
+ const thoughtMatch = content.match(THOUGHT_REGEX);
1500
+ if (thoughtMatch) {
1501
+ this.logger.reasoning(thoughtMatch[1].trim());
1502
+ }
1503
+ const codeMatch = content.match(CODE_BLOCK_REGEX);
1504
+ if (!codeMatch) {
1505
+ this.logger.warn("No code block found in response");
1506
+ memoryStep.observation = "No code block was found in your response. Please provide JavaScript code in a ```javascript code block.";
1507
+ return {
1508
+ output: null,
1509
+ isFinalAnswer: false
1510
+ };
1511
+ }
1512
+ const code = codeMatch[1].trim();
1513
+ memoryStep.codeAction = code;
1514
+ this.logger.code(code);
1515
+ if (this.config.codeExecutionDelay > 0) {
1516
+ this.logger.waiting(this.config.codeExecutionDelay / 1e3);
1517
+ await this.sleep(this.config.codeExecutionDelay);
1518
+ }
1519
+ this.logger.subheader("Executing code...");
1520
+ const result = await this.executor.execute(code);
1521
+ if (result.logs) {
1522
+ this.logger.logs(result.logs);
1523
+ }
1524
+ if (result.error) {
1525
+ this.logger.error("Code execution error", result.error);
1526
+ memoryStep.error = result.error;
1527
+ memoryStep.observation = `Error during code execution:
1528
+ ${result.error.message}`;
1529
+ return {
1530
+ output: null,
1531
+ isFinalAnswer: false
1532
+ };
1533
+ }
1534
+ const outputStr = this.formatOutput(result.output);
1535
+ this.logger.output(outputStr);
1536
+ memoryStep.observation = this.formatObservation(result.logs, outputStr);
1537
+ return {
1538
+ output: result.output,
1539
+ isFinalAnswer: result.isFinalAnswer
1540
+ };
1541
+ }
1542
+ /**
1543
+ * Generate response from the LLM, optionally streaming.
1544
+ */
1545
+ async generateResponse(messages) {
1546
+ if (this.config.streamOutputs && this.model.supportsStreaming() && this.model.generateStream) {
1547
+ this.logger.subheader("Agent thinking...");
1548
+ let fullContent = "";
1549
+ const generator = this.model.generateStream(messages, {
1550
+ stopSequences: ["Observation:", "Observation:\n"]
1551
+ });
1552
+ for await (const chunk of generator) {
1553
+ this.logger.streamChar(chunk);
1554
+ fullContent += chunk;
1555
+ }
1556
+ this.logger.streamEnd();
1557
+ return {
1558
+ role: "assistant",
1559
+ content: fullContent
1560
+ };
1561
+ } else {
1562
+ this.logger.subheader("Agent thinking...");
1563
+ return this.model.generate(messages, {
1564
+ stopSequences: ["Observation:", "Observation:\n"]
1565
+ });
1566
+ }
1567
+ }
1568
+ /**
1569
+ * Format output for display.
1570
+ */
1571
+ formatOutput(output) {
1572
+ if (output === void 0 || output === null) {
1573
+ return "(no output)";
1574
+ }
1575
+ if (typeof output === "string") {
1576
+ return output;
1577
+ }
1578
+ try {
1579
+ return JSON.stringify(output, null, 2);
1580
+ } catch {
1581
+ return String(output);
1582
+ }
1583
+ }
1584
+ /**
1585
+ * Format the observation to send back to the LLM.
1586
+ */
1587
+ formatObservation(logs, output) {
1588
+ const parts = [];
1589
+ if (logs.trim()) {
1590
+ parts.push(`Execution logs:
1591
+ ${logs}`);
1592
+ }
1593
+ parts.push(`Last output:
1594
+ ${output}`);
1595
+ return `Observation:
1596
+ ${parts.join("\n\n")}`;
1597
+ }
1598
+ /**
1599
+ * Reset the agent and executor state.
1600
+ */
1601
+ reset() {
1602
+ this.executor.reset();
1603
+ this.currentStep = 0;
1604
+ }
1605
+ /**
1606
+ * Get the executor instance.
1607
+ */
1608
+ getExecutor() {
1609
+ return this.executor;
1610
+ }
1611
+ /**
1612
+ * Override addTool to also register with executor.
1613
+ */
1614
+ addTool(tool) {
1615
+ super.addTool(tool);
1616
+ this.executor.sendTools({ [tool.name]: tool });
1617
+ }
1618
+ };
1619
+
1620
+ // src/models/Model.ts
1621
+ var Model = class {
1622
+ /**
1623
+ * Check if the model supports streaming.
1624
+ */
1625
+ supportsStreaming() {
1626
+ return typeof this.generateStream === "function";
1627
+ }
1628
+ /**
1629
+ * Extract token usage from a response message.
1630
+ */
1631
+ extractTokenUsage(_response) {
1632
+ return void 0;
1633
+ }
1634
+ /**
1635
+ * Convert messages to the format expected by the model's API.
1636
+ */
1637
+ formatMessages(messages) {
1638
+ return messages.map((msg) => ({
1639
+ role: msg.role,
1640
+ content: msg.content,
1641
+ ...msg.name && { name: msg.name },
1642
+ ...msg.toolCallId && { tool_call_id: msg.toolCallId }
1643
+ }));
1644
+ }
1645
+ };
1646
+
1647
+ // src/models/OpenAIModel.ts
1648
+ var import_openai = __toESM(require("openai"));
1649
+ var DEFAULT_CONFIG = {
1650
+ modelId: "anthropic/claude-sonnet-4.5",
1651
+ baseUrl: "https://openrouter.ai/api/v1",
1652
+ maxTokens: 4096,
1653
+ temperature: 0.7,
1654
+ timeout: 12e4
1655
+ };
1656
+ var OpenAIModel = class extends Model {
1657
+ modelId;
1658
+ client;
1659
+ config;
1660
+ constructor(config = {}) {
1661
+ super();
1662
+ this.config = {
1663
+ ...DEFAULT_CONFIG,
1664
+ ...config
1665
+ };
1666
+ this.modelId = this.config.modelId ?? DEFAULT_CONFIG.modelId;
1667
+ const apiKey = this.config.apiKey ?? process.env.OPENAI_API_KEY ?? process.env.OPENROUTER_API_KEY;
1668
+ if (!apiKey) {
1669
+ throw new Error(
1670
+ "API key is required. Set OPENAI_API_KEY or OPENROUTER_API_KEY environment variable, or pass apiKey in config."
1671
+ );
1672
+ }
1673
+ this.client = new import_openai.default({
1674
+ apiKey,
1675
+ baseURL: this.config.baseUrl,
1676
+ timeout: this.config.timeout,
1677
+ defaultHeaders: this.config.defaultHeaders
1678
+ });
1679
+ }
1680
+ /**
1681
+ * Generate a response from the model.
1682
+ */
1683
+ async generate(messages, options = {}) {
1684
+ const formattedMessages = this.formatMessages(messages);
1685
+ const response = await this.client.chat.completions.create({
1686
+ model: this.modelId,
1687
+ messages: formattedMessages,
1688
+ max_tokens: options.maxTokens ?? this.config.maxTokens,
1689
+ temperature: options.temperature ?? this.config.temperature,
1690
+ ...options.stopSequences && { stop: options.stopSequences }
1691
+ });
1692
+ const choice = response.choices[0];
1693
+ const message = choice?.message;
1694
+ if (!message) {
1695
+ throw new Error("No response from model");
1696
+ }
1697
+ const tokenUsage = response.usage ? {
1698
+ inputTokens: response.usage.prompt_tokens,
1699
+ outputTokens: response.usage.completion_tokens,
1700
+ totalTokens: response.usage.total_tokens
1701
+ } : void 0;
1702
+ return {
1703
+ role: "assistant",
1704
+ content: message.content ?? "",
1705
+ tokenUsage
1706
+ };
1707
+ }
1708
+ /**
1709
+ * Generate a streaming response from the model.
1710
+ */
1711
+ async *generateStream(messages, options = {}) {
1712
+ const formattedMessages = this.formatMessages(messages);
1713
+ const stream = await this.client.chat.completions.create({
1714
+ model: this.modelId,
1715
+ messages: formattedMessages,
1716
+ max_tokens: options.maxTokens ?? this.config.maxTokens,
1717
+ temperature: options.temperature ?? this.config.temperature,
1718
+ ...options.stopSequences && { stop: options.stopSequences },
1719
+ stream: true
1720
+ });
1721
+ let fullContent = "";
1722
+ for await (const chunk of stream) {
1723
+ const delta = chunk.choices[0]?.delta;
1724
+ if (delta?.content) {
1725
+ fullContent += delta.content;
1726
+ yield delta.content;
1727
+ }
1728
+ }
1729
+ return {
1730
+ role: "assistant",
1731
+ content: fullContent
1732
+ };
1733
+ }
1734
+ /**
1735
+ * Format messages for the OpenAI API.
1736
+ */
1737
+ formatMessages(messages) {
1738
+ return messages.map((msg) => {
1739
+ if (msg.role === "tool") {
1740
+ return {
1741
+ role: "user",
1742
+ content: msg.content ?? ""
1743
+ };
1744
+ }
1745
+ return {
1746
+ role: msg.role,
1747
+ content: msg.content ?? ""
1748
+ };
1749
+ });
1750
+ }
1751
+ };
1752
+ // Annotate the CommonJS export names for ESM import in node:
1753
+ 0 && (module.exports = {
1754
+ Agent,
1755
+ AgentLogger,
1756
+ AgentMemory,
1757
+ CodeAgent,
1758
+ FINAL_ANSWER_PROMPT,
1759
+ FinalAnswerTool,
1760
+ LocalExecutor,
1761
+ LogLevel,
1762
+ Model,
1763
+ OpenAIModel,
1764
+ Tool,
1765
+ UserInputTool,
1766
+ createTool,
1767
+ finalAnswerTool,
1768
+ generateSystemPrompt,
1769
+ getErrorRecoveryPrompt
1770
+ });
1771
+ //# sourceMappingURL=index.js.map