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