@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.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,133 @@ 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 parseStackFrames(stack) {
297
+ const frames = [];
298
+ const lines = stack.split("\n");
299
+ for (const line of lines) {
300
+ const match = line.match(/at\s+(?:.*?\s+)?\(?([^()]+):(\d+):(\d+)\)?/);
301
+ if (match) {
302
+ frames.push({
303
+ file: match[1],
304
+ line: parseInt(match[2], 10),
305
+ column: parseInt(match[3], 10)
306
+ });
307
+ }
308
+ }
309
+ return frames;
310
+ }
311
+ function isInternalFrame(filePath) {
312
+ return INTERNAL_PATTERNS.some((pattern) => filePath.includes(pattern));
313
+ }
314
+ function isSourceFile(filePath) {
315
+ const ext = extname(filePath).toLowerCase();
316
+ return SOURCE_EXTENSIONS.has(ext);
317
+ }
318
+ function getCallerFile(skipFrames = 0) {
319
+ const err = new Error();
320
+ if (!err.stack) return null;
321
+ const frames = parseStackFrames(err.stack);
322
+ for (const frame of frames) {
323
+ if (isInternalFrame(frame.file)) continue;
324
+ if (!isSourceFile(frame.file)) continue;
325
+ return readSourceFile(frame.file, frame.line, frame.column);
326
+ }
327
+ return null;
328
+ }
329
+ function getErrorSourceFile(error) {
330
+ if (!error.stack) return null;
331
+ const frames = parseStackFrames(error.stack);
332
+ for (const frame of frames) {
333
+ if (frame.file.includes("node:internal/") || frame.file.includes("<anonymous>")) continue;
334
+ if (!isSourceFile(frame.file)) continue;
335
+ return readSourceFile(frame.file, frame.line, frame.column);
336
+ }
337
+ return null;
338
+ }
339
+ function readSourceFile(filePath, line, column) {
340
+ try {
341
+ const resolvedPath = resolve(filePath);
342
+ if (!existsSync(resolvedPath)) return null;
343
+ const { statSync } = __require("fs");
344
+ const stats = statSync(resolvedPath);
345
+ if (stats.size > MAX_FILE_SIZE) {
346
+ const content2 = readFileSync(resolvedPath, "utf-8").substring(0, MAX_FILE_SIZE);
347
+ return {
348
+ filePath: resolvedPath,
349
+ fileName: basename(resolvedPath),
350
+ line,
351
+ column,
352
+ content: content2 + "\n\n// [TRUNCATED \u2014 file exceeds 100KB limit]"
353
+ };
354
+ }
355
+ const content = readFileSync(resolvedPath, "utf-8");
356
+ return {
357
+ filePath: resolvedPath,
358
+ fileName: basename(resolvedPath),
359
+ line,
360
+ column,
361
+ content
362
+ };
363
+ } catch {
364
+ return null;
365
+ }
366
+ }
367
+ function formatSourceForContext(source) {
368
+ const lines = source.content.split("\n");
369
+ const numbered = lines.map((line, i) => {
370
+ const lineNum = i + 1;
371
+ const marker = lineNum === source.line ? " \u2192 " : " ";
372
+ return `${marker}${String(lineNum).padStart(4)} | ${line}`;
373
+ });
374
+ return `--- Source File: ${source.fileName} (line ${source.line}) ---
375
+ ${numbered.join("\n")}`;
376
+ }
231
377
  var currentLogLevel = "info";
