@console-agent/agent 1.2.0 → 1.2.1

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,133 @@ 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 parseStackFrames(stack) {
304
+ const frames = [];
305
+ const lines = stack.split("\n");
306
+ for (const line of lines) {
307
+ const match = line.match(/at\s+(?:.*?\s+)?\(?([^()]+):(\d+):(\d+)\)?/);
308
+ if (match) {
309
+ frames.push({
310
+ file: match[1],
311
+ line: parseInt(match[2], 10),
312
+ column: parseInt(match[3], 10)
313
+ });
314
+ }
315
+ }
316
+ return frames;
317
+ }
318
+ function isInternalFrame(filePath) {
319
+ return INTERNAL_PATTERNS.some((pattern) => filePath.includes(pattern));
320
+ }
321
+ function isSourceFile(filePath) {
322
+ const ext = path.extname(filePath).toLowerCase();
323
+ return SOURCE_EXTENSIONS.has(ext);
324
+ }
325
+ function getCallerFile(skipFrames = 0) {
326
+ const err = new Error();
327
+ if (!err.stack) return null;
328
+ const frames = parseStackFrames(err.stack);
329
+ for (const frame of frames) {
330
+ if (isInternalFrame(frame.file)) continue;
331
+ if (!isSourceFile(frame.file)) continue;
332
+ return readSourceFile(frame.file, frame.line, frame.column);
333
+ }
334
+ return null;
335
+ }
336
+ function getErrorSourceFile(error) {
337
+ if (!error.stack) return null;
338
+ const frames = parseStackFrames(error.stack);
339
+ for (const frame of frames) {
340
+ if (frame.file.includes("node:internal/") || frame.file.includes("<anonymous>")) continue;
341
+ if (!isSourceFile(frame.file)) continue;
342
+ return readSourceFile(frame.file, frame.line, frame.column);
343
+ }
344
+ return null;
345
+ }
346
+ function readSourceFile(filePath, line, column) {
347
+ try {
348
+ const resolvedPath = path.resolve(filePath);
349
+ if (!fs.existsSync(resolvedPath)) return null;
350
+ const { statSync } = __require("fs");
351
+ const stats = statSync(resolvedPath);
352
+ if (stats.size > MAX_FILE_SIZE) {
353
+ const content2 = fs.readFileSync(resolvedPath, "utf-8").substring(0, MAX_FILE_SIZE);
354
+ return {
355
+ filePath: resolvedPath,
356
+ fileName: path.basename(resolvedPath),
357
+ line,
358
+ column,
359
+ content: content2 + "\n\n// [TRUNCATED \u2014 file exceeds 100KB limit]"
360
+ };
361
+ }
362
+ const content = fs.readFileSync(resolvedPath, "utf-8");
363
+ return {
364
+ filePath: resolvedPath,
365
+ fileName: path.basename(resolvedPath),
366
+ line,
367
+ column,
368
+ content
369
+ };
370
+ } catch {
371
+ return null;
372
+ }
373
+ }
374
+ function formatSourceForContext(source) {
375
+ const lines = source.content.split("\n");
376
+ const numbered = lines.map((line, i) => {
377
+ const lineNum = i + 1;
378
+ const marker = lineNum === source.line ? " \u2192 " : " ";
379
+ return `${marker}${String(lineNum).padStart(4)} | ${line}`;
380
+ });
381
+ return `--- Source File: ${source.fileName} (line ${source.line}) ---
382
+ ${numbered.join("\n")}`;
383
+ }
238
384
  var currentLogLevel = "info";
