@juspay/neurolink 9.10.0 → 9.11.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 (174) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/adapters/video/videoAnalyzer.d.ts +3 -3
  3. package/dist/adapters/video/videoAnalyzer.js +39 -25
  4. package/dist/agent/directTools.d.ts +3 -3
  5. package/dist/cli/commands/config.d.ts +9 -9
  6. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  7. package/dist/constants/contextWindows.d.ts +6 -3
  8. package/dist/constants/contextWindows.js +30 -3
  9. package/dist/constants/index.d.ts +3 -3
  10. package/dist/constants/retry.d.ts +4 -4
  11. package/dist/constants/retry.js +1 -1
  12. package/dist/context/contextCompactor.d.ts +1 -1
  13. package/dist/context/contextCompactor.js +59 -1
  14. package/dist/context/summarizationEngine.d.ts +2 -2
  15. package/dist/context/summarizationEngine.js +44 -18
  16. package/dist/context/toolOutputLimits.d.ts +22 -13
  17. package/dist/context/toolOutputLimits.js +58 -64
  18. package/dist/core/baseProvider.d.ts +11 -2
  19. package/dist/core/baseProvider.js +30 -1
  20. package/dist/core/conversationMemoryManager.d.ts +13 -1
  21. package/dist/core/conversationMemoryManager.js +36 -5
  22. package/dist/core/modules/GenerationHandler.d.ts +6 -0
  23. package/dist/core/modules/GenerationHandler.js +192 -7
  24. package/dist/core/modules/MessageBuilder.js +42 -4
  25. package/dist/core/modules/TelemetryHandler.js +4 -1
  26. package/dist/core/redisConversationMemoryManager.d.ts +19 -3
  27. package/dist/core/redisConversationMemoryManager.js +253 -58
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +3 -0
  30. package/dist/lib/adapters/video/videoAnalyzer.d.ts +3 -3
  31. package/dist/lib/adapters/video/videoAnalyzer.js +39 -25
  32. package/dist/lib/agent/directTools.d.ts +7 -7
  33. package/dist/lib/constants/contextWindows.d.ts +6 -3
  34. package/dist/lib/constants/contextWindows.js +30 -3
  35. package/dist/lib/constants/index.d.ts +3 -3
  36. package/dist/lib/constants/retry.d.ts +4 -4
  37. package/dist/lib/constants/retry.js +1 -1
  38. package/dist/lib/context/contextCompactor.d.ts +1 -1
  39. package/dist/lib/context/contextCompactor.js +59 -1
  40. package/dist/lib/context/summarizationEngine.d.ts +2 -2
  41. package/dist/lib/context/summarizationEngine.js +44 -18
  42. package/dist/lib/context/toolOutputLimits.d.ts +22 -13
  43. package/dist/lib/context/toolOutputLimits.js +58 -64
  44. package/dist/lib/core/baseProvider.d.ts +11 -2
  45. package/dist/lib/core/baseProvider.js +30 -1
  46. package/dist/lib/core/conversationMemoryManager.d.ts +13 -1
  47. package/dist/lib/core/conversationMemoryManager.js +36 -5
  48. package/dist/lib/core/modules/GenerationHandler.d.ts +6 -0
  49. package/dist/lib/core/modules/GenerationHandler.js +192 -7
  50. package/dist/lib/core/modules/MessageBuilder.js +42 -4
  51. package/dist/lib/core/modules/TelemetryHandler.js +4 -1
  52. package/dist/lib/core/redisConversationMemoryManager.d.ts +19 -3
  53. package/dist/lib/core/redisConversationMemoryManager.js +253 -58
  54. package/dist/lib/files/fileTools.d.ts +3 -3
  55. package/dist/lib/index.d.ts +2 -0
  56. package/dist/lib/index.js +3 -0
  57. package/dist/lib/mcp/externalServerManager.js +36 -1
  58. package/dist/lib/memory/memoryRetrievalTools.d.ts +166 -0
  59. package/dist/lib/memory/memoryRetrievalTools.js +145 -0
  60. package/dist/lib/neurolink.d.ts +35 -1
  61. package/dist/lib/neurolink.js +471 -16
  62. package/dist/lib/providers/amazonBedrock.d.ts +1 -1
  63. package/dist/lib/providers/amazonBedrock.js +78 -45
  64. package/dist/lib/providers/amazonSagemaker.d.ts +1 -1
  65. package/dist/lib/providers/amazonSagemaker.js +1 -1
  66. package/dist/lib/providers/anthropic.d.ts +1 -1
  67. package/dist/lib/providers/anthropic.js +7 -7
  68. package/dist/lib/providers/anthropicBaseProvider.d.ts +1 -1
  69. package/dist/lib/providers/anthropicBaseProvider.js +7 -6
  70. package/dist/lib/providers/azureOpenai.d.ts +1 -1
  71. package/dist/lib/providers/azureOpenai.js +1 -1
  72. package/dist/lib/providers/googleAiStudio.d.ts +1 -1
  73. package/dist/lib/providers/googleAiStudio.js +5 -5
  74. package/dist/lib/providers/googleVertex.d.ts +1 -1
  75. package/dist/lib/providers/googleVertex.js +74 -17
  76. package/dist/lib/providers/huggingFace.d.ts +1 -1
  77. package/dist/lib/providers/huggingFace.js +1 -1
  78. package/dist/lib/providers/litellm.d.ts +1 -1
  79. package/dist/lib/providers/litellm.js +18 -16
  80. package/dist/lib/providers/mistral.d.ts +1 -1
  81. package/dist/lib/providers/mistral.js +1 -1
  82. package/dist/lib/providers/ollama.d.ts +1 -1
  83. package/dist/lib/providers/ollama.js +8 -7
  84. package/dist/lib/providers/openAI.d.ts +1 -1
  85. package/dist/lib/providers/openAI.js +6 -6
  86. package/dist/lib/providers/openRouter.d.ts +1 -1
  87. package/dist/lib/providers/openRouter.js +6 -2
  88. package/dist/lib/providers/openaiCompatible.d.ts +1 -1
  89. package/dist/lib/providers/openaiCompatible.js +1 -1
  90. package/dist/lib/proxy/proxyFetch.js +291 -65
  91. package/dist/lib/server/utils/validation.d.ts +4 -4
  92. package/dist/lib/services/server/ai/observability/instrumentation.js +12 -3
  93. package/dist/lib/telemetry/telemetryService.d.ts +2 -1
  94. package/dist/lib/telemetry/telemetryService.js +8 -1
  95. package/dist/lib/types/contextTypes.d.ts +26 -2
  96. package/dist/lib/types/conversation.d.ts +72 -40
  97. package/dist/lib/types/conversationMemoryInterface.d.ts +5 -1
  98. package/dist/lib/types/generateTypes.d.ts +26 -0
  99. package/dist/lib/types/modelTypes.d.ts +2 -2
  100. package/dist/lib/types/multimodal.d.ts +2 -0
  101. package/dist/lib/types/observability.d.ts +10 -0
  102. package/dist/lib/types/sdkTypes.d.ts +1 -1
  103. package/dist/lib/utils/conversationMemory.d.ts +4 -3
  104. package/dist/lib/utils/conversationMemory.js +44 -6
  105. package/dist/lib/utils/errorHandling.d.ts +5 -0
  106. package/dist/lib/utils/errorHandling.js +7 -2
  107. package/dist/lib/utils/logger.d.ts +8 -0
  108. package/dist/lib/utils/logger.js +56 -1
  109. package/dist/lib/utils/messageBuilder.js +74 -4
  110. package/dist/lib/utils/redis.js +6 -1
  111. package/dist/lib/utils/tokenEstimation.d.ts +2 -2
  112. package/dist/lib/utils/tokenEstimation.js +16 -1
  113. package/dist/lib/utils/videoAnalysisProcessor.d.ts +2 -1
  114. package/dist/lib/utils/videoAnalysisProcessor.js +7 -2
  115. package/dist/lib/workflow/config.d.ts +110 -110
  116. package/dist/mcp/externalServerManager.js +36 -1
  117. package/dist/memory/memoryRetrievalTools.d.ts +166 -0
  118. package/dist/memory/memoryRetrievalTools.js +144 -0
  119. package/dist/neurolink.d.ts +35 -1
  120. package/dist/neurolink.js +471 -16
  121. package/dist/providers/amazonBedrock.d.ts +1 -1
  122. package/dist/providers/amazonBedrock.js +78 -45
  123. package/dist/providers/amazonSagemaker.d.ts +1 -1
  124. package/dist/providers/amazonSagemaker.js +1 -1
  125. package/dist/providers/anthropic.d.ts +1 -1
  126. package/dist/providers/anthropic.js +7 -7
  127. package/dist/providers/anthropicBaseProvider.d.ts +1 -1
  128. package/dist/providers/anthropicBaseProvider.js +7 -6
  129. package/dist/providers/azureOpenai.d.ts +1 -1
  130. package/dist/providers/azureOpenai.js +1 -1
  131. package/dist/providers/googleAiStudio.d.ts +1 -1
  132. package/dist/providers/googleAiStudio.js +5 -5
  133. package/dist/providers/googleVertex.d.ts +1 -1
  134. package/dist/providers/googleVertex.js +74 -17
  135. package/dist/providers/huggingFace.d.ts +1 -1
  136. package/dist/providers/huggingFace.js +1 -1
  137. package/dist/providers/litellm.d.ts +1 -1
  138. package/dist/providers/litellm.js +18 -16
  139. package/dist/providers/mistral.d.ts +1 -1
  140. package/dist/providers/mistral.js +1 -1
  141. package/dist/providers/ollama.d.ts +1 -1
  142. package/dist/providers/ollama.js +8 -7
  143. package/dist/providers/openAI.d.ts +1 -1
  144. package/dist/providers/openAI.js +6 -6
  145. package/dist/providers/openRouter.d.ts +1 -1
  146. package/dist/providers/openRouter.js +6 -2
  147. package/dist/providers/openaiCompatible.d.ts +1 -1
  148. package/dist/providers/openaiCompatible.js +1 -1
  149. package/dist/proxy/proxyFetch.js +291 -65
  150. package/dist/services/server/ai/observability/instrumentation.js +12 -3
  151. package/dist/telemetry/telemetryService.d.ts +2 -1
  152. package/dist/telemetry/telemetryService.js +8 -1
  153. package/dist/types/contextTypes.d.ts +26 -2
  154. package/dist/types/conversation.d.ts +72 -40
  155. package/dist/types/conversationMemoryInterface.d.ts +5 -1
  156. package/dist/types/generateTypes.d.ts +26 -0
  157. package/dist/types/modelTypes.d.ts +10 -10
  158. package/dist/types/multimodal.d.ts +2 -0
  159. package/dist/types/observability.d.ts +10 -0
  160. package/dist/types/sdkTypes.d.ts +1 -1
  161. package/dist/utils/conversationMemory.d.ts +4 -3
  162. package/dist/utils/conversationMemory.js +44 -6
  163. package/dist/utils/errorHandling.d.ts +5 -0
  164. package/dist/utils/errorHandling.js +7 -2
  165. package/dist/utils/logger.d.ts +8 -0
  166. package/dist/utils/logger.js +56 -1
  167. package/dist/utils/messageBuilder.js +74 -4
  168. package/dist/utils/redis.js +6 -1
  169. package/dist/utils/tokenEstimation.d.ts +2 -2
  170. package/dist/utils/tokenEstimation.js +16 -1
  171. package/dist/utils/videoAnalysisProcessor.d.ts +2 -1
  172. package/dist/utils/videoAnalysisProcessor.js +7 -2
  173. package/dist/workflow/config.d.ts +12 -12
  174. package/package.json +1 -1
