@juspay/neurolink 7.48.0 → 7.49.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.
Files changed (156) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +177 -784
  3. package/dist/agent/directTools.d.ts +55 -0
  4. package/dist/agent/directTools.js +266 -0
  5. package/dist/cli/factories/commandFactory.d.ts +2 -0
  6. package/dist/cli/factories/commandFactory.js +130 -16
  7. package/dist/cli/index.js +0 -0
  8. package/dist/cli/loop/conversationSelector.d.ts +45 -0
  9. package/dist/cli/loop/conversationSelector.js +222 -0
  10. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  11. package/dist/cli/loop/session.d.ts +36 -8
  12. package/dist/cli/loop/session.js +257 -61
  13. package/dist/core/baseProvider.js +9 -2
  14. package/dist/core/evaluation.js +5 -2
  15. package/dist/factories/providerRegistry.js +2 -2
  16. package/dist/lib/agent/directTools.d.ts +55 -0
  17. package/dist/lib/agent/directTools.js +266 -0
  18. package/dist/lib/core/baseProvider.js +9 -2
  19. package/dist/lib/core/evaluation.js +5 -2
  20. package/dist/lib/factories/providerRegistry.js +2 -2
  21. package/dist/lib/mcp/factory.d.ts +2 -157
  22. package/dist/lib/mcp/flexibleToolValidator.d.ts +1 -5
  23. package/dist/lib/mcp/index.d.ts +3 -2
  24. package/dist/lib/mcp/mcpCircuitBreaker.d.ts +1 -75
  25. package/dist/lib/mcp/mcpClientFactory.d.ts +1 -20
  26. package/dist/lib/mcp/mcpClientFactory.js +1 -0
  27. package/dist/lib/mcp/registry.d.ts +3 -10
  28. package/dist/lib/mcp/servers/agent/directToolsServer.d.ts +1 -1
  29. package/dist/lib/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
  30. package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
  31. package/dist/lib/mcp/toolDiscoveryService.d.ts +3 -84
  32. package/dist/lib/mcp/toolRegistry.d.ts +2 -24
  33. package/dist/lib/middleware/builtin/guardrails.d.ts +5 -16
  34. package/dist/lib/middleware/builtin/guardrails.js +44 -39
  35. package/dist/lib/middleware/utils/guardrailsUtils.d.ts +64 -0
  36. package/dist/lib/middleware/utils/guardrailsUtils.js +387 -0
  37. package/dist/lib/neurolink.d.ts +1 -1
  38. package/dist/lib/providers/anthropic.js +46 -3
  39. package/dist/lib/providers/azureOpenai.js +8 -2
  40. package/dist/lib/providers/googleAiStudio.js +8 -2
  41. package/dist/lib/providers/googleVertex.js +11 -2
  42. package/dist/lib/providers/huggingFace.js +1 -1
  43. package/dist/lib/providers/litellm.js +1 -1
  44. package/dist/lib/providers/mistral.js +1 -1
  45. package/dist/lib/providers/openAI.js +46 -3
  46. package/dist/lib/providers/sagemaker/adaptive-semaphore.d.ts +1 -13
  47. package/dist/lib/providers/sagemaker/client.d.ts +1 -1
  48. package/dist/lib/providers/sagemaker/config.d.ts +1 -1
  49. package/dist/lib/providers/sagemaker/detection.d.ts +1 -1
  50. package/dist/lib/providers/sagemaker/errors.d.ts +1 -1
  51. package/dist/lib/providers/sagemaker/index.d.ts +1 -1
  52. package/dist/lib/providers/sagemaker/language-model.d.ts +1 -1
  53. package/dist/lib/providers/sagemaker/parsers.d.ts +1 -1
  54. package/dist/lib/providers/sagemaker/streaming.d.ts +1 -1
  55. package/dist/lib/providers/sagemaker/structured-parser.d.ts +1 -1
  56. package/dist/lib/session/globalSessionState.d.ts +26 -0
  57. package/dist/lib/session/globalSessionState.js +49 -0
  58. package/dist/lib/types/cli.d.ts +28 -0
  59. package/dist/lib/types/content.d.ts +18 -5
  60. package/dist/lib/types/contextTypes.d.ts +1 -1
  61. package/dist/lib/types/conversation.d.ts +55 -4
  62. package/dist/lib/types/fileTypes.d.ts +65 -0
  63. package/dist/lib/types/fileTypes.js +4 -0
  64. package/dist/lib/types/generateTypes.d.ts +12 -0
  65. package/dist/lib/types/guardrails.d.ts +103 -0
  66. package/dist/lib/types/guardrails.js +1 -0
  67. package/dist/lib/types/index.d.ts +4 -2
  68. package/dist/lib/types/index.js +4 -0
  69. package/dist/lib/types/mcpTypes.d.ts +407 -14
  70. package/dist/lib/types/providers.d.ts +469 -0
  71. package/dist/lib/types/streamTypes.d.ts +7 -0
  72. package/dist/lib/types/tools.d.ts +132 -35
  73. package/dist/lib/utils/csvProcessor.d.ts +68 -0
  74. package/dist/lib/utils/csvProcessor.js +277 -0
  75. package/dist/lib/utils/fileDetector.d.ts +57 -0
  76. package/dist/lib/utils/fileDetector.js +457 -0
  77. package/dist/lib/utils/imageProcessor.d.ts +10 -0
  78. package/dist/lib/utils/imageProcessor.js +22 -0
  79. package/dist/lib/utils/loopUtils.d.ts +71 -0
  80. package/dist/lib/utils/loopUtils.js +262 -0
  81. package/dist/lib/utils/messageBuilder.d.ts +2 -1
  82. package/dist/lib/utils/messageBuilder.js +197 -2
  83. package/dist/lib/utils/optionsUtils.d.ts +1 -1
  84. package/dist/mcp/factory.d.ts +2 -157
  85. package/dist/mcp/flexibleToolValidator.d.ts +1 -5
  86. package/dist/mcp/index.d.ts +3 -2
  87. package/dist/mcp/mcpCircuitBreaker.d.ts +1 -75
  88. package/dist/mcp/mcpClientFactory.d.ts +1 -20
  89. package/dist/mcp/mcpClientFactory.js +1 -0
  90. package/dist/mcp/registry.d.ts +3 -10
  91. package/dist/mcp/servers/agent/directToolsServer.d.ts +1 -1
  92. package/dist/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
  93. package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
  94. package/dist/mcp/toolDiscoveryService.d.ts +3 -84
  95. package/dist/mcp/toolRegistry.d.ts +2 -24
  96. package/dist/middleware/builtin/guardrails.d.ts +5 -16
  97. package/dist/middleware/builtin/guardrails.js +44 -39
  98. package/dist/middleware/utils/guardrailsUtils.d.ts +64 -0
  99. package/dist/middleware/utils/guardrailsUtils.js +387 -0
  100. package/dist/neurolink.d.ts +1 -1
  101. package/dist/providers/anthropic.js +46 -3
  102. package/dist/providers/azureOpenai.js +8 -2
  103. package/dist/providers/googleAiStudio.js +8 -2
  104. package/dist/providers/googleVertex.js +11 -2
  105. package/dist/providers/huggingFace.js +1 -1
  106. package/dist/providers/litellm.js +1 -1
  107. package/dist/providers/mistral.js +1 -1
  108. package/dist/providers/openAI.js +46 -3
  109. package/dist/providers/sagemaker/adaptive-semaphore.d.ts +1 -13
  110. package/dist/providers/sagemaker/client.d.ts +1 -1
  111. package/dist/providers/sagemaker/config.d.ts +1 -1
  112. package/dist/providers/sagemaker/detection.d.ts +1 -1
  113. package/dist/providers/sagemaker/errors.d.ts +1 -1
  114. package/dist/providers/sagemaker/index.d.ts +1 -1
  115. package/dist/providers/sagemaker/language-model.d.ts +3 -3
  116. package/dist/providers/sagemaker/parsers.d.ts +1 -1
  117. package/dist/providers/sagemaker/streaming.d.ts +1 -1
  118. package/dist/providers/sagemaker/structured-parser.d.ts +1 -1
  119. package/dist/session/globalSessionState.d.ts +26 -0
  120. package/dist/session/globalSessionState.js +49 -0
  121. package/dist/types/cli.d.ts +28 -0
  122. package/dist/types/content.d.ts +18 -5
  123. package/dist/types/contextTypes.d.ts +1 -1
  124. package/dist/types/conversation.d.ts +55 -4
  125. package/dist/types/fileTypes.d.ts +65 -0
  126. package/dist/types/fileTypes.js +4 -0
  127. package/dist/types/generateTypes.d.ts +12 -0
  128. package/dist/types/guardrails.d.ts +103 -0
  129. package/dist/types/guardrails.js +1 -0
  130. package/dist/types/index.d.ts +4 -2
  131. package/dist/types/index.js +4 -0
  132. package/dist/types/mcpTypes.d.ts +407 -14
  133. package/dist/types/modelTypes.d.ts +6 -6
  134. package/dist/types/providers.d.ts +469 -0
  135. package/dist/types/streamTypes.d.ts +7 -0
  136. package/dist/types/tools.d.ts +132 -35
  137. package/dist/utils/csvProcessor.d.ts +68 -0
  138. package/dist/utils/csvProcessor.js +277 -0
  139. package/dist/utils/fileDetector.d.ts +57 -0
  140. package/dist/utils/fileDetector.js +457 -0
  141. package/dist/utils/imageProcessor.d.ts +10 -0
  142. package/dist/utils/imageProcessor.js +22 -0
  143. package/dist/utils/loopUtils.d.ts +71 -0
  144. package/dist/utils/loopUtils.js +262 -0
  145. package/dist/utils/messageBuilder.d.ts +2 -1
  146. package/dist/utils/messageBuilder.js +197 -2
  147. package/dist/utils/optionsUtils.d.ts +1 -1
  148. package/package.json +9 -3
  149. package/dist/lib/mcp/contracts/mcpContract.d.ts +0 -106
  150. package/dist/lib/mcp/contracts/mcpContract.js +0 -5
  151. package/dist/lib/providers/sagemaker/types.d.ts +0 -456
  152. package/dist/lib/providers/sagemaker/types.js +0 -7
  153. package/dist/mcp/contracts/mcpContract.d.ts +0 -106
  154. package/dist/mcp/contracts/mcpContract.js +0 -5
  155. package/dist/providers/sagemaker/types.d.ts +0 -456
  156. package/dist/providers/sagemaker/types.js +0 -7