239
385
  function setLogLevel(level) {
240
386
  currentLogLevel = level;
@@ -243,8 +389,9 @@ function shouldLog(level) {
243
389
  const levels = ["silent", "errors", "info", "debug"];
244
390
  return levels.indexOf(currentLogLevel) >= levels.indexOf(level);
245
391
  }
246
- function startSpinner(persona, prompt) {
392
+ function startSpinner(persona, prompt, verbose = false) {
247
393
  if (!shouldLog("info")) return null;
394
+ if (!verbose) return null;
248
395
  const truncated = prompt.length > 60 ? prompt.substring(0, 57) + "..." : prompt;
249
396
  const spinner = ora__default.default({
250
397
  text: chalk__default.default.cyan(`${persona.icon} ${persona.label}... `) + chalk__default.default.dim(truncated),
@@ -260,8 +407,29 @@ function stopSpinner(spinner, success) {
260
407
  spinner.fail();
261
408
  }
262
409
  }
263
- function formatResult(result, persona) {
410
+ function formatResult(result, persona, verbose = false) {
264
411
  if (!shouldLog("info")) return;
412
+ if (!verbose) {
413
+ formatResultQuiet(result);
414
+ return;
415
+ }
416
+ formatResultVerbose(result, persona);
417
+ }
418
+ function formatResultQuiet(result) {
419
+ console.log("");
420
+ console.log(result.summary);
421
+ const dataEntries = Object.entries(result.data);
422
+ const hasMeaningfulData = dataEntries.length > 0 && !(dataEntries.length === 1 && dataEntries[0][0] === "raw");
423
+ if (hasMeaningfulData) {
424
+ for (const [key, value] of dataEntries) {
425
+ if (key === "raw") continue;
426
+ const displayValue = typeof value === "string" ? value : JSON.stringify(value);
427
+ console.log(`${chalk__default.default.dim(key + ":")} ${displayValue}`);
428
+ }
429
+ }
430
+ console.log("");
431
+ }
432
+ function formatResultVerbose(result, persona) {
265
433
  const prefix = chalk__default.default.gray("[AGENT]");
266
434
  const confidenceColor = result.confidence >= 0.8 ? chalk__default.default.green : result.confidence >= 0.5 ? chalk__default.default.yellow : chalk__default.default.red;
267
435
  const statusIcon = result.success ? chalk__default.default.green("\u2713") : chalk__default.default.red("\u2717");
@@ -291,12 +459,20 @@ function formatResult(result, persona) {
291
459
  const confidence = confidenceColor(`confidence: ${result.confidence.toFixed(2)}`);
292
460
  const latency = chalk__default.default.dim(`${result.metadata.latencyMs}ms`);
293
461
  const tokens = chalk__default.default.dim(`${result.metadata.tokensUsed} tokens`);
462
+ const model = chalk__default.default.dim(`model: ${result.metadata.model}`);
294
463
  const cached = result.metadata.cached ? chalk__default.default.green(" (cached)") : "";
295
- console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens}${cached}`);
464
+ const toolNames = result.metadata.toolCalls.length > 0 ? chalk__default.default.dim(` | tools: ${result.metadata.toolCalls.map((t) => t.name).join(", ")}`) : "";
465
+ console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens} | ${model}${cached}${toolNames}`);
296
466
  console.log("");
297
467
  }
298
- function formatError(error, persona) {
468
+ function formatError(error, persona, verbose = false) {
299
469
  if (!shouldLog("errors")) return;
470
+ if (!verbose) {
471
+ console.log("");
472
+ console.log(chalk__default.default.red(`Error: ${error.message}`));
473
+ console.log("");
474
+ return;
475
+ }
300
476
  const prefix = chalk__default.default.gray("[AGENT]");
301
477
  console.log("");
302
478
  console.log(`${prefix} ${persona.icon} ${chalk__default.default.red("Error:")} ${error.message}`);
@@ -305,18 +481,32 @@ function formatError(error, persona) {
305
481
  }
306
482
  console.log("");
307
483
  }
308
- function formatBudgetWarning(reason) {
484
+ function formatBudgetWarning(reason, verbose = false) {
309
485
  if (!shouldLog("errors")) return;
486
+ if (!verbose) {
487
+ console.log(chalk__default.default.yellow(`Budget limit: ${reason}`));
488
+ return;
489
+ }
310
490
  const prefix = chalk__default.default.gray("[AGENT]");
311
491
  console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Budget limit:")} ${reason}`);
312
492
  }
313
- function formatRateLimitWarning() {
493
+ function formatRateLimitWarning(verbose = false) {
314
494
  if (!shouldLog("errors")) return;
495
+ if (!verbose) {
496
+ console.log(chalk__default.default.yellow("Rate limited: Too many calls. Try again later."));
497
+ return;
498
+ }
315
499
  const prefix = chalk__default.default.gray("[AGENT]");
316
500
  console.log(`${prefix} ${chalk__default.default.yellow("\u26A0 Rate limited:")} Too many calls. Try again later.`);
317
501
  }
318
- function formatDryRun(prompt, persona, context) {
502
+ function formatDryRun(prompt, persona, context, verbose = false) {
319
503
  if (!shouldLog("info")) return;
504
+ if (!verbose) {
505
+ console.log("");
506
+ console.log(chalk__default.default.magenta("[DRY RUN]") + ` Would execute with ${persona.name} persona`);
507
+ console.log("");
508
+ return;
509
+ }
320
510
  const prefix = chalk__default.default.gray("[AGENT]");
321
511
  console.log("");
322
512
  console.log(`${prefix} ${chalk__default.default.magenta("DRY RUN")} ${persona.icon} ${persona.label}`);
@@ -364,7 +554,32 @@ var JSON_RESPONSE_INSTRUCTION = `
364
554
  IMPORTANT: You MUST respond with ONLY a valid JSON object (no markdown, no code fences, no extra text).
365
555
  Use this exact format:
366
556
  {"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) {
557
+ function buildMessages(prompt, context, sourceFile, files) {
558
+ const parts = [];
559
+ parts.push({ type: "text", text: prompt });
560
+ if (context) {
561
+ parts.push({ type: "text", text: `
562
+ --- Context ---
563
+ ${context}` });
564
+ }
565
+ if (sourceFile) {
566
+ const formatted = formatSourceForContext(sourceFile);
567
+ parts.push({ type: "text", text: `
568
+ ${formatted}` });
569
+ }
570
+ if (files && files.length > 0) {
571
+ for (const file of files) {
572
+ const prepared = prepareFileContent(file.data, file.mediaType);
573
+ if (file.fileName) {
574
+ parts.push({ type: "text", text: `
575
+ --- Attached File: ${file.fileName} ---` });
576
+ }
577
+ parts.push(prepared);
578
+ }
579
+ }
580
+ return [{ role: "user", content: parts }];
581
+ }
582
+ async function callGoogle(prompt, context, persona, config2, options, sourceFile, files) {
368
583
  const startTime = Date.now();
369
584
  const modelName = options?.model ?? config2.model;
370
585
  logDebug(`Using model: ${modelName}`);
@@ -388,24 +603,21 @@ async function callGoogle(prompt, context, persona, config2, options) {
388
603
  const useTools = hasExplicitTools(options) && !config2.localOnly;
389
604
  if (useTools) {
390
605
  logDebug("Tools requested \u2014 using generateText path (no structured output)");
391
- return callWithTools(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions);
606
+ return callWithTools(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions, sourceFile, files);
392
607
  }
393
608
  logDebug("No tools \u2014 using ToolLoopAgent with structured output");
394
- return callWithStructuredOutput(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions);
609
+ return callWithStructuredOutput(prompt, context, persona, config2, options, google$1, modelName, startTime, providerOptions, sourceFile, files);
395
610
  }
396
- async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
611
+ async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
397
612
  const resolvedTools = resolveTools(options.tools, google);
398
613
  const toolNames = Object.keys(resolvedTools);
399
614
  logDebug(`Tools enabled: ${toolNames.join(", ")}`);
400
615
  const effectiveTimeout = Math.max(config2.timeout, TOOLS_MIN_TIMEOUT);
401
- const userMessage = context ? `${prompt}
402
-
403
- --- Context ---
404
- ${context}` : prompt;
616
+ const messages = buildMessages(prompt, context, sourceFile, files);
405
617
  const result = await ai.generateText({
406
618
  model: google(modelName),
407
619
  system: persona.systemPrompt + JSON_RESPONSE_INSTRUCTION,
408
- prompt: userMessage,
620
+ messages,
409
621
  tools: resolvedTools,
410
622
  stopWhen: ai.stepCountIs(5),
411
623
  // Allow multi-step: tool invocation → response
@@ -449,11 +661,8 @@ ${context}` : prompt;
449
661
  }
450
662
  };
451
663
  }
452
- async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
453
- const userMessage = context ? `${prompt}
454
-
455
- --- Context ---
456
- ${context}` : prompt;
664
+ async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
665
+ const messages = buildMessages(prompt, context, sourceFile, files);
457
666
  const collectedToolCalls = [];
458
667
  const useCustomSchema = !!(options?.schema || options?.responseFormat);
459
668
  let outputConfig;
@@ -492,7 +701,7 @@ IMPORTANT: You must respond with structured data matching the requested output s
492
701
  }
493
702
  });
494
703
  const result = await agent.generate({
495
- prompt: userMessage,
704
+ messages,
496
705
  timeout: config2.timeout
497
706
  });
498
707
  const latencyMs = Date.now() - startTime;
@@ -787,7 +996,9 @@ var DEFAULT_CONFIG = {
787
996
  localOnly: false,
788
997
  dryRun: false,
789
998
  logLevel: "info",
790
- safetySettings: []
999
+ verbose: false,
1000
+ safetySettings: [],
1001
+ includeCallerSource: true
791
1002
  };
792
1003
  var config = { ...DEFAULT_CONFIG };
793
1004
  var rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
@@ -806,18 +1017,19 @@ function getConfig() {
806
1017
  async function executeAgent(prompt, context, options) {
807
1018
  const personaName = options?.persona ?? config.persona;
808
1019
  const persona = options?.persona ? getPersona(options.persona) : detectPersona(prompt, personaName);
1020
+ const verbose = options?.verbose ?? config.verbose;
809
1021
  logDebug(`Selected persona: ${persona.name} (${persona.icon})`);
810
1022
  if (config.dryRun) {
811
- formatDryRun(prompt, persona, context);
1023
+ formatDryRun(prompt, persona, context, verbose);
812
1024
  return createDryRunResult(persona.name);
813
1025
  }
814
1026
  if (!rateLimiter.tryConsume()) {
815
- formatRateLimitWarning();
1027
+ formatRateLimitWarning(verbose);
816
1028
  return createErrorResult("Rate limited \u2014 too many calls. Try again later.");
817
1029
  }
818
1030
  const budgetCheck = budgetTracker.canMakeCall();
819
1031
  if (!budgetCheck.allowed) {
820
- formatBudgetWarning(budgetCheck.reason);
1032
+ formatBudgetWarning(budgetCheck.reason, verbose);
821
1033
  return createErrorResult(budgetCheck.reason);
822
1034
  }
823
1035
  let contextStr = "";
@@ -839,10 +1051,27 @@ async function executeAgent(prompt, context, options) {
839
1051
  }
840
1052
  }
841
1053
  const processedPrompt = config.anonymize ? anonymizeValue(prompt) : prompt;
842
- const spinner = startSpinner(persona, processedPrompt);
1054
+ const shouldIncludeSource = options?.includeCallerSource ?? config.includeCallerSource;
1055
+ let sourceFile = null;
1056
+ if (shouldIncludeSource) {
1057
+ if (context instanceof Error) {
1058
+ sourceFile = getErrorSourceFile(context);
1059
+ if (sourceFile) {
1060
+ logDebug(`Auto-detected error source file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1061
+ }
1062
+ }
1063
+ if (!sourceFile) {
1064
+ sourceFile = getCallerFile();
1065
+ if (sourceFile) {
1066
+ logDebug(`Auto-detected caller file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1067
+ }
1068
+ }
1069
+ }
1070
+ const files = options?.files;
1071
+ const spinner = startSpinner(persona, processedPrompt, verbose);
843
1072
  try {
844
1073
  const result = await Promise.race([
845
- callGoogle(processedPrompt, contextStr, persona, config, options),
1074
+ callGoogle(processedPrompt, contextStr, persona, config, options, sourceFile, files),
846
1075
  createTimeout(config.timeout)
847
1076
  ]);
848
1077
  budgetTracker.recordUsage(
@@ -850,12 +1079,12 @@ async function executeAgent(prompt, context, options) {
850
1079
  estimateCost(result.metadata.tokensUsed, result.metadata.model)
851
1080
  );
852
1081
  stopSpinner(spinner, result.success);
853
- formatResult(result, persona);
1082
+ formatResult(result, persona, verbose);
854
1083
  return result;
855
1084
  } catch (error) {
856
1085
  stopSpinner(spinner, false);
857
1086
  const err = error instanceof Error ? error : new Error(String(error));
858
- formatError(err, persona);
1087
+ formatError(err, persona, verbose);
859
1088
  return createErrorResult(err.message);
860
1089
  }
861
1090
  }