@juspay/neurolink 7.48.1 → 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 (130) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +215 -16
  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/session/globalSessionState.d.ts +26 -0
  47. package/dist/lib/session/globalSessionState.js +49 -0
  48. package/dist/lib/types/cli.d.ts +28 -0
  49. package/dist/lib/types/content.d.ts +18 -5
  50. package/dist/lib/types/contextTypes.d.ts +1 -1
  51. package/dist/lib/types/conversation.d.ts +55 -4
  52. package/dist/lib/types/fileTypes.d.ts +65 -0
  53. package/dist/lib/types/fileTypes.js +4 -0
  54. package/dist/lib/types/generateTypes.d.ts +12 -0
  55. package/dist/lib/types/guardrails.d.ts +103 -0
  56. package/dist/lib/types/guardrails.js +1 -0
  57. package/dist/lib/types/index.d.ts +4 -2
  58. package/dist/lib/types/index.js +4 -0
  59. package/dist/lib/types/mcpTypes.d.ts +407 -14
  60. package/dist/lib/types/streamTypes.d.ts +7 -0
  61. package/dist/lib/types/tools.d.ts +132 -35
  62. package/dist/lib/utils/csvProcessor.d.ts +68 -0
  63. package/dist/lib/utils/csvProcessor.js +277 -0
  64. package/dist/lib/utils/fileDetector.d.ts +57 -0
  65. package/dist/lib/utils/fileDetector.js +457 -0
  66. package/dist/lib/utils/imageProcessor.d.ts +10 -0
  67. package/dist/lib/utils/imageProcessor.js +22 -0
  68. package/dist/lib/utils/loopUtils.d.ts +71 -0
  69. package/dist/lib/utils/loopUtils.js +262 -0
  70. package/dist/lib/utils/messageBuilder.d.ts +2 -1
  71. package/dist/lib/utils/messageBuilder.js +197 -2
  72. package/dist/lib/utils/optionsUtils.d.ts +1 -1
  73. package/dist/mcp/factory.d.ts +2 -157
  74. package/dist/mcp/flexibleToolValidator.d.ts +1 -5
  75. package/dist/mcp/index.d.ts +3 -2
  76. package/dist/mcp/mcpCircuitBreaker.d.ts +1 -75
  77. package/dist/mcp/mcpClientFactory.d.ts +1 -20
  78. package/dist/mcp/mcpClientFactory.js +1 -0
  79. package/dist/mcp/registry.d.ts +3 -10
  80. package/dist/mcp/servers/agent/directToolsServer.d.ts +1 -1
  81. package/dist/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
  82. package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
  83. package/dist/mcp/toolDiscoveryService.d.ts +3 -84
  84. package/dist/mcp/toolRegistry.d.ts +2 -24
  85. package/dist/middleware/builtin/guardrails.d.ts +5 -16
  86. package/dist/middleware/builtin/guardrails.js +44 -39
  87. package/dist/middleware/utils/guardrailsUtils.d.ts +64 -0
  88. package/dist/middleware/utils/guardrailsUtils.js +387 -0
  89. package/dist/neurolink.d.ts +1 -1
  90. package/dist/providers/anthropic.js +46 -3
  91. package/dist/providers/azureOpenai.js +8 -2
  92. package/dist/providers/googleAiStudio.js +8 -2
  93. package/dist/providers/googleVertex.js +11 -2
  94. package/dist/providers/huggingFace.js +1 -1
  95. package/dist/providers/litellm.js +1 -1
  96. package/dist/providers/mistral.js +1 -1
  97. package/dist/providers/openAI.js +46 -3
  98. package/dist/session/globalSessionState.d.ts +26 -0
  99. package/dist/session/globalSessionState.js +49 -0
  100. package/dist/types/cli.d.ts +28 -0
  101. package/dist/types/content.d.ts +18 -5
  102. package/dist/types/contextTypes.d.ts +1 -1
  103. package/dist/types/conversation.d.ts +55 -4
  104. package/dist/types/fileTypes.d.ts +65 -0
  105. package/dist/types/fileTypes.js +4 -0
  106. package/dist/types/generateTypes.d.ts +12 -0
  107. package/dist/types/guardrails.d.ts +103 -0
  108. package/dist/types/guardrails.js +1 -0
  109. package/dist/types/index.d.ts +4 -2
  110. package/dist/types/index.js +4 -0
  111. package/dist/types/mcpTypes.d.ts +407 -14
  112. package/dist/types/modelTypes.d.ts +6 -6
  113. package/dist/types/streamTypes.d.ts +7 -0
  114. package/dist/types/tools.d.ts +132 -35
  115. package/dist/utils/csvProcessor.d.ts +68 -0
  116. package/dist/utils/csvProcessor.js +277 -0
  117. package/dist/utils/fileDetector.d.ts +57 -0
  118. package/dist/utils/fileDetector.js +457 -0
  119. package/dist/utils/imageProcessor.d.ts +10 -0
  120. package/dist/utils/imageProcessor.js +22 -0
  121. package/dist/utils/loopUtils.d.ts +71 -0
  122. package/dist/utils/loopUtils.js +262 -0
  123. package/dist/utils/messageBuilder.d.ts +2 -1
  124. package/dist/utils/messageBuilder.js +197 -2
  125. package/dist/utils/optionsUtils.d.ts +1 -1
  126. package/package.json +9 -3
  127. package/dist/lib/mcp/contracts/mcpContract.d.ts +0 -106
  128. package/dist/lib/mcp/contracts/mcpContract.js +0 -5
  129. package/dist/mcp/contracts/mcpContract.d.ts +0 -106
  130. package/dist/mcp/contracts/mcpContract.js +0 -5
