@console-agent/agent 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  var google = require('@ai-sdk/google');
4
4
  var ai = require('ai');
5
+ var fs = require('fs');
6
+ var path = require('path');
5
7
  var chalk = require('chalk');
6
8
  var ora = require('ora');
7
9
 
@@ -10,6 +12,13 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
12
  var chalk__default = /*#__PURE__*/_interopDefault(chalk);
11
13
  var ora__default = /*#__PURE__*/_interopDefault(ora);
12
14
 
15
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
16
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
17
+ }) : x)(function(x) {
18
+ if (typeof require !== "undefined") return require.apply(this, arguments);
19
+ throw Error('Dynamic require of "' + x + '" is not supported');
20
+ });
21
+
13
22
  // src/personas/debugger.ts
14
23
  var debuggerPersona = {
15
24
  name: "debugger",
@@ -211,6 +220,16 @@ function getPersona(name) {
211
220
  return personas[name];
212
221
  }
213
222
 
223
+ // src/tools/file-analysis.ts
224
+ function prepareFileContent(fileData, mimeType) {
225
+ const base64 = Buffer.from(fileData).toString("base64");
226
+ return {
227
+ type: "file",
228
+ data: base64,
229
+ mimeType
230
+ };
231
+ }
232
+
214
233
  // src/tools/index.ts
215
234
  var TOOLS_MIN_TIMEOUT = 3e4;
216
235
  function resolveTools(tools, google) {
@@ -235,6 +254,144 @@ function resolveTools(tools, google) {
235
254
  function hasExplicitTools(options) {
236
255
  return !!(options?.tools && options.tools.length > 0);
237
256
  }
257
+ var MAX_FILE_SIZE = 100 * 1024;
258
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
259
+ ".ts",
260
+ ".tsx",
261
+ ".js",
262
+ ".jsx",
263
+ ".mjs",
264
+ ".cjs",
265
+ ".py",
266
+ ".rb",
267
+ ".go",
268
+ ".rs",
269
+ ".java",
270
+ ".kt",
271
+ ".c",
272
+ ".cpp",
273
+ ".h",
274
+ ".hpp",
275
+ ".cs",
276
+ ".swift",
277
+ ".vue",
278
+ ".svelte",
279
+ ".astro",
280
+ ".json",
281
+ ".yaml",
282
+ ".yml",
283
+ ".toml",
284
+ ".env",
285
+ ".sql",
286
+ ".graphql",
287
+ ".gql",
288
+ ".md",
289
+ ".txt",
290
+ ".html",
291
+ ".css",
292
+ ".scss"
293
+ ]);
294
+ var INTERNAL_PATTERNS = [
295
+ "/console-agent/",
296
+ "/console_agent/",
297
+ "@console-agent/",
298
+ "/node_modules/",
299
+ "node:internal/",
300
+ "node:async_hooks",
301
+ "<anonymous>"
302
+ ];
303
+ function normalizeFilePath(filePath) {
304
+ if (filePath.startsWith("file://")) {
305
+ try {
306
+ return new URL(filePath).pathname;
307
+ } catch {
308
+ return filePath.replace(/^file:\/\//, "");
309
+ }
310
+ }
311
+ return filePath;
312
+ }
313
+ function parseStackFrames(stack) {
314
+ const frames = [];
315
+ const lines = stack.split("\n");
316
+ for (const line of lines) {
317
+ const match = line.match(/at\s+(?:.*?\s+)?\(?([^()]+):(\d+):(\d+)\)?/);
318
+ if (match) {
319
+ frames.push({
320
+ file: match[1],
321
+ line: parseInt(match[2], 10),
322
+ column: parseInt(match[3], 10)
323
+ });
324
+ }
325
+ }
326
+ return frames;
327
+ }
328
+ function isInternalFrame(filePath) {
329
+ return INTERNAL_PATTERNS.some((pattern) => filePath.includes(pattern));
330
+ }
331
+ function isSourceFile(filePath) {
332
+ const ext = path.extname(filePath).toLowerCase();
333
+ return SOURCE_EXTENSIONS.has(ext);
334
+ }
335
+ function getCallerFile(skipFrames = 0) {
336
+ const err = new Error();
337
+ if (!err.stack) return null;
338
+ const frames = parseStackFrames(err.stack);
339
+ for (const frame of frames) {
340
+ if (isInternalFrame(frame.file)) continue;
341
+ if (!isSourceFile(frame.file)) continue;
342
+ return readSourceFile(frame.file, frame.line, frame.column);
343
+ }
344
+ return null;
345
+ }
346
+ function getErrorSourceFile(error) {
347
+ if (!error.stack) return null;
348
+ const frames = parseStackFrames(error.stack);
349
+ for (const frame of frames) {
350
+ if (frame.file.includes("node:internal/") || frame.file.includes("<anonymous>")) continue;
351
+ if (!isSourceFile(frame.file)) continue;
352
+ return readSourceFile(frame.file, frame.line, frame.column);
353
+ }
354
+ return null;
355
+ }
356
+ function readSourceFile(filePath, line, column) {
357
+ try {
358
+ const normalizedPath = normalizeFilePath(filePath);
359
+ const resolvedPath = path.resolve(normalizedPath);
360
+ if (!fs.existsSync(resolvedPath)) return null;
361
+ const { statSync } = __require("fs");
362
+ const stats = statSync(resolvedPath);
363
+ if (stats.size > MAX_FILE_SIZE) {
364
+ const content2 = fs.readFileSync(resolvedPath, "utf-8").substring(0, MAX_FILE_SIZE);
365
+ return {
366
+ filePath: resolvedPath,
367
+ fileName: path.basename(resolvedPath),
368
+ line,
369
+ column,
370
+ content: content2 + "\n\n// [TRUNCATED \u2014 file exceeds 100KB limit]"
371
+ };
372
+ }
373
+ const content = fs.readFileSync(resolvedPath, "utf-8");
374
+ return {
375
+ filePath: resolvedPath,
376
+ fileName: path.basename(resolvedPath),
377
+ line,
378
+ column,
379
+ content
380
+ };
381
+ } catch {
382
+ return null;
383
+ }
384
+ }
385
+ function formatSourceForContext(source) {
386
+ const lines = source.content.split("\n");
387
+ const numbered = lines.map((line, i) => {
388
+ const lineNum = i + 1;
389
+ const marker = lineNum === source.line ? " \u2192 " : " ";
390
+ return `${marker}${String(lineNum).padStart(4)} | ${line}`;
391
+ });
392
+ return `--- Source File: ${source.fileName} (line ${source.line}) ---
393
+ ${numbered.join("\n")}`;
394
+ }
238
395
  var currentLogLevel = "info";
239
396
  function setLogLevel(level) {
240
397
  currentLogLevel = level;
@@ -243,8 +400,9 @@ function shouldLog(level) {
243
400
  const levels = ["silent", "errors", "info", "debug"];
244
401
  return levels.indexOf(currentLogLevel) >= levels.indexOf(level);
245
402
  }
246
- function startSpinner(persona, prompt) {
403
+ function startSpinner(persona, prompt, verbose = false) {
247
404
  if (!shouldLog("info")) return null;
405
+ if (!verbose) return null;
248
406
  const truncated = prompt.length > 60 ? prompt.substring(0, 57) + "..." : prompt;
249
407
  const spinner = ora__default.default({
250
408
  text: chalk__default.default.cyan(`${persona.icon} ${persona.label}... `) + chalk__default.default.dim(truncated),
@@ -260,8 +418,29 @@ function stopSpinner(spinner, success) {
260
418
  spinner.fail();
261
419
  }
262
420
  }
263
- function formatResult(result, persona) {
421
+ function formatResult(result, persona, verbose = false) {
264
422
  if (!shouldLog("info")) return;
423
+ if (!verbose) {
424
+ formatResultQuiet(result);
425
+ return;
426
+ }
427
+ formatResultVerbose(result, persona);
428
+ }
429
+ function formatResultQuiet(result) {
430
+ console.log("");
431
+ console.log(result.summary);
432
+ const dataEntries = Object.entries(result.data);
433
+ const hasMeaningfulData = dataEntries.length > 0 && !(dataEntries.length === 1 && dataEntries[0][0] === "raw");
434
+ if (hasMeaningfulData) {
435
+ for (const [key, value] of dataEntries) {
436
+ if (key === "raw") continue;
437
+ const displayValue = typeof value === "string" ? value : JSON.stringify(value);
438
+ console.log(`${chalk__default.default.dim(key + ":")} ${displayValue}`);
439
+ }
440
+ }
441
+ console.log("");
442
+ }
443
+ function formatResultVerbose(result, persona) {
265
444
  const prefix = chalk__default.default.gray("[AGENT]");
266
445
  const confidenceColor = result.confidence >= 0.8 ? chalk__default.default.green : result.confidence >= 0.5 ? chalk__default.default.yellow : chalk__default.default.red;
267
446
  const statusIcon = result.success ? chalk__default.default.green("\u2713") : chalk__default.default.red("\u2717");
@@ -291,12 +470,20 @@ function formatResult(result, persona) {
291
470
  const confidence = confidenceColor(`confidence: ${result.confidence.toFixed(2)}`);
292
471
  const latency = chalk__default.default.dim(`${result.metadata.latencyMs}ms`);
293
472
  const tokens = chalk__default.default.dim(`${result.metadata.tokensUsed} tokens`);
473
+ const model = chalk__default.default.dim(`model: ${result.metadata.model}`);
294
474
  const cached = result.metadata.cached ? chalk__default.default.green(" (cached)") : "";
295
- console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens}${cached}`);
475
+ const toolNames = result.metadata.toolCalls.length > 0 ? chalk__default.default.dim(` | tools: ${result.metadata.toolCalls.map((t) => t.name).join(", ")}`) : "";
476
+ console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens} | ${model}${cached}${toolNames}`);
296
477
  console.log("");
297
478
  }
298
- function formatError(error, persona) {
479
+ function formatError(error, persona, verbose = false) {
299
480
  if (!shouldLog("errors")) return;
481
+ if (!verbose) {
482
+ console.log("");
483
+ console.log(chalk__default.default.red(`Error: ${error.message}`));
484
+ console.log("");
485
+ return;
486
+ }
300
487
  const prefix = chalk__default.default.gray("[AGENT]");
301
488
  console.log("");
302
489
  console.log(`${prefix} ${persona.icon} ${chalk__default.default.red("Error:")} ${error.message}`);
@@ -305,18 +492,32 @@ function formatError(error, persona) {
305
492
  }
306
493
  console.log("");
307
494
  }
308
- function formatBudgetWarning(reason) {
495
+ function formatBudgetWarning(reason, verbose = false) {
309
496
  if (!shouldLog("errors")) return;
497
+ if (!verbose) {
498
+ console.log(chalk__default.default.yellow(`Budget limit: ${reason}`));
499
+ return;
500
+ }
310
501
  const prefix = chalk__default.default.gray("[AGENT]");
311
502
  console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Budget limit:")} ${reason}`);
312
503
  }
313
- function formatRateLimitWarning() {
504
+ function formatRateLimitWarning(verbose = false) {
314
505
  if (!shouldLog("errors")) return;
506
+ if (!verbose) {
507
+ console.log(chalk__default.default.yellow("Rate limited: Too many calls. Try again later."));
508
+ return;
509
+ }
315
510
  const prefix = chalk__default.default.gray("[AGENT]");
316
511
  console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Rate limited:")} Too many calls. Try again later.`);
317
512
  }
318
- function formatDryRun(prompt, persona, context) {
513
+ function formatDryRun(prompt, persona, context, verbose = false) {
319
514
  if (!shouldLog("info")) return;
515
+ if (!verbose) {
516
+ console.log("");
517
+ console.log(chalk__default.default.magenta("[DRY RUN]") + ` Would execute with ${persona.name} persona`);
518
+ console.log("");
519
+ return;
520
+ }
320
521
  const prefix = chalk__default.default.gray("[AGENT]");
321
522
  console.log("");
322
523
  console.log(`${prefix} ${chalk__default.default.magenta("DRY RUN")} ${persona.icon} ${persona.label}`);
@@ -364,7 +565,32 @@ var JSON_RESPONSE_INSTRUCTION = `
364
565
  IMPORTANT: You MUST respond with ONLY a valid JSON object (no markdown, no code fences, no extra text).
365
566
  Use this exact format:
366
567
  {"success": true, "summary": "one-line conclusion", "reasoning": "your thought process", "data": {"result": "primary finding"}, "actions": ["tools/steps used"], "confidence": 0.95}`;
367
- async function callGoogle(prompt, context, persona, config2, options) {
568
+ function buildMessages(prompt, context, sourceFile, files) {
569
+ const parts = [];
570
+ parts.push({ type: "text", text: prompt });
571
+ if (context) {
572
+ parts.push({ type: "text", text: `
573
+ --- Context ---
574
+ ${context}` });
575
+ }
576
+ if (sourceFile) {
577
+ const formatted = formatSourceForContext(sourceFile);
578
+ parts.push({ type: "text", text: `
579
+ ${formatted}` });
580
+ }
581
+ if (files && files.length > 0) {
582
+ for (const file of files) {
583
+ const prepared = prepareFileContent(file.data, file.mediaType);
584
+ if (file.fileName) {
585
+ parts.push({ type: "text", text: `
586
+ --- Attached File: ${file.fileName} ---` });
587
+ }
588
+ parts.push(prepared);
589
+ }
590
+ }
591
+ return [{ role: "user", content: parts }];
592
+ }
593
+ async function callGoogle(prompt, context, persona, config2, options, sourceFile, files) {
368
594
  const startTime = Date.now();
369
595
  const modelName = options?.model ?? config2.model;
370
596
  logDebug(`Using model: ${modelName}`);
@@ -388,24 +614,21 @@ async function callGoogle(prompt, context, persona, config2, options) {
388
614
  const useTools = hasExplicitTools(options) && !config2.localOnly;
389
615
  if (useTools) {
390
616
  logDebug("Tools requested \u2014 using generateText path (no structured output)");
391
- return callWithTools(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions);
617
+ return callWithTools(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions, sourceFile, files);
392
618
  }
393
619
  logDebug("No tools \u2014 using ToolLoopAgent with structured output");
394
- return callWithStructuredOutput(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions);
620
+ return callWithStructuredOutput(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions, sourceFile, files);
395
621
  }
396
- async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
622
+ async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
397
623
  const resolvedTools = resolveTools(options.tools, google);
398
624
  const toolNames = Object.keys(resolvedTools);
399
625
  logDebug(`Tools enabled: ${toolNames.join(", ")}`);
400
626
  const effectiveTimeout = Math.max(config2.timeout, TOOLS_MIN_TIMEOUT);
401
- const userMessage = context ? `${prompt}
402
-
403
- --- Context ---
404
- ${context}` : prompt;
627
+ const messages = buildMessages(prompt, context, sourceFile, files);
405
628
  const result = await ai.generateText({
406
629
  model: google(modelName),
407
630
  system: persona.systemPrompt + JSON_RESPONSE_INSTRUCTION,
408
- prompt: userMessage,
631
+ messages,
409
632
  tools: resolvedTools,
410
633
  stopWhen: ai.stepCountIs(5),
411
634
  // Allow multi-step: tool invocation → response
@@ -449,11 +672,8 @@ ${context}` : prompt;
449
672
  }
450
673
  };
451
674
  }
452
- async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
453
- const userMessage = context ? `${prompt}
454
-
455
- --- Context ---
456
- ${context}` : prompt;
675
+ async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
676
+ const messages = buildMessages(prompt, context, sourceFile, files);
457
677
  const collectedToolCalls = [];
458
678
  const useCustomSchema = !!(options?.schema || options?.responseFormat);
459
679
  let outputConfig;
@@ -492,7 +712,7 @@ IMPORTANT: You must respond with structured data matching the requested output s
492
712
  }
493
713
  });
494
714
  const result = await agent.generate({
495
- prompt: userMessage,
715
+ messages,
496
716
  timeout: config2.timeout
497
717
  });
498
718
  const latencyMs = Date.now() - startTime;
@@ -787,7 +1007,9 @@ var DEFAULT_CONFIG = {
787
1007
  localOnly: false,
788
1008
  dryRun: false,
789
1009
  logLevel: "info",
790
- safetySettings: []
1010
+ verbose: false,
1011
+ safetySettings: [],
1012
+ includeCallerSource: true
791
1013
  };
792
1014
  var config = { ...DEFAULT_CONFIG };
793
1015
  var rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
@@ -806,18 +1028,19 @@ function getConfig() {
806
1028
  async function executeAgent(prompt, context, options) {
807
1029
  const personaName = options?.persona ?? config.persona;
808
1030
  const persona = options?.persona ? getPersona(options.persona) : detectPersona(prompt, personaName);
1031
+ const verbose = options?.verbose ?? config.verbose;
809
1032
  logDebug(`Selected persona: ${persona.name} (${persona.icon})`);
810
1033
  if (config.dryRun) {
811
- formatDryRun(prompt, persona, context);
1034
+ formatDryRun(prompt, persona, context, verbose);
812
1035
  return createDryRunResult(persona.name);
813
1036
  }
814
1037
  if (!rateLimiter.tryConsume()) {
815
- formatRateLimitWarning();
1038
+ formatRateLimitWarning(verbose);
816
1039
  return createErrorResult("Rate limited \u2014 too many calls. Try again later.");
817
1040
  }
818
1041
  const budgetCheck = budgetTracker.canMakeCall();
819
1042
  if (!budgetCheck.allowed) {
820
- formatBudgetWarning(budgetCheck.reason);
1043
+ formatBudgetWarning(budgetCheck.reason, verbose);
821
1044
  return createErrorResult(budgetCheck.reason);
822
1045
  }
823
1046
  let contextStr = "";
@@ -839,10 +1062,27 @@ async function executeAgent(prompt, context, options) {
839
1062
  }
840
1063
  }
841
1064
  const processedPrompt = config.anonymize ? anonymizeValue(prompt) : prompt;
842
- const spinner = startSpinner(persona, processedPrompt);
1065
+ const shouldIncludeSource = options?.includeCallerSource ?? config.includeCallerSource;
1066
+ let sourceFile = null;
1067
+ if (shouldIncludeSource) {
1068
+ if (context instanceof Error) {
1069
+ sourceFile = getErrorSourceFile(context);
1070
+ if (sourceFile) {
1071
+ logDebug(`Auto-detected error source file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1072
+ }
1073
+ }
1074
+ if (!sourceFile) {
1075
+ sourceFile = getCallerFile();
1076
+ if (sourceFile) {
1077
+ logDebug(`Auto-detected caller file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1078
+ }
1079
+ }
1080
+ }
1081
+ const files = options?.files;
1082
+ const spinner = startSpinner(persona, processedPrompt, verbose);
843
1083
  try {
844
1084
  const result = await Promise.race([
845
- callGoogle(processedPrompt, contextStr, persona, config, options),
1085
+ callGoogle(processedPrompt, contextStr, persona, config, options, sourceFile, files),
846
1086
  createTimeout(config.timeout)
847
1087
  ]);
848
1088
  budgetTracker.recordUsage(
@@ -850,12 +1090,12 @@ async function executeAgent(prompt, context, options) {
850
1090
  estimateCost(result.metadata.tokensUsed, result.metadata.model)
851
1091
  );
852
1092
  stopSpinner(spinner, result.success);
853
- formatResult(result, persona);
1093
+ formatResult(result, persona, verbose);
854
1094
  return result;
855
1095
  } catch (error) {
856
1096
  stopSpinner(spinner, false);
857
1097
  const err = error instanceof Error ? error : new Error(String(error));
858
- formatError(err, persona);
1098
+ formatError(err, persona, verbose);
859
1099
  return createErrorResult(err.message);
860
1100
  }
861
1101
  }