@@ -346,6 +346,61 @@ export declare const directAgentTools: {
346
346
  count?: undefined;
347
347
  }>;
348
348
  };
349
+ analyzeCSV: import("ai").Tool<z.ZodObject<{
350
+ filePath: z.ZodEffects<z.ZodString, string, string>;
351
+ operation: z.ZodEnum<["count_by_column", "sum_by_column", "average_by_column", "min_max_by_column", "describe"]>;
352
+ column: z.ZodOptional<z.ZodString>;
353
+ maxRows: z.ZodOptional<z.ZodNumber>;
354
+ }, "strip", z.ZodTypeAny, {
355
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
356
+ filePath: string;
357
+ maxRows?: number | undefined;
358
+ column?: string | undefined;
359
+ }, {
360
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
361
+ filePath: string;
362
+ maxRows?: number | undefined;
363
+ column?: string | undefined;
364
+ }>, {
365
+ success: boolean;
366
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
367
+ column: string | undefined;
368
+ result: string;
369
+ rowCount: number;
370
+ } | {
371
+ success: boolean;
372
+ error: string;
373
+ operation?: undefined;
374
+ column?: undefined;
375
+ } | {
376
+ success: boolean;
377
+ error: string;
378
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
379
+ column: string | undefined;
380
+ }> & {
381
+ execute: (args: {
382
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
383
+ filePath: string;
384
+ maxRows?: number | undefined;
385
+ column?: string | undefined;
386
+ }, options: import("ai").ToolExecutionOptions) => PromiseLike<{
387
+ success: boolean;
388
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
389
+ column: string | undefined;
390
+ result: string;
391
+ rowCount: number;
392
+ } | {
393
+ success: boolean;
394
+ error: string;
395
+ operation?: undefined;
396
+ column?: undefined;
397
+ } | {
398
+ success: boolean;
399
+ error: string;
400
+ operation: "count_by_column" | "sum_by_column" | "average_by_column" | "min_max_by_column" | "describe";
401
+ column: string | undefined;
402
+ }>;
403
+ };
349
404
  websearchGrounding: import("ai").Tool<z.ZodObject<{
350
405
  query: z.ZodString;
351
406
  maxResults: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
@@ -8,6 +8,7 @@ import * as fs from "fs";
8
8
  import * as path from "path";
9
9
  import { logger } from "../utils/logger.js";
10
10
  import { VertexAI } from "@google-cloud/vertexai";
11
+ import { CSVProcessor } from "../utils/csvProcessor.js";
11
12
  // Runtime Google Search tool creation - bypasses TypeScript strict typing
12
13
  function createGoogleSearchTools() {
13
14
  const searchTool = {};
@@ -336,6 +337,271 @@ export const directAgentTools = {
336
337
  }
337
338
  },
338
339
  }),