@@ -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
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Conversation Selector for Loop Mode
3
+ * Handles discovery and selection of stored conversations from Redis
4
+ */
5
+ import inquirer from "inquirer";
6
+ import chalk from "chalk";
7
+ import { createRedisClient, scanKeys, deserializeConversation, getNormalizedConfig, } from "../../lib/utils/redis.js";
8
+ import { logger } from "../../lib/utils/logger.js";
9
+ import { LOOP_CACHE_CONFIG, LOOP_DISPLAY_LIMITS, generateConversationTitle, truncateText, formatTimeAgo, getContentIcon, } from "../../lib/utils/loopUtils.js";
10
+ export class ConversationSelector {
11
+ redisClient = null;
12
+ redisConfig;
13
+ conversationCache = null;
14
+ cacheTimestamp = 0;
15
+ constructor(redisConfig = {}) {
16
+ this.redisConfig = getNormalizedConfig(redisConfig);
17
+ }
18
+ /**
19
+ * Initialize Redis connection
20
+ */
21
+ async initializeRedis() {
22
+ if (!this.redisClient) {
23
+ this.redisClient = await createRedisClient(this.redisConfig);
24
+ }
25
+ }
26
+ /**
27
+ * Get available conversations for a user
28
+ */
29
+ async getAvailableConversations(userId) {
30
+ // Check if cached conversations are still valid (within TTL)
31
+ if (this.conversationCache &&
32
+ Date.now() - this.cacheTimestamp < LOOP_CACHE_CONFIG.TTL_MS) {
33
+ logger.debug("Using cached conversation list");
34
+ return this.filterConversationsByUser(this.conversationCache, userId);
35
+ }
36
+ try {
37
+ await this.initializeRedis();
38
+ if (!this.redisClient) {
39
+ throw new Error("Redis client not available");
40
+ }
41
+ const keys = await this.scanConversationKeys();
42
+ if (keys.length === 0) {
43
+ logger.debug("No conversations found in Redis");
44
+ return [];
45
+ }
46
+ const summaries = await this.processConversationKeys(keys);
47
+ const sortedSummaries = this.sortConversationsByDate(summaries);
48
+ this.updateCache(sortedSummaries);
49
+ return this.filterConversationsByUser(sortedSummaries, userId);
50
+ }
51
+ catch (error) {
52
+ return this.handleRetrievalError(error);
53
+ }
54
+ }
55
+ /**
56
+ * Display conversation menu and get user selection
57
+ */
58
+ async displayConversationMenu(userId) {
59
+ try {
60
+ const conversations = await this.getAvailableConversations(userId);
61
+ if (conversations.length === 0) {
62
+ logger.debug("No conversations available for selection");
63
+ return "NEW_CONVERSATION";
64
+ }
65
+ const choices = this.createMenuChoices(conversations);
66
+ return await this.showSelectionPrompt(choices);
67
+ }
68
+ catch (error) {
69
+ return this.handleMenuError(error);
70
+ }
71
+ }
72
+ /**
73
+ * Check if there are any stored conversations
74
+ */
75
+ async hasStoredConversations(userId) {
76
+ try {
77
+ const conversations = await this.getAvailableConversations(userId);
78
+ return conversations.length > 0;
79
+ }
80
+ catch (error) {
81
+ logger.debug("Failed to check for stored conversations:", error);
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Close Redis connection
87
+ */
88
+ async close() {
89
+ if (this.redisClient) {
90
+ await this.redisClient.quit();
91
+ this.redisClient = null;
92
+ }
93
+ }
94
+ async scanConversationKeys() {
95
+ if (!this.redisClient) {
96
+ throw new Error("Redis client not initialized");
97
+ }
98
+ const pattern = `${this.redisConfig.keyPrefix}*`;
99
+ const keys = await scanKeys(this.redisClient, pattern);
100
+ logger.debug(`Found ${keys.length} conversation keys in Redis`);
101
+ return keys;
102
+ }
103
+ async processConversationKeys(keys) {
104
+ const summaries = [];
105
+ for (const key of keys) {
106
+ const summary = await this.processSingleConversationKey(key);
107
+ if (summary) {
108
+ summaries.push(summary);
109
+ }
110
+ }
111
+ return summaries;
112
+ }
113
+ async processSingleConversationKey(key) {
114
+ if (!this.redisClient) {
115
+ logger.warn(`Redis client not available for key ${key}`);
116
+ return null;
117
+ }
118
+ try {
119
+ const conversationData = await this.redisClient.get(key);
120
+ const conversation = deserializeConversation(conversationData);
121
+ if (conversation &&
122
+ conversation.messages &&
123
+ conversation.messages.length > 0) {
124
+ return this.createConversationSummary(conversation);
125
+ }
126
+ return null;
127
+ }
128
+ catch (error) {
129
+ logger.warn(`Failed to process conversation key ${key}:`, error);
130
+ return null;
131
+ }
132
+ }
133
+ sortConversationsByDate(summaries) {
134
+ return summaries.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
135
+ }
136
+ updateCache(summaries) {
137
+ this.conversationCache = summaries;
138
+ this.cacheTimestamp = Date.now();
139
+ logger.debug(`Retrieved ${summaries.length} valid conversations`);
140
+ }
141
+ filterConversationsByUser(summaries, userId) {
142
+ if (!userId) {
143
+ return summaries;
144
+ }
145
+ return summaries.filter((summary) => summary.userId === userId);
146
+ }
147
+ /*
148
+ * Create menu choices for inquirer prompt
149
+ */
150
+ createMenuChoices(conversations) {
151
+ const choices = [
152
+ {
153
+ name: chalk.green("🆕 Start New Conversation"),
154
+ value: "NEW_CONVERSATION",
155
+ short: "New Conversation",
156
+ },
157
+ new inquirer.Separator(),
158
+ ];
159
+ for (const conversation of conversations.slice(0, LOOP_DISPLAY_LIMITS.MAX_CONVERSATIONS)) {
160
+ const choice = this.formatConversationChoice(conversation);
161
+ choices.push(choice);
162
+ }
163
+ return choices;
164
+ }
165
+ async showSelectionPrompt(choices) {
166
+ const answer = await inquirer.prompt([
167
+ {
168
+ type: "list",
169
+ name: "selectedConversation",
170
+ message: "Select a conversation to continue:",
171
+ choices,
172
+ pageSize: LOOP_DISPLAY_LIMITS.PAGE_SIZE,
173
+ },
174
+ ]);
175
+ return answer.selectedConversation;
176
+ }
177
+ handleRetrievalError(error) {
178
+ logger.error("Failed to retrieve conversations:", error);
179
+ return [];
180
+ }
181
+ handleMenuError(error) {
182
+ logger.error("Failed to display conversation menu:", error);
183
+ return "NEW_CONVERSATION";
184
+ }
185
+ createConversationSummary(conversation) {
186
+ const messages = conversation.messages;
187
+ const firstMessage = messages[0];
188
+ const lastMessage = messages[messages.length - 1];
189
+ return {
190
+ sessionId: conversation.sessionId,
191
+ id: conversation.id,
192
+ title: conversation.title || generateConversationTitle(firstMessage.content),
193
+ firstMessage: {
194
+ content: truncateText(firstMessage.content, LOOP_DISPLAY_LIMITS.CONTENT_LENGTH),
195
+ timestamp: firstMessage.timestamp || conversation.createdAt,
196
+ },
197
+ lastMessage: {
198
+ content: truncateText(lastMessage.content, LOOP_DISPLAY_LIMITS.CONTENT_LENGTH),
199
+ timestamp: lastMessage.timestamp || conversation.updatedAt,
200
+ },
201
+ messageCount: messages.length,
202
+ userId: conversation.userId,
203
+ duration: formatTimeAgo(conversation.updatedAt),
204
+ createdAt: conversation.createdAt,
205
+ updatedAt: conversation.updatedAt,
206
+ };
207
+ }
208
+ formatConversationChoice(summary) {
209
+ const icon = getContentIcon(summary.firstMessage.content);
210
+ const title = chalk.white(summary.title || "Untitled Conversation");
211
+ const duration = chalk.gray(`(${summary.duration})`);
212
+ const details = chalk.gray(` └ ${summary.messageCount} message${summary.messageCount !== 1 ? "s" : ""} | ` +
213
+ `Session: ${summary.sessionId.slice(0, LOOP_DISPLAY_LIMITS.SESSION_ID_DISPLAY)}... | ` +
214
+ `Updated: ${new Date(summary.updatedAt).toLocaleString()}`);
215
+ const name = `${icon} ${title} ${duration}\n${details}`;
216
+ return {
217
+ name,
218
+ value: summary.sessionId,
219
+ short: `${summary.title} (${summary.sessionId.slice(0, LOOP_DISPLAY_LIMITS.SESSION_ID_SHORT)}...)`,
220
+ };
221
+ }
222
+ }
@@ -12,4 +12,4 @@ export interface OptionSchema {
12
12
  * This object provides metadata for validation and help text in the CLI loop.
13
13
  * It is derived from the main TextGenerationOptions interface to ensure consistency.
14
14
  */
15
- export declare const textGenerationOptionsSchema: Record<keyof Omit<TextGenerationOptions, "prompt" | "input" | "schema" | "tools" | "context" | "conversationHistory" | "conversationMessages" | "conversationMemoryConfig" | "originalPrompt" | "middleware" | "expectedOutcome" | "evaluationCriteria" | "region">, OptionSchema>;
15
+ export declare const textGenerationOptionsSchema: Record<keyof Omit<TextGenerationOptions, "prompt" | "input" | "schema" | "tools" | "context" | "conversationHistory" | "conversationMessages" | "conversationMemoryConfig" | "originalPrompt" | "middleware" | "expectedOutcome" | "evaluationCriteria" | "region" | "csvOptions">, OptionSchema>;
@@ -2,27 +2,55 @@ import type { Argv } from "yargs";
2
2
  import type { ConversationMemoryConfig } from "../../lib/types/conversation.js";
3
3
  export declare class LoopSession {
4
4
  private conversationMemoryConfig?;
5
+ private options?;
5
6
  private initializeCliParser;
6
7
  private isRunning;
7
8
  private sessionId?;
8
9
  private commandHistory;
9
10
  private sessionVariablesSchema;
10
- constructor(initializeCliParser: () => Argv, conversationMemoryConfig?: ConversationMemoryConfig | undefined);
11
+ constructor(initializeCliParser: () => Argv, conversationMemoryConfig?: ConversationMemoryConfig | undefined, options?: {
12
+ directResumeSessionId?: string;
13
+ forceNewSession?: boolean;
14
+ } | undefined);
11
15
  start(): Promise<void>;
16
+ /**
17
+ * Handle direct session resume from CLI option
18
+ */
19
+ private handleDirectSessionResume;
20
+ /**
21
+ * Handle conversation selection logic when no direct resume is specified
22
+ */
23
+ private handleConversationSelection;
24
+ /**
25
+ * Clean up session resources and connections
26
+ */
27
+ private cleanup;
12
28
  private handleSessionCommands;
13
- private parseValue;
14
29
  private showHelp;
15
30
  private showSetHelp;
16
31
  /**
17
- * Load command history from the global history file
32
+ * Get command input with history support using readline
33
+ */
34
+ private getCommandWithHistory;
35
+ /**
36
+ * Restore a conversation session and set up the global session state
18
37
  */
19
- private loadHistory;
38
+ private restoreSession;
20
39
  /**
21
- * Save a command to the global history file
40
+ * Create NeuroLink instance and validate conversation in one step
41
+ * Eliminates redundant instance creation and initialization
22
42
  */
23
- private saveCommand;
43
+ private createAndValidateNeurolinkInstance;
24
44
  /**
25
- * Get command input with history support using readline
45
+ * Configure tool execution context for the restored session
26
46
  */
27
- private getCommandWithHistory;
47
+ private configureToolContext;
48
+ /**
49
+ * Verify that tools are available and working in the restored session
50
+ */
51
+ private verifyToolAvailability;
52
+ /**
53
+ * Restore global session state and session variables
54
+ */
55
+ private restoreGlobalSessionState;
28
56
  }