@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.js CHANGED
@@ -1,8 +1,17 @@
1
1
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
2
2
  import { jsonSchema, generateText, stepCountIs, Output, ToolLoopAgent } from 'ai';
3
+ import { existsSync, readFileSync } from 'fs';
4
+ import { extname, resolve, basename } from 'path';
3
5
  import chalk from 'chalk';
4
6
  import ora from 'ora';
5
7
 
8
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
+ }) : x)(function(x) {
11
+ if (typeof require !== "undefined") return require.apply(this, arguments);
12
+ throw Error('Dynamic require of "' + x + '" is not supported');
13
+ });
14
+
6
15
  // src/personas/debugger.ts
7
16
  var debuggerPersona = {
8
17
  name: "debugger",
@@ -204,6 +213,16 @@ function getPersona(name) {
204
213
  return personas[name];
205
214
  }
206
215
 
216
+ // src/tools/file-analysis.ts
217
+ function prepareFileContent(fileData, mimeType) {
218
+ const base64 = Buffer.from(fileData).toString("base64");
219
+ return {
220
+ type: "file",
221
+ data: base64,
222
+ mimeType
223
+ };
224
+ }
225
+
207
226
  // src/tools/index.ts
208
227
  var TOOLS_MIN_TIMEOUT = 3e4;
209
228
  function resolveTools(tools, google) {
@@ -228,6 +247,144 @@ function resolveTools(tools, google) {
228
247
  function hasExplicitTools(options) {
229
248
  return !!(options?.tools && options.tools.length > 0);
230
249
  }
250
+ var MAX_FILE_SIZE = 100 * 1024;
251
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
252
+ ".ts",
253
+ ".tsx",
254
+ ".js",
255
+ ".jsx",
256
+ ".mjs",
257
+ ".cjs",
258
+ ".py",
259
+ ".rb",
260
+ ".go",
261
+ ".rs",
262
+ ".java",
263
+ ".kt",
264
+ ".c",
265
+ ".cpp",
266
+ ".h",
267
+ ".hpp",
268
+ ".cs",
269
+ ".swift",
270
+ ".vue",
271
+ ".svelte",
272
+ ".astro",
273
+ ".json",
274
+ ".yaml",
275
+ ".yml",
276
+ ".toml",
277
+ ".env",
278
+ ".sql",
279
+ ".graphql",
280
+ ".gql",
281
+ ".md",
282
+ ".txt",
283
+ ".html",
284
+ ".css",
285
+ ".scss"
286
+ ]);
287
+ var INTERNAL_PATTERNS = [
288
+ "/console-agent/",
289
+ "/console_agent/",
290
+ "@console-agent/",
291
+ "/node_modules/",
292
+ "node:internal/",
293
+ "node:async_hooks",
294
+ "<anonymous>"
295
+ ];
296
+ function normalizeFilePath(filePath) {
297
+ if (filePath.startsWith("file://")) {
298
+ try {
299
+ return new URL(filePath).pathname;
300
+ } catch {
301
+ return filePath.replace(/^file:\/\//, "");
302
+ }
303
+ }
304
+ return filePath;
305
+ }
306
+ function parseStackFrames(stack) {
307
+ const frames = [];
308
+ const lines = stack.split("\n");
309
+ for (const line of lines) {
310
+ const match = line.match(/at\s+(?:.*?\s+)?\(?([^()]+):(\d+):(\d+)\)?/);
311
+ if (match) {
312
+ frames.push({
313
+ file: match[1],
314
+ line: parseInt(match[2], 10),
315
+ column: parseInt(match[3], 10)
316
+ });
317
+ }
318
+ }
319
+ return frames;
320
+ }
321
+ function isInternalFrame(filePath) {
322
+ return INTERNAL_PATTERNS.some((pattern) => filePath.includes(pattern));
323
+ }
324
+ function isSourceFile(filePath) {
325
+ const ext = extname(filePath).toLowerCase();
326
+ return SOURCE_EXTENSIONS.has(ext);
327
+ }
328
+ function getCallerFile(skipFrames = 0) {
329
+ const err = new Error();
330
+ if (!err.stack) return null;
331
+ const frames = parseStackFrames(err.stack);
332
+ for (const frame of frames) {
333
+ if (isInternalFrame(frame.file)) continue;
334
+ if (!isSourceFile(frame.file)) continue;
335
+ return readSourceFile(frame.file, frame.line, frame.column);
336
+ }
337
+ return null;
338
+ }
339
+ function getErrorSourceFile(error) {
340
+ if (!error.stack) return null;
341
+ const frames = parseStackFrames(error.stack);
342
+ for (const frame of frames) {
343
+ if (frame.file.includes("node:internal/") || frame.file.includes("<anonymous>")) continue;
344
+ if (!isSourceFile(frame.file)) continue;
345
+ return readSourceFile(frame.file, frame.line, frame.column);
346
+ }
347
+ return null;
348
+ }
349
+ function readSourceFile(filePath, line, column) {
350
+ try {
351
+ const normalizedPath = normalizeFilePath(filePath);
352
+ const resolvedPath = resolve(normalizedPath);
353
+ if (!existsSync(resolvedPath)) return null;
354
+ const { statSync } = __require("fs");
355
+ const stats = statSync(resolvedPath);
356
+ if (stats.size > MAX_FILE_SIZE) {
357
+ const content2 = readFileSync(resolvedPath, "utf-8").substring(0, MAX_FILE_SIZE);
358
+ return {
359
+ filePath: resolvedPath,
360
+ fileName: basename(resolvedPath),
361
+ line,
362
+ column,
363
+ content: content2 + "\n\n// [TRUNCATED \u2014 file exceeds 100KB limit]"
364
+ };
365
+ }
366
+ const content = readFileSync(resolvedPath, "utf-8");
367
+ return {
368
+ filePath: resolvedPath,
369
+ fileName: basename(resolvedPath),
370
+ line,
371
+ column,
372
+ content
373
+ };
374
+ } catch {
375
+ return null;
376
+ }
377
+ }
378
+ function formatSourceForContext(source) {
379
+ const lines = source.content.split("\n");
380
+ const numbered = lines.map((line, i) => {
381
+ const lineNum = i + 1;
382
+ const marker = lineNum === source.line ? " \u2192 " : " ";
383
+ return `${marker}${String(lineNum).padStart(4)} | ${line}`;
384
+ });
385
+ return `--- Source File: ${source.fileName} (line ${source.line}) ---
386
+ ${numbered.join("\n")}`;
387
+ }
231
388
  var currentLogLevel = "info";
232
389
  function setLogLevel(level) {
233
390
  currentLogLevel = level;
@@ -236,8 +393,9 @@ function shouldLog(level) {
236
393
  const levels = ["silent", "errors", "info", "debug"];
237
394
  return levels.indexOf(currentLogLevel) >= levels.indexOf(level);
238
395
  }
239
- function startSpinner(persona, prompt) {
396
+ function startSpinner(persona, prompt, verbose = false) {
240
397
  if (!shouldLog("info")) return null;
398
+ if (!verbose) return null;
241
399
  const truncated = prompt.length > 60 ? prompt.substring(0, 57) + "..." : prompt;
242
400
  const spinner = ora({
243
401
  text: chalk.cyan(`${persona.icon} ${persona.label}... `) + chalk.dim(truncated),
@@ -253,8 +411,29 @@ function stopSpinner(spinner, success) {
253
411
  spinner.fail();
254
412
  }
255
413
  }
256
- function formatResult(result, persona) {
414
+ function formatResult(result, persona, verbose = false) {
257
415
  if (!shouldLog("info")) return;
416
+ if (!verbose) {
417
+ formatResultQuiet(result);
418
+ return;
419
+ }
420
+ formatResultVerbose(result, persona);
421
+ }
422
+ function formatResultQuiet(result) {
423
+ console.log("");
424
+ console.log(result.summary);
425
+ const dataEntries = Object.entries(result.data);
426
+ const hasMeaningfulData = dataEntries.length > 0 && !(dataEntries.length === 1 && dataEntries[0][0] === "raw");
427
+ if (hasMeaningfulData) {
428
+ for (const [key, value] of dataEntries) {
429
+ if (key === "raw") continue;
430
+ const displayValue = typeof value === "string" ? value : JSON.stringify(value);
431
+ console.log(`${chalk.dim(key + ":")} ${displayValue}`);
432
+ }
433
+ }
434
+ console.log("");
435
+ }
436
+ function formatResultVerbose(result, persona) {
258
437
  const prefix = chalk.gray("[AGENT]");
259
438
  const confidenceColor = result.confidence >= 0.8 ? chalk.green : result.confidence >= 0.5 ? chalk.yellow : chalk.red;
260
439
  const statusIcon = result.success ? chalk.green("\u2713") : chalk.red("\u2717");
@@ -284,12 +463,20 @@ function formatResult(result, persona) {
284
463
  const confidence = confidenceColor(`confidence: ${result.confidence.toFixed(2)}`);
285
464
  const latency = chalk.dim(`${result.metadata.latencyMs}ms`);
286
465
  const tokens = chalk.dim(`${result.metadata.tokensUsed} tokens`);
466
+ const model = chalk.dim(`model: ${result.metadata.model}`);
287
467
  const cached = result.metadata.cached ? chalk.green(" (cached)") : "";
288
- console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens}${cached}`);
468
+ const toolNames = result.metadata.toolCalls.length > 0 ? chalk.dim(` | tools: ${result.metadata.toolCalls.map((t) => t.name).join(", ")}`) : "";
469
+ console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens} | ${model}${cached}${toolNames}`);
289
470
  console.log("");
290
471
  }
291
- function formatError(error, persona) {
472
+ function formatError(error, persona, verbose = false) {
292
473
  if (!shouldLog("errors")) return;
474
+ if (!verbose) {
475
+ console.log("");
476
+ console.log(chalk.red(`Error: ${error.message}`));
477
+ console.log("");
478
+ return;
479
+ }
293
480
  const prefix = chalk.gray("[AGENT]");
294
481
  console.log("");
295
482
  console.log(`${prefix} ${persona.icon} ${chalk.red("Error:")} ${error.message}`);
@@ -298,18 +485,32 @@ function formatError(error, persona) {
298
485
  }
299
486
  console.log("");
300
487
  }
301
- function formatBudgetWarning(reason) {
488
+ function formatBudgetWarning(reason, verbose = false) {
302
489
  if (!shouldLog("errors")) return;
490
+ if (!verbose) {
491
+ console.log(chalk.yellow(`Budget limit: ${reason}`));
492
+ return;
493
+ }
303
494
  const prefix = chalk.gray("[AGENT]");
304
495
  console.log(`${prefix} ${chalk.yellow("\u26A0 Budget limit:")} ${reason}`);
305
496
  }
306
- function formatRateLimitWarning() {
497
+ function formatRateLimitWarning(verbose = false) {
307
498
  if (!shouldLog("errors")) return;
499
+ if (!verbose) {
500
+ console.log(chalk.yellow("Rate limited: Too many calls. Try again later."));
501
+ return;
502
+ }
308
503
  const prefix = chalk.gray("[AGENT]");
309
504
  console.log(`${prefix} ${chalk.yellow("\u26A0 Rate limited:")} Too many calls. Try again later.`);
310
505
  }
311
- function formatDryRun(prompt, persona, context) {
506
+ function formatDryRun(prompt, persona, context, verbose = false) {
312
507
  if (!shouldLog("info")) return;
508
+ if (!verbose) {
509
+ console.log("");
510
+ console.log(chalk.magenta("[DRY RUN]") + ` Would execute with ${persona.name} persona`);
511
+ console.log("");
512
+ return;
513
+ }
313
514
  const prefix = chalk.gray("[AGENT]");
314
515
  console.log("");
315
516
  console.log(`${prefix} ${chalk.magenta("DRY RUN")} ${persona.icon} ${persona.label}`);
@@ -357,7 +558,32 @@ var JSON_RESPONSE_INSTRUCTION = `
357
558
  IMPORTANT: You MUST respond with ONLY a valid JSON object (no markdown, no code fences, no extra text).
358
559
  Use this exact format:
359
560
  {"success": true, "summary": "one-line conclusion", "reasoning": "your thought process", "data": {"result": "primary finding"}, "actions": ["tools/steps used"], "confidence": 0.95}`;
360
- async function callGoogle(prompt, context, persona, config2, options) {
561
+ function buildMessages(prompt, context, sourceFile, files) {
562
+ const parts = [];
563
+ parts.push({ type: "text", text: prompt });
564
+ if (context) {
565
+ parts.push({ type: "text", text: `
566
+ --- Context ---
567
+ ${context}` });
568
+ }
569
+ if (sourceFile) {
570
+ const formatted = formatSourceForContext(sourceFile);
571
+ parts.push({ type: "text", text: `
572
+ ${formatted}` });
573
+ }
574
+ if (files && files.length > 0) {
575
+ for (const file of files) {
576
+ const prepared = prepareFileContent(file.data, file.mediaType);
577
+ if (file.fileName) {
578
+ parts.push({ type: "text", text: `
579
+ --- Attached File: ${file.fileName} ---` });
580
+ }
581
+ parts.push(prepared);
582
+ }
583
+ }
584
+ return [{ role: "user", content: parts }];
585
+ }
586
+ async function callGoogle(prompt, context, persona, config2, options, sourceFile, files) {
361
587
  const startTime = Date.now();
362
588
  const modelName = options?.model ?? config2.model;
363
589
  logDebug(`Using model: ${modelName}`);
@@ -381,24 +607,21 @@ async function callGoogle(prompt, context, persona, config2, options) {
381
607
  const useTools = hasExplicitTools(options) && !config2.localOnly;
382
608
  if (useTools) {
383
609
  logDebug("Tools requested \u2014 using generateText path (no structured output)");
384
- return callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions);
610
+ return callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files);
385
611
  }
386
612
  logDebug("No tools \u2014 using ToolLoopAgent with structured output");
387
- return callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions);
613
+ return callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files);
388
614
  }
389
- async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
615
+ async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
390
616
  const resolvedTools = resolveTools(options.tools, google);
391
617
  const toolNames = Object.keys(resolvedTools);
392
618
  logDebug(`Tools enabled: ${toolNames.join(", ")}`);
393
619
  const effectiveTimeout = Math.max(config2.timeout, TOOLS_MIN_TIMEOUT);
394
- const userMessage = context ? `${prompt}
395
-
396
- --- Context ---
397
- ${context}` : prompt;
620
+ const messages = buildMessages(prompt, context, sourceFile, files);
398
621
  const result = await generateText({
399
622
  model: google(modelName),
400
623
  system: persona.systemPrompt + JSON_RESPONSE_INSTRUCTION,
401
- prompt: userMessage,
624
+ messages,
402
625
  tools: resolvedTools,
403
626
  stopWhen: stepCountIs(5),
404
627
  // Allow multi-step: tool invocation → response
@@ -442,11 +665,8 @@ ${context}` : prompt;
442
665
  }
443
666
  };
444
667
  }
445
- async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
446
- const userMessage = context ? `${prompt}
447
-
448
- --- Context ---
449
- ${context}` : prompt;
668
+ async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
669
+ const messages = buildMessages(prompt, context, sourceFile, files);
450
670
  const collectedToolCalls = [];
451
671
  const useCustomSchema = !!(options?.schema || options?.responseFormat);
452
672
  let outputConfig;
@@ -485,7 +705,7 @@ IMPORTANT: You must respond with structured data matching the requested output s
485
705
  }
486
706
  });
487
707
  const result = await agent.generate({
488
- prompt: userMessage,
708
+ messages,
489
709
  timeout: config2.timeout
490
710
  });
491
711
  const latencyMs = Date.now() - startTime;
@@ -780,7 +1000,9 @@ var DEFAULT_CONFIG = {
780
1000
  localOnly: false,
781
1001
  dryRun: false,
782
1002
  logLevel: "info",
783
- safetySettings: []
1003
+ verbose: false,
1004
+ safetySettings: [],
1005
+ includeCallerSource: true
784
1006
  };
785
1007
  var config = { ...DEFAULT_CONFIG };
786
1008
  var rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
@@ -799,18 +1021,19 @@ function getConfig() {
799
1021
  async function executeAgent(prompt, context, options) {
800
1022
  const personaName = options?.persona ?? config.persona;
801
1023
  const persona = options?.persona ? getPersona(options.persona) : detectPersona(prompt, personaName);
1024
+ const verbose = options?.verbose ?? config.verbose;
802
1025
  logDebug(`Selected persona: ${persona.name} (${persona.icon})`);
803
1026
  if (config.dryRun) {
804
- formatDryRun(prompt, persona, context);
1027
+ formatDryRun(prompt, persona, context, verbose);
805
1028
  return createDryRunResult(persona.name);
806
1029
  }
807
1030
  if (!rateLimiter.tryConsume()) {
808
- formatRateLimitWarning();
1031
+ formatRateLimitWarning(verbose);
809
1032
  return createErrorResult("Rate limited \u2014 too many calls. Try again later.");
810
1033
  }
811
1034
  const budgetCheck = budgetTracker.canMakeCall();
812
1035
  if (!budgetCheck.allowed) {
813
- formatBudgetWarning(budgetCheck.reason);
1036
+ formatBudgetWarning(budgetCheck.reason, verbose);
814
1037
  return createErrorResult(budgetCheck.reason);
815
1038
  }
816
1039
  let contextStr = "";
@@ -832,10 +1055,27 @@ async function executeAgent(prompt, context, options) {
832
1055
  }
833
1056
  }
834
1057
  const processedPrompt = config.anonymize ? anonymizeValue(prompt) : prompt;
835
- const spinner = startSpinner(persona, processedPrompt);
1058
+ const shouldIncludeSource = options?.includeCallerSource ?? config.includeCallerSource;
1059
+ let sourceFile = null;
1060
+ if (shouldIncludeSource) {
1061
+ if (context instanceof Error) {
1062
+ sourceFile = getErrorSourceFile(context);
1063
+ if (sourceFile) {
1064
+ logDebug(`Auto-detected error source file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1065
+ }
1066
+ }
1067
+ if (!sourceFile) {
1068
+ sourceFile = getCallerFile();
1069
+ if (sourceFile) {
1070
+ logDebug(`Auto-detected caller file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1071
+ }
1072
+ }
1073
+ }
1074
+ const files = options?.files;
1075
+ const spinner = startSpinner(persona, processedPrompt, verbose);
836
1076
  try {
837
1077
  const result = await Promise.race([
838
- callGoogle(processedPrompt, contextStr, persona, config, options),
1078
+ callGoogle(processedPrompt, contextStr, persona, config, options, sourceFile, files),
839
1079
  createTimeout(config.timeout)
840
1080
  ]);
841
1081
  budgetTracker.recordUsage(
@@ -843,12 +1083,12 @@ async function executeAgent(prompt, context, options) {
843
1083
  estimateCost(result.metadata.tokensUsed, result.metadata.model)
844
1084
  );
845
1085
  stopSpinner(spinner, result.success);
846
- formatResult(result, persona);
1086
+ formatResult(result, persona, verbose);
847
1087
  return result;
848
1088
  } catch (error) {
849
1089
  stopSpinner(spinner, false);
850
1090
  const err = error instanceof Error ? error : new Error(String(error));
851
- formatError(err, persona);
1091
+ formatError(err, persona, verbose);
852
1092
  return createErrorResult(err.message);
853
1093
  }
854
1094
  }