232
378
  function setLogLevel(level) {
233
379
  currentLogLevel = level;
@@ -236,8 +382,9 @@ function shouldLog(level) {
236
382
  const levels = ["silent", "errors", "info", "debug"];
237
383
  return levels.indexOf(currentLogLevel) >= levels.indexOf(level);
238
384
  }
239
- function startSpinner(persona, prompt) {
385
+ function startSpinner(persona, prompt, verbose = false) {
240
386
  if (!shouldLog("info")) return null;
387
+ if (!verbose) return null;
241
388
  const truncated = prompt.length > 60 ? prompt.substring(0, 57) + "..." : prompt;
242
389
  const spinner = ora({
243
390
  text: chalk.cyan(`${persona.icon} ${persona.label}... `) + chalk.dim(truncated),
@@ -253,8 +400,29 @@ function stopSpinner(spinner, success) {
253
400
  spinner.fail();
254
401
  }
255
402
  }
256
- function formatResult(result, persona) {
403
+ function formatResult(result, persona, verbose = false) {
257
404
  if (!shouldLog("info")) return;
405
+ if (!verbose) {
406
+ formatResultQuiet(result);
407
+ return;
408
+ }
409
+ formatResultVerbose(result, persona);
410
+ }
411
+ function formatResultQuiet(result) {
412
+ console.log("");
413
+ console.log(result.summary);
414
+ const dataEntries = Object.entries(result.data);
415
+ const hasMeaningfulData = dataEntries.length > 0 && !(dataEntries.length === 1 && dataEntries[0][0] === "raw");
416
+ if (hasMeaningfulData) {
417
+ for (const [key, value] of dataEntries) {
418
+ if (key === "raw") continue;
419
+ const displayValue = typeof value === "string" ? value : JSON.stringify(value);
420
+ console.log(`${chalk.dim(key + ":")} ${displayValue}`);
421
+ }
422
+ }
423
+ console.log("");
424
+ }
425
+ function formatResultVerbose(result, persona) {
258
426
  const prefix = chalk.gray("[AGENT]");
259
427
  const confidenceColor = result.confidence >= 0.8 ? chalk.green : result.confidence >= 0.5 ? chalk.yellow : chalk.red;
260
428
  const statusIcon = result.success ? chalk.green("\u2713") : chalk.red("\u2717");
@@ -284,12 +452,20 @@ function formatResult(result, persona) {
284
452
  const confidence = confidenceColor(`confidence: ${result.confidence.toFixed(2)}`);
285
453
  const latency = chalk.dim(`${result.metadata.latencyMs}ms`);
286
454
  const tokens = chalk.dim(`${result.metadata.tokensUsed} tokens`);
455
+ const model = chalk.dim(`model: ${result.metadata.model}`);
287
456
  const cached = result.metadata.cached ? chalk.green(" (cached)") : "";
288
- console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens}${cached}`);
457
+ const toolNames = result.metadata.toolCalls.length > 0 ? chalk.dim(` | tools: ${result.metadata.toolCalls.map((t) => t.name).join(", ")}`) : "";
458
+ console.log(`${prefix} \u2514\u2500 ${confidence} | ${latency} | ${tokens} | ${model}${cached}${toolNames}`);
289
459
  console.log("");
290
460
  }
291
- function formatError(error, persona) {
461
+ function formatError(error, persona, verbose = false) {
292
462
  if (!shouldLog("errors")) return;
463
+ if (!verbose) {
464
+ console.log("");
465
+ console.log(chalk.red(`Error: ${error.message}`));
466
+ console.log("");
467
+ return;
468
+ }
293
469
  const prefix = chalk.gray("[AGENT]");
294
470
  console.log("");
295
471
  console.log(`${prefix} ${persona.icon} ${chalk.red("Error:")} ${error.message}`);
@@ -298,18 +474,32 @@ function formatError(error, persona) {
298
474
  }
299
475
  console.log("");
300
476
  }
301
- function formatBudgetWarning(reason) {
477
+ function formatBudgetWarning(reason, verbose = false) {
302
478
  if (!shouldLog("errors")) return;
479
+ if (!verbose) {
480
+ console.log(chalk.yellow(`Budget limit: ${reason}`));
481
+ return;
482
+ }
303
483
  const prefix = chalk.gray("[AGENT]");
304
484
  console.log(`${prefix} ${chalk.yellow("\u26A0 Budget limit:")} ${reason}`);
305
485
  }
306
- function formatRateLimitWarning() {
486
+ function formatRateLimitWarning(verbose = false) {
307
487
  if (!shouldLog("errors")) return;
488
+ if (!verbose) {
489
+ console.log(chalk.yellow("Rate limited: Too many calls. Try again later."));
490
+ return;
491
+ }
308
492
  const prefix = chalk.gray("[AGENT]");
309
493
  console.log(`${prefix} ${chalk.yellow("\u26A0 Rate limited:")} Too many calls. Try again later.`);
310
494
  }
311
- function formatDryRun(prompt, persona, context) {
495
+ function formatDryRun(prompt, persona, context, verbose = false) {
312
496
  if (!shouldLog("info")) return;
497
+ if (!verbose) {
498
+ console.log("");
499
+ console.log(chalk.magenta("[DRY RUN]") + ` Would execute with ${persona.name} persona`);
500
+ console.log("");
501
+ return;
502
+ }
313
503
  const prefix = chalk.gray("[AGENT]");
314
504
  console.log("");
315
505
  console.log(`${prefix} ${chalk.magenta("DRY RUN")} ${persona.icon} ${persona.label}`);
@@ -357,7 +547,32 @@ var JSON_RESPONSE_INSTRUCTION = `
357
547
  IMPORTANT: You MUST respond with ONLY a valid JSON object (no markdown, no code fences, no extra text).
358
548
  Use this exact format:
359
549
  {"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) {
550
+ function buildMessages(prompt, context, sourceFile, files) {
551
+ const parts = [];
552
+ parts.push({ type: "text", text: prompt });
553
+ if (context) {
554
+ parts.push({ type: "text", text: `
555
+ --- Context ---
556
+ ${context}` });
557
+ }
558
+ if (sourceFile) {
559
+ const formatted = formatSourceForContext(sourceFile);
560
+ parts.push({ type: "text", text: `
561
+ ${formatted}` });
562
+ }
563
+ if (files && files.length > 0) {
564
+ for (const file of files) {
565
+ const prepared = prepareFileContent(file.data, file.mediaType);
566
+ if (file.fileName) {
567
+ parts.push({ type: "text", text: `
568
+ --- Attached File: ${file.fileName} ---` });
569
+ }
570
+ parts.push(prepared);
571
+ }
572
+ }
573
+ return [{ role: "user", content: parts }];
574
+ }
575
+ async function callGoogle(prompt, context, persona, config2, options, sourceFile, files) {
361
576
  const startTime = Date.now();
362
577
  const modelName = options?.model ?? config2.model;
363
578
  logDebug(`Using model: ${modelName}`);
@@ -381,24 +596,21 @@ async function callGoogle(prompt, context, persona, config2, options) {
381
596
  const useTools = hasExplicitTools(options) && !config2.localOnly;
382
597
  if (useTools) {
383
598
  logDebug("Tools requested \u2014 using generateText path (no structured output)");
384
- return callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions);
599
+ return callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files);
385
600
  }
386
601
  logDebug("No tools \u2014 using ToolLoopAgent with structured output");
387
- return callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions);
602
+ return callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files);
388
603
  }
389
- async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
604
+ async function callWithTools(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
390
605
  const resolvedTools = resolveTools(options.tools, google);
391
606
  const toolNames = Object.keys(resolvedTools);
392
607
  logDebug(`Tools enabled: ${toolNames.join(", ")}`);
393
608
  const effectiveTimeout = Math.max(config2.timeout, TOOLS_MIN_TIMEOUT);
394
- const userMessage = context ? `${prompt}
395
-
396
- --- Context ---
397
- ${context}` : prompt;
609
+ const messages = buildMessages(prompt, context, sourceFile, files);
398
610
  const result = await generateText({
399
611
  model: google(modelName),
400
612
  system: persona.systemPrompt + JSON_RESPONSE_INSTRUCTION,
401
- prompt: userMessage,
613
+ messages,
402
614
  tools: resolvedTools,
403
615
  stopWhen: stepCountIs(5),
404
616
  // Allow multi-step: tool invocation → response
@@ -442,11 +654,8 @@ ${context}` : prompt;
442
654
  }
443
655
  };
444
656
  }
445
- async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions) {
446
- const userMessage = context ? `${prompt}
447
-
448
- --- Context ---
449
- ${context}` : prompt;
657
+ async function callWithStructuredOutput(prompt, context, persona, config2, options, google, modelName, startTime, providerOptions, sourceFile, files) {
658
+ const messages = buildMessages(prompt, context, sourceFile, files);
450
659
  const collectedToolCalls = [];
451
660
  const useCustomSchema = !!(options?.schema || options?.responseFormat);
452
661
  let outputConfig;
@@ -485,7 +694,7 @@ IMPORTANT: You must respond with structured data matching the requested output s
485
694
  }
486
695
  });
487
696
  const result = await agent.generate({
488
- prompt: userMessage,
697
+ messages,
489
698
  timeout: config2.timeout
490
699
  });
491
700
  const latencyMs = Date.now() - startTime;
@@ -780,7 +989,9 @@ var DEFAULT_CONFIG = {
780
989
  localOnly: false,
781
990
  dryRun: false,
782
991
  logLevel: "info",
783
- safetySettings: []
992
+ verbose: false,
993
+ safetySettings: [],
994
+ includeCallerSource: true
784
995
  };
785
996
  var config = { ...DEFAULT_CONFIG };
786
997
  var rateLimiter = new RateLimiter(config.budget.maxCallsPerDay);
@@ -799,18 +1010,19 @@ function getConfig() {
799
1010
  async function executeAgent(prompt, context, options) {
800
1011
  const personaName = options?.persona ?? config.persona;
801
1012
  const persona = options?.persona ? getPersona(options.persona) : detectPersona(prompt, personaName);
1013
+ const verbose = options?.verbose ?? config.verbose;
802
1014
  logDebug(`Selected persona: ${persona.name} (${persona.icon})`);
803
1015
  if (config.dryRun) {
804
- formatDryRun(prompt, persona, context);
1016
+ formatDryRun(prompt, persona, context, verbose);
805
1017
  return createDryRunResult(persona.name);
806
1018
  }
807
1019
  if (!rateLimiter.tryConsume()) {
808
- formatRateLimitWarning();
1020
+ formatRateLimitWarning(verbose);
809
1021
  return createErrorResult("Rate limited \u2014 too many calls. Try again later.");
810
1022
  }
811
1023
  const budgetCheck = budgetTracker.canMakeCall();
812
1024
  if (!budgetCheck.allowed) {
813
- formatBudgetWarning(budgetCheck.reason);
1025
+ formatBudgetWarning(budgetCheck.reason, verbose);
814
1026
  return createErrorResult(budgetCheck.reason);
815
1027
  }
816
1028
  let contextStr = "";
@@ -832,10 +1044,27 @@ async function executeAgent(prompt, context, options) {
832
1044
  }
833
1045
  }
834
1046
  const processedPrompt = config.anonymize ? anonymizeValue(prompt) : prompt;
835
- const spinner = startSpinner(persona, processedPrompt);
1047
+ const shouldIncludeSource = options?.includeCallerSource ?? config.includeCallerSource;
1048
+ let sourceFile = null;
1049
+ if (shouldIncludeSource) {
1050
+ if (context instanceof Error) {
1051
+ sourceFile = getErrorSourceFile(context);
1052
+ if (sourceFile) {
1053
+ logDebug(`Auto-detected error source file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1054
+ }
1055
+ }
1056
+ if (!sourceFile) {
1057
+ sourceFile = getCallerFile();
1058
+ if (sourceFile) {
1059
+ logDebug(`Auto-detected caller file: ${sourceFile.fileName} (line ${sourceFile.line})`);
1060
+ }
1061
+ }
1062
+ }
1063
+ const files = options?.files;
1064
+ const spinner = startSpinner(persona, processedPrompt, verbose);
836
1065
  try {
837
1066
  const result = await Promise.race([
838
- callGoogle(processedPrompt, contextStr, persona, config, options),
1067
+ callGoogle(processedPrompt, contextStr, persona, config, options, sourceFile, files),
839
1068
  createTimeout(config.timeout)
840
1069
  ]);
841
1070
  budgetTracker.recordUsage(
@@ -843,12 +1072,12 @@ async function executeAgent(prompt, context, options) {
843
1072
  estimateCost(result.metadata.tokensUsed, result.metadata.model)
844
1073
  );
845
1074
  stopSpinner(spinner, result.success);
846
- formatResult(result, persona);
1075
+ formatResult(result, persona, verbose);
847
1076
  return result;
848
1077
  } catch (error) {
849
1078
  stopSpinner(spinner, false);
850
1079
  const err = error instanceof Error ? error : new Error(String(error));
851
- formatError(err, persona);
1080
+ formatError(err, persona, verbose);
852
1081
  return createErrorResult(err.message);
853
1082
  }
854
1083
  }