340
+ analyzeCSV: tool({
341
+ description: "Analyze CSV file for accurate counting, aggregation, and statistical analysis. Use this for precise data operations like counting rows by column, calculating sums/averages, finding min/max values, etc. The tool reads the file directly - do NOT pass CSV content.",
342
+ parameters: z.object({
343
+ filePath: z
344
+ .string()
345
+ .refine((inputPath) => {
346
+ const resolvedPath = path.resolve(inputPath);
347
+ const normalizedPath = resolvedPath
348
+ .toLowerCase()
349
+ .replace(/\\/g, "/");
350
+ const sensitivePatterns = [
351
+ "/etc/",
352
+ "/sys/",
353
+ "/proc/",
354
+ "/dev/",
355
+ "/root/",
356
+ "/.ssh/",
357
+ "/private/etc/",
358
+ "/private/var/",
359
+ "c:/windows/",
360
+ "c:/program files/",
361
+ "c:/programdata/",
362
+ ];
363
+ return !sensitivePatterns.some((pattern) => normalizedPath.startsWith(pattern));
364
+ }, {
365
+ message: "Invalid file path: access to system directories is not allowed",
366
+ })
367
+ .describe("Path to the CSV file to analyze (e.g., 'test/data.csv' or '/absolute/path/file.csv')"),
368
+ operation: z
369
+ .enum([
370
+ "count_by_column",
371
+ "sum_by_column",
372
+ "average_by_column",
373
+ "min_max_by_column",
374
+ "describe",
375
+ ])
376
+ .describe("Type of analysis to perform"),
377
+ column: z
378
+ .string()
379
+ .optional()
380
+ .describe("Column name for the operation (required for most operations)"),
381
+ maxRows: z
382
+ .number()
383
+ .optional()
384
+ .describe("Maximum rows to process (default: 1000)"),
385
+ }),
386
+ execute: async ({ filePath, operation, column, maxRows = 1000 }) => {
387
+ const startTime = Date.now();
388
+ logger.info(`[analyzeCSV] 🚀 START: file=${filePath}, operation=${operation}, column=${column}, maxRows=${maxRows}`);
389
+ try {
390
+ // Resolve file path
391
+ logger.debug(`[analyzeCSV] Resolving file: ${filePath}`);
392
+ const path = await import("path");
393
+ // Resolve path (support both relative and absolute)
394
+ const resolvedPath = path.isAbsolute(filePath)
395
+ ? filePath
396
+ : path.resolve(process.cwd(), filePath);
397
+ logger.debug(`[analyzeCSV] Resolved path: ${resolvedPath}`);
398
+ // Parse CSV using streaming from disk (memory efficient)
399
+ logger.info(`[analyzeCSV] Starting CSV parsing (max ${maxRows} rows)...`);
400
+ const rows = (await CSVProcessor.parseCSVFile(resolvedPath, maxRows));
401
+ logger.info(`[analyzeCSV] ✅ CSV parsing complete: ${rows.length} rows`);
402
+ if (rows.length === 0) {
403
+ logger.warn(`[analyzeCSV] No data rows found`);
404
+ return {
405
+ success: false,
406
+ error: "No data rows found in CSV",
407
+ };
408
+ }
409
+ // Log column names
410
+ const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [];
411
+ logger.info(`[analyzeCSV] Found ${rows.length} rows with columns:`, columnNames);
412
+ logger.info(`[analyzeCSV] Executing operation: ${operation}`);
413
+ let result;
414
+ switch (operation) {
415
+ case "count_by_column": {
416
+ logger.info(`[analyzeCSV] count_by_column: column=${column}`);
417
+ if (!column) {
418
+ return {
419
+ success: false,
420
+ error: "Column name required for count_by_column operation",
421
+ };
422
+ }
423
+ // Count occurrences of each value in the column
424
+ const counts = {};
425
+ logger.debug(`[analyzeCSV] Counting rows...`);
426
+ for (const row of rows) {
427
+ const value = row[column];
428
+ if (value !== undefined) {
429
+ counts[value] = (counts[value] || 0) + 1;
430
+ }
431
+ }
432
+ logger.debug(`[analyzeCSV] Found ${Object.keys(counts).length} unique values`);
433
+ // Sort by count descending
434
+ logger.debug(`[analyzeCSV] Sorting results...`);
435
+ result = Object.fromEntries(Object.entries(counts).sort(([, a], [, b]) => b - a));
436
+ logger.info(`[analyzeCSV] ✅ count_by_column complete. Result:`, result);
437
+ break;
438
+ }
439
+ case "sum_by_column": {
440
+ logger.info(`[analyzeCSV] sum_by_column: column=${column}`);
441
+ if (!column) {
442
+ return {
443
+ success: false,
444
+ error: "Column name required for sum_by_column operation",
445
+ };
446
+ }
447
+ // Sum numeric values from the target column itself for each group
448
+ const groups = {};
449
+ logger.debug(`[analyzeCSV] Grouping and summing ${rows.length} rows...`);
450
+ let processedRows = 0;
451
+ let totalNumericValuesFound = 0;
452
+ for (const row of rows) {
453
+ const key = row[column];
454
+ if (!key) {
455
+ continue;
456
+ }
457
+ // Parse numeric value from the target column
458
+ const value = row[column];
459
+ if (value === undefined || value === null || value === "") {
460
+ continue;
461
+ }
462
+ const num = parseFloat(value);
463
+ if (isNaN(num)) {
464
+ continue;
465
+ }
466
+ if (!groups[key]) {
467
+ groups[key] = 0;
468
+ }
469
+ groups[key] += num;
470
+ totalNumericValuesFound++;
471
+ processedRows++;
472
+ if (processedRows % 10 === 0) {
473
+ logger.debug(`[analyzeCSV] Processed ${processedRows}/${rows.length} rows`);
474
+ }
475
+ }
476
+ // Fail fast if no numeric data found in the requested column
477
+ if (totalNumericValuesFound === 0) {
478
+ return {
479
+ success: false,
480
+ error: `No numeric data found in column "${column}" for sum_by_column operation`,
481
+ };
482
+ }
483
+ logger.debug(`[analyzeCSV] Calculated sums for ${Object.keys(groups).length} groups (${totalNumericValuesFound} numeric values)`);
484
+ result = groups;
485
+ logger.info(`[analyzeCSV] ✅ sum_by_column complete`);
486
+ break;
487
+ }
488
+ case "average_by_column": {
489
+ logger.info(`[analyzeCSV] average_by_column: column=${column}`);
490
+ if (!column) {
491
+ return {
492
+ success: false,
493
+ error: "Column name required for average_by_column operation",
494
+ };
495
+ }
496
+ // Average numeric values from the target column itself for each group
497
+ const groups = {};
498
+ logger.debug(`[analyzeCSV] Grouping and averaging ${rows.length} rows...`);
499
+ let processedRows = 0;
500
+ let totalNumericValuesFound = 0;
501
+ for (const row of rows) {
502
+ const key = row[column];
503
+ if (!key) {
504
+ continue;
505
+ }
506
+ // Parse numeric value from the target column
507
+ const value = row[column];
508
+ if (value === undefined || value === null || value === "") {
509
+ continue;
510
+ }
511
+ const num = parseFloat(value);
512
+ if (isNaN(num)) {
513
+ continue;
514
+ }
515
+ if (!groups[key]) {
516
+ groups[key] = { sum: 0, count: 0 };
517
+ }
518
+ groups[key].sum += num;
519
+ groups[key].count++;
520
+ totalNumericValuesFound++;
521
+ processedRows++;
522
+ if (processedRows % 10 === 0) {
523
+ logger.debug(`[analyzeCSV] Processed ${processedRows}/${rows.length} rows`);
524
+ }
525
+ }
526
+ // Fail fast if no numeric data found in the requested column
527
+ if (totalNumericValuesFound === 0) {
528
+ return {
529
+ success: false,
530
+ error: `No numeric data found in column "${column}" for average_by_column operation`,
531
+ };
532
+ }
533
+ logger.debug(`[analyzeCSV] Calculated averages for ${Object.keys(groups).length} groups (${totalNumericValuesFound} numeric values)`);
534
+ result = Object.fromEntries(Object.entries(groups).map(([k, v]) => [
535
+ k,
536
+ v.count > 0 ? v.sum / v.count : 0,
537
+ ]));
538
+ logger.info(`[analyzeCSV] ✅ average_by_column complete`);
539
+ break;
540
+ }
541
+ case "min_max_by_column": {
542
+ if (!column) {
543
+ return {
544
+ success: false,
545
+ error: "Column name required for min_max_by_column operation",
546
+ };
547
+ }
548
+ const values = rows
549
+ .map((row) => row[column])
550
+ .filter((v) => v !== undefined && v !== "");
551
+ const numericValues = values
552
+ .map((v) => parseFloat(v))
553
+ .filter((n) => !isNaN(n));
554
+ if (numericValues.length === 0) {
555
+ return {
556
+ success: false,
557
+ error: `No numeric data found in column "${column}" for min_max_by_column operation`,
558
+ };
559
+ }
560
+ result = {
561
+ min: Math.min(...numericValues),
562
+ max: Math.max(...numericValues),
563
+ numericCount: numericValues.length,
564
+ totalCount: values.length,
565
+ };
566
+ break;
567
+ }
568
+ case "describe": {
569
+ const columnNames = rows.length > 0 ? Object.keys(rows[0]) : [];
570
+ result = {
571
+ total_rows: rows.length,
572
+ columns: columnNames,
573
+ column_count: columnNames.length,
574
+ };
575
+ break;
576
+ }
577
+ default:
578
+ return {
579
+ success: false,
580
+ error: `Unknown operation: ${operation}`,
581
+ };
582
+ }
583
+ const duration = Date.now() - startTime;
584
+ logger.info(`[analyzeCSV] 🏁 COMPLETE: ${operation} took ${duration}ms`);
585
+ const response = {
586
+ success: true,
587
+ operation,
588
+ column,
589
+ result: JSON.stringify(result, null, 2),
590
+ rowCount: rows.length,
591
+ };
592
+ logger.debug(`[analyzeCSV] 📤 RETURNING TO LLM:`, JSON.stringify(response, null, 2));
593
+ return response;
594
+ }
595
+ catch (error) {
596
+ return {
597
+ success: false,
598
+ error: error instanceof Error ? error.message : String(error),
599
+ operation,
600
+ column,
601
+ };
602
+ }
603
+ },
604
+ }),
339
605
  websearchGrounding: tool({
340
606
  description: "Search the web for current information using Google Search grounding. Returns raw search data for AI processing.",
341
607
  parameters: z.object({
@@ -6,6 +6,8 @@ export declare class CLICommandFactory {
6
6
  private static readonly commonOptions;
7
7
  private static buildOptions;
8
8
  private static processCliImages;
9
+ private static processCliCSVFiles;
10
+ private static processCliFiles;
9
11
  private static processOptions;
10
12
  private static handleOutput;
11
13
  private static isValidTokenUsage;
@@ -49,6 +49,26 @@ export class CLICommandFactory {
49
49
  description: "Add image file for multimodal analysis (can be used multiple times)",
50
50
  alias: "i",
51
51
  },
52
+ csv: {
53
+ type: "string",
54
+ description: "Add CSV file for data analysis (can be used multiple times)",
55
+ alias: "c",
56
+ },
57
+ file: {
58
+ type: "string",
59
+ description: "Add file with auto-detection (CSV, image, etc. - can be used multiple times)",
60
+ },
61
+ csvMaxRows: {
62
+ type: "number",
63
+ default: 1000,
64
+ description: "Maximum number of CSV rows to process",
65
+ },
66
+ csvFormat: {
67
+ type: "string",
68
+ choices: ["raw", "markdown", "json"],
69
+ default: "raw",
70
+ description: "CSV output format (raw recommended for large files)",
71
+ },
52
72
  model: {
53
73
  type: "string",
54
74
  description: "Specific model to use (e.g. gemini-2.5-pro, gemini-2.5-flash)",
@@ -188,6 +208,20 @@ export class CLICommandFactory {
188
208
  // File paths will be converted to base64 by the message builder
189
209
  return imagePaths;
190
210
  }
211
+ // Helper method to process CLI CSV files
212
+ static processCliCSVFiles(csvFiles) {
213
+ if (!csvFiles) {
214
+ return undefined;
215
+ }
216
+ return Array.isArray(csvFiles) ? csvFiles : [csvFiles];
217
+ }
218
+ // Helper method to process CLI files with auto-detection
219
+ static processCliFiles(files) {
220
+ if (!files) {
221
+ return undefined;
222
+ }
223
+ return Array.isArray(files) ? files : [files];
224
+ }
191
225
  // Helper method to process common options
192
226
  static processOptions(argv) {
193
227
  // Handle noColor option by disabling chalk
@@ -674,7 +708,7 @@ export class CLICommandFactory {
674
708
  static createLoopCommand() {
675
709
  return {
676
710
  command: "loop",
677
- describe: "Start an interactive loop session",
711
+ describe: "Start an interactive loop session with conversation management",
678
712
  builder: (yargs) => this.buildOptions(yargs, {
679
713
  "enable-conversation-memory": {
680
714
  type: "boolean",
@@ -696,9 +730,27 @@ export class CLICommandFactory {
696
730
  description: "Automatically use Redis if available",
697
731
  default: true,
698
732
  },
733
+ resume: {
734
+ type: "string",
735
+ description: "Directly resume a specific conversation by session ID",
736
+ alias: "r",
737
+ },
738
+ new: {
739
+ type: "boolean",
740
+ description: "Force start a new conversation (skip selection menu)",
741
+ alias: "n",
742
+ },
743
+ "list-conversations": {
744
+ type: "boolean",
745
+ description: "List available conversations and exit",
746
+ alias: "l",
747
+ },
699
748
  })
700
- .example("$0 loop --no-auto-redis", "Start loop with memory storage only")
701
- .example("$0 loop", "Start interactive session")
749
+ .example("$0 loop", "Start interactive session with conversation selection")
750
+ .example("$0 loop --new", "Force start new conversation")
751
+ .example("$0 loop --resume abc123", "Resume specific conversation")
752
+ .example("$0 loop --list-conversations", "List available conversations")
753
+ .example("$0 loop --no-auto-redis", "Use in-memory storage only")
702
754
  .example("$0 loop --enable-conversation-memory", "Start loop with memory"),
703
755
  handler: async (argv) => {
704
756
  if (globalSession.getCurrentSessionId()) {
@@ -706,7 +758,7 @@ export class CLICommandFactory {
706
758
  return;
707
759
  }
708
760
  let conversationMemoryConfig;
709
- const { enableConversationMemory, maxSessions, maxTurnsPerSession, autoRedis, } = argv;
761
+ const { enableConversationMemory, maxSessions, maxTurnsPerSession, autoRedis, listConversations, } = argv;
710
762
  if (enableConversationMemory) {
711
763
  let storageType = "memory";
712
764
  if (autoRedis) {
@@ -731,7 +783,48 @@ export class CLICommandFactory {
731
783
  maxTurnsPerSession: maxTurnsPerSession,
732
784
  };
733
785
  }
734
- const session = new LoopSession(initializeCliParser, conversationMemoryConfig);
786
+ // Handle --list-conversations option
787
+ if (listConversations) {
788
+ const { ConversationSelector } = await import("../loop/conversationSelector.js");
789
+ const conversationSelector = new ConversationSelector();
790
+ try {
791
+ const hasConversations = await conversationSelector.hasStoredConversations();
792
+ if (!hasConversations) {
793
+ logger.always(chalk.yellow("📝 No stored conversations found"));
794
+ return;
795
+ }
796
+ const conversations = await conversationSelector.getAvailableConversations();
797
+ logger.always(chalk.blue("📋 Available Conversations:"));
798
+ conversations.forEach((conv, index) => {
799
+ const sessionId = conv.sessionId.slice(0, 12) + "...";
800
+ const title = conv.title || "Untitled Conversation";
801
+ const messageCount = conv.messageCount || 0;
802
+ const lastActivity = conv.updatedAt
803
+ ? new Date(conv.updatedAt).toLocaleDateString()
804
+ : "Unknown";
805
+ logger.always(`${index + 1}. ${chalk.cyan(sessionId)} - ${title}`);
806
+ logger.always(` ${chalk.gray(`${messageCount} messages | Last: ${lastActivity}`)}`);
807
+ });
808
+ logger.always(chalk.gray(`\nUse: neurolink loop --resume <session-id> to resume a conversation`));
809
+ }
810
+ catch (error) {
811
+ logger.error("Failed to list conversations:", error);
812
+ }
813
+ finally {
814
+ await conversationSelector.close();
815
+ }
816
+ return;
817
+ }
818
+ // Create enhanced session with direct session management options
819
+ const sessionOptions = {};
820
+ // Pass CLI options to session for direct session management
821
+ if (argv.resume && typeof argv.resume === "string") {
822
+ sessionOptions.directResumeSessionId = argv.resume;
823
+ }
824
+ if (argv.new) {
825
+ sessionOptions.forceNewSession = true;
826
+ }
827
+ const session = new LoopSession(initializeCliParser, conversationMemoryConfig, sessionOptions);
735
828
  await session.start();
736
829
  },
737
830
  };
@@ -967,12 +1060,21 @@ export class CLICommandFactory {
967
1060
  toolsEnabled: !options.disableTools,
968
1061
  });
969
1062
  }
970
- // Process CLI images if provided
1063
+ // Process CLI multimodal inputs
971
1064
  const imageBuffers = CLICommandFactory.processCliImages(argv.image);
1065
+ const csvFiles = CLICommandFactory.processCliCSVFiles(argv.csv);
1066
+ const files = CLICommandFactory.processCliFiles(argv.file);
972
1067
  const result = await sdk.generate({
973
- input: imageBuffers
974
- ? { text: inputText, images: imageBuffers }
975
- : { text: inputText },
1068
+ input: {
1069
+ text: inputText,
1070
+ ...(imageBuffers && { images: imageBuffers }),
1071
+ ...(csvFiles && { csvFiles }),
1072
+ ...(files && { files }),
1073
+ },
1074
+ csvOptions: {
1075
+ maxRows: argv.csvMaxRows,
1076
+ formatStyle: argv.csvFormat,
1077
+ },
976
1078
  provider: enhancedOptions.provider,
977
1079
  model: enhancedOptions.model,
978
1080
  temperature: enhancedOptions.temperature,
@@ -1140,12 +1242,21 @@ export class CLICommandFactory {
1140
1242
  const context = sessionId
1141
1243
  ? { ...contextMetadata, sessionId }
1142
1244
  : contextMetadata;
1143
- // Process CLI images if provided
1245
+ // Process CLI multimodal inputs
1144
1246
  const imageBuffers = CLICommandFactory.processCliImages(argv.image);
1247
+ const csvFiles = CLICommandFactory.processCliCSVFiles(argv.csv);
1248
+ const files = CLICommandFactory.processCliFiles(argv.file);
1145
1249
  const stream = await sdk.stream({
1146
- input: imageBuffers
1147
- ? { text: inputText, images: imageBuffers }
1148
- : { text: inputText },
1250
+ input: {
1251
+ text: inputText,
1252
+ ...(imageBuffers && { images: imageBuffers }),
1253
+ ...(csvFiles && { csvFiles }),
1254
+ ...(files && { files }),
1255
+ },
1256
+ csvOptions: {
1257
+ maxRows: argv.csvMaxRows,
1258
+ formatStyle: argv.csvFormat,
1259
+ },
1149
1260
  provider: enhancedOptions.provider,
1150
1261
  model: enhancedOptions.model,
1151
1262
  temperature: enhancedOptions.temperature,
@@ -1179,11 +1290,14 @@ export class CLICommandFactory {
1179
1290
  let fullContent = "";
1180
1291
  let contentReceived = false;
1181
1292
  const abortController = new AbortController();
1182
- // Create timeout promise for stream consumption (30 seconds)
1293
+ // Create timeout promise for stream consumption (default: 30 seconds, respects user-provided timeout)
1294
+ const streamTimeout = options.timeout && typeof options.timeout === "number"
1295
+ ? options.timeout * 1000
1296
+ : 30000;
1183
1297
  const timeoutPromise = new Promise((_, reject) => {
1184
1298
  const timeoutId = setTimeout(() => {
1185
1299
  if (!contentReceived) {
1186
- const timeoutError = new Error("\n❌ Stream timeout - no content received within 30 seconds\n" +
1300
+ const timeoutError = new Error(`\n❌ Stream timeout - no content received within ${streamTimeout / 1000} seconds\n` +
1187
1301
  "This usually indicates authentication or network issues\n\n" +
1188
1302
  "🔧 Try these steps:\n" +
1189
1303
  "1. Check your provider credentials are configured correctly\n" +
@@ -1191,7 +1305,7 @@ export class CLICommandFactory {
1191
1305
  `3. Use debug mode: neurolink stream "test" --provider ${options.provider} --debug`);
1192
1306
  reject(timeoutError);
1193
1307
  }
1194
- }, 30000);
1308
+ }, streamTimeout);
1195
1309
  // Clean up timeout when aborted
1196
1310
  abortController.signal.addEventListener("abort", () => {
1197
1311
  clearTimeout(timeoutId);
package/dist/cli/index.js CHANGED
File without changes
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Conversation Selector for Loop Mode
3
+ * Handles discovery and selection of stored conversations from Redis
4
+ */
5
+ import type { ConversationSummary } from "../../lib/types/conversation.js";
6
+ import type { RedisStorageConfig } from "../../lib/types/conversation.js";
7
+ export declare class ConversationSelector {
8
+ private redisClient;
9
+ private redisConfig;
10
+ private conversationCache;
11
+ private cacheTimestamp;
12
+ constructor(redisConfig?: RedisStorageConfig);
13
+ /**
14
+ * Initialize Redis connection
15
+ */
16
+ private initializeRedis;
17
+ /**
18
+ * Get available conversations for a user
19
+ */
20
+ getAvailableConversations(userId?: string): Promise<ConversationSummary[]>;
21
+ /**
22
+ * Display conversation menu and get user selection
23
+ */
24
+ displayConversationMenu(userId?: string): Promise<string | "NEW_CONVERSATION">;
25
+ /**
26
+ * Check if there are any stored conversations
27
+ */
28
+ hasStoredConversations(userId?: string): Promise<boolean>;
29
+ /**
30
+ * Close Redis connection
31
+ */
32
+ close(): Promise<void>;
33
+ private scanConversationKeys;
34
+ private processConversationKeys;
35
+ private processSingleConversationKey;
36
+ private sortConversationsByDate;
37
+ private updateCache;
38
+ private filterConversationsByUser;
39
+ private createMenuChoices;
40
+ private showSelectionPrompt;
41
+ private handleRetrievalError;
42
+ private handleMenuError;
43
+ private createConversationSummary;
44
+ private formatConversationChoice;
45
+ }