@@ -21,19 +21,20 @@ export class SummarizationEngine {
21
21
  * @param logPrefix - Prefix for log messages
22
22
  * @returns True if summarization was performed
23
23
  */
24
- async checkAndSummarize(session, threshold, config, logPrefix = "[SummarizationEngine]") {
25
- const contextMessages = buildContextFromPointer(session);
24
+ async checkAndSummarize(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) {
25
+ const contextMessages = buildContextFromPointer(session, requestId);
26
26
  const tokenCount = this.estimateTokens(contextMessages);
27
27
  session.lastTokenCount = tokenCount;
28
28
  session.lastCountedAt = Date.now();
29
- logger.debug(`${logPrefix} Token count check`, {
29
+ logger.info("[Summarization] Check", {
30
+ requestId,
30
31
  sessionId: session.sessionId,
31
32
  tokenCount,
32
33
  threshold,
33
- needsSummarization: tokenCount >= threshold,
34
+ willSummarize: tokenCount >= threshold,
34
35
  });
35
36
  if (tokenCount >= threshold) {
36
- await this.summarizeSession(session, threshold, config, logPrefix);
37
+ await this.summarizeSession(session, threshold, config, logPrefix, requestId);
37
38
  return true;
38
39
  }
39
40
  return false;
@@ -46,7 +47,8 @@ export class SummarizationEngine {
46
47
  * @param config - Conversation memory configuration (partial allowed)
47
48
  * @param logPrefix - Prefix for log messages
48
49
  */
49
- async summarizeSession(session, threshold, config, logPrefix = "[SummarizationEngine]") {
50
+ async summarizeSession(session, threshold, config, logPrefix = "[SummarizationEngine]", requestId) {
51
+ const startTime = Date.now();
50
52
  const startIndex = session.summarizedUpToMessageId
51
53
  ? session.messages.findIndex((m) => m.id === session.summarizedUpToMessageId) + 1
52
54
  : 0;
@@ -60,21 +62,45 @@ export class SummarizationEngine {
60
62
  if (messagesToSummarize.length === 0) {
61
63
  return;
62
64
  }
63
- const summary = await generateSummary(messagesToSummarize, config, logPrefix, session.summarizedMessage);
64
- if (!summary) {
65
- logger.warn(`${logPrefix} Summary generation failed`, {
65
+ const recentToKeep = recentMessages.length - messagesToSummarize.length;
66
+ logger.info("[Summarization] Starting", {
67
+ requestId,
68
+ sessionId: session.sessionId,
69
+ messagesToSummarize: messagesToSummarize.length,
70
+ recentToKeep,
71
+ hasPreviousSummary: !!session.summarizedMessage,
72
+ });
73
+ try {
74
+ const summary = await generateSummary(messagesToSummarize, config, logPrefix, session.summarizedMessage, requestId);
75
+ if (!summary) {
76
+ logger.warn(`${logPrefix} Summary generation failed`, {
77
+ requestId,
78
+ sessionId: session.sessionId,
79
+ durationMs: Date.now() - startTime,
80
+ });
81
+ return;
82
+ }
83
+ const lastSummarized = messagesToSummarize[messagesToSummarize.length - 1];
84
+ session.summarizedUpToMessageId = lastSummarized.id;
85
+ session.summarizedMessage = summary;
86
+ logger.info("[Summarization] Complete", {
87
+ requestId,
66
88
  sessionId: session.sessionId,
89
+ summaryChars: summary.length,
90
+ newPointerId: lastSummarized.id,
91
+ durationMs: Date.now() - startTime,
67
92
  });
68
- return;
69
93
  }
70
- const lastSummarized = messagesToSummarize[messagesToSummarize.length - 1];
71
- session.summarizedUpToMessageId = lastSummarized.id;
72
- session.summarizedMessage = summary;
73
- logger.info(`${logPrefix} Summarization complete`, {
74
- sessionId: session.sessionId,
75
- summarizedCount: messagesToSummarize.length,
76
- totalMessages: session.messages.length,
77
- });
94
+ catch (err) {
95
+ const errorMessage = err instanceof Error ? err.message : String(err);
96
+ logger.error("[Summarization] Error", {
97
+ requestId,
98
+ sessionId: session.sessionId,
99
+ error: errorMessage,
100
+ durationMs: Date.now() - startTime,
101
+ });
102
+ throw err;
103
+ }
78
104
  }
79
105
  /**
80
106
  * Estimate total tokens for a message array.
@@ -1,17 +1,26 @@
1
1
  /**
2
- * Tool Output Size Limits
3
- *
4
- * Truncates tool outputs exceeding size limits.
5
- * Can save full output to disk with a pointer.
6
- * Modeled on OpenCode's approach.
2
+ * Tool output preview generation.
3
+ * Generates head/tail previews of large tool outputs for context-efficient LLM calls.
4
+ * @module
7
5
  */
8
- /** Maximum tool output size in bytes (50KB) */
9
- export declare const MAX_TOOL_OUTPUT_BYTES: number;
10
- /** Maximum tool output lines */
11
- export declare const MAX_TOOL_OUTPUT_LINES = 2000;
12
- import type { TruncateOptions, TruncateResult } from "../types/contextTypes.js";
13
- export type { TruncateOptions, TruncateResult } from "../types/contextTypes.js";
6
+ import type { ToolOutputPreviewOptions, ToolOutputPreviewResult } from "../types/contextTypes.js";
7
+ export type { ToolOutputPreviewOptions, ToolOutputPreviewResult, } from "../types/contextTypes.js";
8
+ /** Default maximum preview size in bytes (50KB) */
9
+ export declare const DEFAULT_MAX_PREVIEW_BYTES: number;
10
+ /** Default maximum preview lines */
11
+ export declare const DEFAULT_MAX_PREVIEW_LINES = 2000;
12
+ /** Default head ratio (25% of preview budget) */
13
+ export declare const DEFAULT_HEAD_RATIO = 0.25;
14
+ /** Tool name referenced in truncation notices for on-demand full-output access */
15
+ export declare const RETRIEVE_CONTEXT_TOOL_NAME = "retrieve_context";
16
+ /** Default tail ratio (75% of preview budget) */
17
+ export declare const DEFAULT_TAIL_RATIO = 0.75;
14
18
  /**
15
- * Truncate tool output if it exceeds size limits.
19
+ * Generate a head/tail preview of a tool output string.
20
+ * If the output is within limits, returns it unchanged with truncated: false.
21
+ * If over limits, keeps the first 25% and last 75% with an omission notice.
22
+ *
23
+ * Industry pattern: 25/75 head/tail split. Head captures schema/headers/structure,
24
+ * tail captures the most recent and typically most relevant data.
16
25
  */
17
- export declare function truncateToolOutput(output: string, options?: TruncateOptions): TruncateResult;
26
+ export declare function generateToolOutputPreview(output: string, options?: ToolOutputPreviewOptions): ToolOutputPreviewResult;
@@ -1,84 +1,78 @@
1
1
  /**
2
- * Tool Output Size Limits
3
- *
4
- * Truncates tool outputs exceeding size limits.
5
- * Can save full output to disk with a pointer.
6
- * Modeled on OpenCode's approach.
2
+ * Tool output preview generation.
3
+ * Generates head/tail previews of large tool outputs for context-efficient LLM calls.
4
+ * @module
7
5
  */
8
- import { writeFileSync, mkdirSync } from "fs";
9
- import { join } from "path";
10
- import { randomUUID } from "crypto";
11
- import { tmpdir } from "os";
12
- /** Maximum tool output size in bytes (50KB) */
13
- export const MAX_TOOL_OUTPUT_BYTES = 50 * 1024;
14
- /** Maximum tool output lines */
15
- export const MAX_TOOL_OUTPUT_LINES = 2_000;
6
+ /** Default maximum preview size in bytes (50KB) */
7
+ export const DEFAULT_MAX_PREVIEW_BYTES = 50 * 1024;
8
+ /** Default maximum preview lines */
9
+ export const DEFAULT_MAX_PREVIEW_LINES = 2_000;
10
+ /** Default head ratio (25% of preview budget) */
11
+ export const DEFAULT_HEAD_RATIO = 0.25;
12
+ /** Tool name referenced in truncation notices for on-demand full-output access */
13
+ export const RETRIEVE_CONTEXT_TOOL_NAME = "retrieve_context";
14
+ /** Default tail ratio (75% of preview budget) */
15
+ export const DEFAULT_TAIL_RATIO = 0.75;
16
16
  /**
17
- * Truncate tool output if it exceeds size limits.
17
+ * Generate a head/tail preview of a tool output string.
18
+ * If the output is within limits, returns it unchanged with truncated: false.
19
+ * If over limits, keeps the first 25% and last 75% with an omission notice.
20
+ *
21
+ * Industry pattern: 25/75 head/tail split. Head captures schema/headers/structure,
22
+ * tail captures the most recent and typically most relevant data.
18
23
  */
19
- export function truncateToolOutput(output, options) {
20
- const maxBytes = options?.maxBytes ?? MAX_TOOL_OUTPUT_BYTES;
21
- const maxLines = options?.maxLines ?? MAX_TOOL_OUTPUT_LINES;
22
- const direction = options?.direction ?? "tail";
23
- const saveToDisk = options?.saveToDisk ?? false;
24
+ export function generateToolOutputPreview(output, options) {
25
+ const maxBytes = options?.maxBytes ?? DEFAULT_MAX_PREVIEW_BYTES;
26
+ const maxLines = options?.maxLines ?? DEFAULT_MAX_PREVIEW_LINES;
27
+ const rawHeadRatio = options?.headRatio ?? DEFAULT_HEAD_RATIO;
28
+ const rawTailRatio = options?.tailRatio ?? DEFAULT_TAIL_RATIO;
29
+ // Clamp ratios to valid range to avoid negative omittedBytes
30
+ const headRatio = Math.max(0, Math.min(1, rawHeadRatio));
31
+ const tailRatio = Math.max(0, Math.min(1, rawTailRatio));
24
32
  const originalSize = Buffer.byteLength(output, "utf-8");
25
- // Check byte limit
26
- const exceedsBytes = originalSize > maxBytes;
27
- // Check line limit
28
33
  const lines = output.split("\n");
34
+ const exceedsBytes = originalSize > maxBytes;
29
35
  const exceedsLines = lines.length > maxLines;
30
36
  if (!exceedsBytes && !exceedsLines) {
31
- return { content: output, truncated: false, originalSize };
37
+ return { preview: output, truncated: false, originalSize };
32
38
  }
33
- // Save to disk if requested
34
- let savedPath;
35
- if (saveToDisk) {
36
- try {
37
- const saveDir = options?.saveDir ?? join(tmpdir(), "neurolink-tool-output");
38
- mkdirSync(saveDir, { recursive: true });
39
- savedPath = join(saveDir, `tool-output-${randomUUID()}.txt`);
40
- writeFileSync(savedPath, output, "utf-8");
41
- }
42
- catch {
43
- // Silently fail disk save
44
- }
45
- }
46
- // Apply truncation
47
- let truncated;
39
+ // Line-based split
40
+ const headLineCount = Math.max(1, Math.floor(maxLines * headRatio));
41
+ const tailLineCount = Math.max(1, maxLines - headLineCount);
42
+ let head;
43
+ let tail;
48
44
  if (exceedsLines) {
49
- if (direction === "head") {
50
- truncated = lines.slice(0, maxLines).join("\n");
51
- }
52
- else {
53
- truncated = lines.slice(-maxLines).join("\n");
54
- }
45
+ head = lines.slice(0, headLineCount).join("\n");
46
+ tail = lines.slice(-tailLineCount).join("\n");
55
47
  }
56
48
  else {
57
- truncated = output;
58
- }
59
- // Apply byte limit
60
- if (Buffer.byteLength(truncated, "utf-8") > maxBytes) {
61
- if (direction === "head") {
62
- truncated = truncated.slice(0, maxBytes);
63
- }
64
- else {
65
- truncated = truncated.slice(-maxBytes);
66
- }
49
+ head = lines
50
+ .slice(0, Math.max(1, Math.floor(lines.length * headRatio)))
51
+ .join("\n");
52
+ tail = lines
53
+ .slice(-Math.max(1, Math.ceil(lines.length * tailRatio)))
54
+ .join("\n");
67
55
  }
68
- // Add truncation notice
69
- const notice = savedPath
70
- ? `\n\n[Output truncated from ${originalSize} bytes to ${Buffer.byteLength(truncated, "utf-8")} bytes. Full output saved to: ${savedPath}]`
71
- : `\n\n[Output truncated from ${originalSize} bytes to ${Buffer.byteLength(truncated, "utf-8")} bytes]`;
72
- if (direction === "head") {
73
- truncated = truncated + notice;
56
+ // Byte-based cap on each portion
57
+ const headMaxBytes = Math.floor(maxBytes * headRatio);
58
+ const tailMaxBytes = maxBytes - headMaxBytes;
59
+ if (Buffer.byteLength(head, "utf-8") > headMaxBytes) {
60
+ head = Buffer.from(head, "utf-8")
61
+ .subarray(0, headMaxBytes)
62
+ .toString("utf-8");
74
63
  }
75
- else {
76
- truncated = notice + "\n" + truncated;
64
+ if (Buffer.byteLength(tail, "utf-8") > tailMaxBytes) {
65
+ const tailBuf = Buffer.from(tail, "utf-8");
66
+ tail = tailBuf.subarray(tailBuf.length - tailMaxBytes).toString("utf-8");
77
67
  }
68
+ const omittedBytes = Math.max(0, originalSize -
69
+ Buffer.byteLength(head, "utf-8") -
70
+ Buffer.byteLength(tail, "utf-8"));
71
+ const notice = `\n\n[... ${omittedBytes} bytes omitted. ` +
72
+ `Use ${RETRIEVE_CONTEXT_TOOL_NAME} tool to access full output ...]\n\n`;
78
73
  return {
79
- content: truncated,
74
+ preview: head + notice + tail,
80
75
  truncated: true,
81
- savedPath,
82
76
  originalSize,
83
77
  };
84
78
  }
@@ -218,9 +218,18 @@ export declare abstract class BaseProvider implements AIProvider {
218
218
  */
219
219
  setSessionContext(sessionId?: string, userId?: string): void;
220
220
  /**
221
- * Provider-specific error handling
221
+ * Provider-specific error formatting.
222
+ * Subclasses implement this to produce human-readable error messages
223
+ * (e.g., "❌ Google Vertex AI Provider Error\n\n...").
222
224
  */
223
- protected abstract handleProviderError(error: unknown): Error;
225
+ protected abstract formatProviderError(error: unknown): Error;
226
+ /**
227
+ * Handle provider errors with abort passthrough.
228
+ * AbortErrors are never wrapped — they must propagate with their
229
+ * original identity so that isAbortError() can detect them in
230
+ * retry/fallback loops (directProviderGeneration, performMCPGenerationRetries).
231
+ */
232
+ protected handleProviderError(error: unknown): Error;
224
233
  /**
225
234
  * Image generation method. Providers that support it should override this.
226
235
  * By default, it throws an error indicating that the functionality is not supported.
@@ -86,6 +86,20 @@ export class BaseProvider {
86
86
  temperature: options.temperature,
87
87
  timestamp: Date.now(),
88
88
  });
89
+ // ===== EARLY MULTIMODAL DETECTION =====
90
+ const hasFileInput = !!options.input?.files?.length || !!options.input?.videoFiles?.length;
91
+ if (hasFileInput) {
92
+ // ===== VIDEO ANALYSIS DETECTION =====
93
+ // Check if video frames are present and handle with fake streaming
94
+ const messages = await this.buildMessagesForStream(options);
95
+ if (hasVideoFrames(messages)) {
96
+ logger.info(`Video frames detected in stream, using fake streaming for video analysis`, {
97
+ provider: this.providerName,
98
+ model: this.modelName,
99
+ });
100
+ return await this.executeFakeStreaming(options, analysisSchema);
101
+ }
102
+ }
89
103
  // 🔧 CRITICAL: Image generation models don't support real streaming
90
104
  // Force fake streaming for image models to ensure image output is yielded
91
105
  const isImageModel = IMAGE_GENERATION_MODELS.some((m) => this.modelName.includes(m));
@@ -160,7 +174,7 @@ export class BaseProvider {
160
174
  temperature: options.temperature,
161
175
  maxTokens: options.maxTokens,
162
176
  tools: options.tools, // 🔧 FIX: Pass user-provided tools (including RAG tools) to generation pipeline
163
- disableTools: false,
177
+ disableTools: !!options.disableTools,
164
178
  maxSteps: options.maxSteps || 5,
165
179
  provider: options.provider,
166
180
  model: options.model,
@@ -753,6 +767,21 @@ export class BaseProvider {
753
767
  this.userId = userId;
754
768
  this.toolsManager.setSessionContext(sessionId, userId);
755
769
  }
770
+ /**
771
+ * Handle provider errors with abort passthrough.
772
+ * AbortErrors are never wrapped — they must propagate with their
773
+ * original identity so that isAbortError() can detect them in
774
+ * retry/fallback loops (directProviderGeneration, performMCPGenerationRetries).
775
+ */
776
+ handleProviderError(error) {
777
+ if (isAbortError(error)) {
778
+ // Preserve AbortError identity — never wrap in provider-specific formatting
779
+ return error instanceof Error
780
+ ? error
781
+ : new DOMException("The operation was aborted", "AbortError");
782
+ }
783
+ return this.formatProviderError(error);
784
+ }
756
785
  /**
757
786
  * Image generation method. Providers that support it should override this.
758
787
  * By default, it throws an error indicating that the functionality is not supported.
@@ -50,7 +50,7 @@ export declare class ConversationMemoryManager implements IConversationMemoryMan
50
50
  * Returns messages from pointer onwards (or all if no pointer)
51
51
  * Now consistently async to match Redis implementation
52
52
  */
53
- buildContextMessages(sessionId: string): Promise<ChatMessage[]>;
53
+ buildContextMessages(sessionId: string, _userId?: string, _enableSummarization?: boolean, requestId?: string): Promise<ChatMessage[]>;
54
54
  getSession(sessionId: string, _userId?: string): SessionMemory | undefined;
55
55
  createSummarySystemMessage(content: string, summarizesFrom?: string, summarizesTo?: string): ChatMessage;
56
56
  private ensureInitialized;
@@ -59,4 +59,16 @@ export declare class ConversationMemoryManager implements IConversationMemoryMan
59
59
  getStats(): Promise<ConversationMemoryStats>;
60
60
  clearSession(sessionId: string): Promise<boolean>;
61
61
  clearAllSessions(): Promise<void>;
62
+ /**
63
+ * Get the raw messages array for a session.
64
+ * Returns the full messages list without context filtering or summarization.
65
+ * Returns a deep copy to prevent external mutation of internal state.
66
+ */
67
+ getSessionMessages(sessionId: string, _userId?: string): Promise<ChatMessage[]>;
68
+ /**
69
+ * Replace the entire messages array for a session.
70
+ * Creates the session if it does not exist.
71
+ * Resets summary pointers since old pointers may reference messages that no longer exist.
72
+ */
73
+ setSessionMessages(sessionId: string, messages: ChatMessage[], userId?: string): Promise<void>;
62
74
  }
@@ -92,11 +92,12 @@ export class ConversationMemoryManager {
92
92
  if (!this.summarizationInProgress.has(options.sessionId)) {
93
93
  setImmediate(async () => {
94
94
  try {
95
- await this.checkAndSummarize(session, tokenThreshold);
95
+ await this.checkAndSummarize(session, tokenThreshold, options.requestId);
96
96
  }
97
97
  catch (error) {
98
98
  logger.error("Background summarization failed", {
99
99
  sessionId: session.sessionId,
100
+ requestId: options.requestId,
100
101
  error: error instanceof Error ? error.message : String(error),
101
102
  });
102
103
  }
@@ -154,7 +155,7 @@ export class ConversationMemoryManager {
154
155
  /**
155
156
  * Check if summarization is needed based on token count
156
157
  */
157
- async checkAndSummarize(session, threshold) {
158
+ async checkAndSummarize(session, threshold, requestId) {
158
159
  // Acquire lock - if already in progress, skip
159
160
  if (this.summarizationInProgress.has(session.sessionId)) {
160
161
  logger.debug("[ConversationMemoryManager] Summarization already in progress, skipping", {
@@ -164,7 +165,7 @@ export class ConversationMemoryManager {
164
165
  }
165
166
  this.summarizationInProgress.add(session.sessionId);
166
167
  try {
167
- await this.summarizationEngine.checkAndSummarize(session, threshold, this.config, "[ConversationMemory]");
168
+ await this.summarizationEngine.checkAndSummarize(session, threshold, this.config, "[ConversationMemory]", requestId);
168
169
  }
169
170
  catch (error) {
170
171
  logger.error("Token counting or summarization failed", {
@@ -195,9 +196,9 @@ export class ConversationMemoryManager {
195
196
  * Returns messages from pointer onwards (or all if no pointer)
196
197
  * Now consistently async to match Redis implementation
197
198
  */
198
- async buildContextMessages(sessionId) {
199
+ async buildContextMessages(sessionId, _userId, _enableSummarization, requestId) {
199
200
  const session = this.sessions.get(sessionId);
200
- return session ? buildContextFromPointer(session) : [];
201
+ return session ? buildContextFromPointer(session, requestId) : [];
201
202
  }
202
203
  getSession(sessionId, _userId) {
203
204
  return this.sessions.get(sessionId);
@@ -263,4 +264,34 @@ export class ConversationMemoryManager {
263
264
  this.sessions.clear();
264
265
  logger.info("All sessions cleared", { clearedCount: sessionIds.length });
265
266
  }
267
+ /**
268
+ * Get the raw messages array for a session.
269
+ * Returns the full messages list without context filtering or summarization.
270
+ * Returns a deep copy to prevent external mutation of internal state.
271
+ */
272
+ async getSessionMessages(sessionId, _userId) {
273
+ await this.ensureInitialized();
274
+ const session = this.sessions.get(sessionId);
275
+ return session ? session.messages.map((msg) => ({ ...msg })) : [];
276
+ }
277
+ /**
278
+ * Replace the entire messages array for a session.
279
+ * Creates the session if it does not exist.
280
+ * Resets summary pointers since old pointers may reference messages that no longer exist.
281
+ */
282
+ async setSessionMessages(sessionId, messages, userId) {
283
+ await this.ensureInitialized();
284
+ let session = this.sessions.get(sessionId);
285
+ if (!session) {
286
+ session = this.createNewSession(sessionId, userId);
287
+ this.sessions.set(sessionId, session);
288
+ this.enforceSessionLimit();
289
+ }
290
+ session.messages = [...messages];
291
+ session.summarizedUpToMessageId = undefined;
292
+ session.summarizedMessage = undefined;
293
+ session.lastTokenCount = undefined;
294
+ session.lastCountedAt = undefined;
295
+ session.lastActivity = Date.now();
296
+ }
266
297
  }
@@ -38,6 +38,12 @@ export declare class GenerationHandler {
38
38
  * Execute the generation with AI SDK
39
39
  */
40
40
  executeGeneration(model: LanguageModelV1, messages: CoreMessage[], tools: Record<string, Tool>, options: TextGenerationOptions): Promise<Awaited<ReturnType<typeof generateText>>>;
41
+ /**
42
+ * Extract cache metrics from provider metadata (e.g. Anthropic's providerMetadata.anthropic)
43
+ * The Vercel AI SDK's LanguageModelUsage only has promptTokens/completionTokens/totalTokens.
44
+ * Cache metrics are surfaced via providerMetadata by provider-specific SDK adapters.
45
+ */
46
+ private extractCacheMetricsFromProviderMetadata;
41
47
  /**
42
48
  * Log generation completion information
43
49
  */