@juspay/neurolink 9.25.2 → 9.26.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.
- package/CHANGELOG.md +6 -0
- package/dist/adapters/providerImageAdapter.d.ts +3 -27
- package/dist/adapters/providerImageAdapter.js +9 -199
- package/dist/agent/directTools.d.ts +35 -3
- package/dist/agent/directTools.js +122 -0
- package/dist/cli/commands/config.d.ts +6 -6
- package/dist/context/contextCompactor.d.ts +1 -2
- package/dist/context/contextCompactor.js +7 -1
- package/dist/context/prompts/summarizationPrompt.d.ts +3 -3
- package/dist/context/prompts/summarizationPrompt.js +16 -9
- package/dist/context/stages/structuredSummarizer.d.ts +2 -2
- package/dist/context/stages/structuredSummarizer.js +80 -30
- package/dist/lib/adapters/providerImageAdapter.d.ts +3 -27
- package/dist/lib/adapters/providerImageAdapter.js +9 -199
- package/dist/lib/agent/directTools.d.ts +33 -1
- package/dist/lib/agent/directTools.js +122 -0
- package/dist/lib/context/contextCompactor.d.ts +1 -2
- package/dist/lib/context/contextCompactor.js +7 -1
- package/dist/lib/context/prompts/summarizationPrompt.d.ts +3 -3
- package/dist/lib/context/prompts/summarizationPrompt.js +16 -9
- package/dist/lib/context/stages/structuredSummarizer.d.ts +2 -2
- package/dist/lib/context/stages/structuredSummarizer.js +80 -30
- package/dist/lib/mcp/servers/agent/directToolsServer.js +2 -0
- package/dist/lib/mcp/toolRegistry.d.ts +8 -0
- package/dist/lib/mcp/toolRegistry.js +20 -0
- package/dist/lib/neurolink.d.ts +10 -0
- package/dist/lib/neurolink.js +281 -17
- package/dist/lib/providers/googleAiStudio.js +13 -7
- package/dist/lib/types/configTypes.d.ts +3 -0
- package/dist/lib/types/contextTypes.d.ts +5 -2
- package/dist/lib/types/contextTypes.js +8 -8
- package/dist/lib/types/generateTypes.d.ts +25 -0
- package/dist/lib/types/modelTypes.d.ts +2 -2
- package/dist/lib/utils/messageBuilder.js +2 -0
- package/dist/lib/utils/modelAliasResolver.d.ts +17 -0
- package/dist/lib/utils/modelAliasResolver.js +55 -0
- package/dist/lib/utils/pdfProcessor.d.ts +1 -1
- package/dist/lib/utils/pdfProcessor.js +7 -7
- package/dist/lib/utils/toolUtils.d.ts +8 -0
- package/dist/lib/utils/toolUtils.js +15 -0
- package/dist/lib/workflow/config.d.ts +24 -24
- package/dist/mcp/servers/agent/directToolsServer.js +2 -0
- package/dist/mcp/toolRegistry.d.ts +8 -0
- package/dist/mcp/toolRegistry.js +20 -0
- package/dist/neurolink.d.ts +10 -0
- package/dist/neurolink.js +281 -17
- package/dist/providers/googleAiStudio.js +13 -7
- package/dist/server/utils/validation.d.ts +2 -2
- package/dist/types/configTypes.d.ts +3 -0
- package/dist/types/contextTypes.d.ts +5 -2
- package/dist/types/contextTypes.js +8 -8
- package/dist/types/generateTypes.d.ts +25 -0
- package/dist/utils/messageBuilder.js +2 -0
- package/dist/utils/modelAliasResolver.d.ts +17 -0
- package/dist/utils/modelAliasResolver.js +54 -0
- package/dist/utils/pdfProcessor.d.ts +1 -1
- package/dist/utils/pdfProcessor.js +7 -7
- package/dist/utils/toolUtils.d.ts +8 -0
- package/dist/utils/toolUtils.js +15 -0
- package/dist/workflow/config.d.ts +82 -82
- package/package.json +1 -1
|
@@ -6,9 +6,18 @@ import { tool } from "ai";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import * as path from "path";
|
|
9
|
+
import { execFile } from "child_process";
|
|
9
10
|
import { logger } from "../utils/logger.js";
|
|
10
11
|
import { VertexAI } from "@google-cloud/vertexai";
|
|
11
12
|
import { CSVProcessor } from "../utils/csvProcessor.js";
|
|
13
|
+
import { shouldEnableBashTool } from "../utils/toolUtils.js";
|
|
14
|
+
const MAX_OUTPUT_BYTES = 102400; // 100KB
|
|
15
|
+
function truncateOutput(output) {
|
|
16
|
+
if (output.length > MAX_OUTPUT_BYTES) {
|
|
17
|
+
return (output.slice(0, MAX_OUTPUT_BYTES) + "\n... [output truncated at 100KB]");
|
|
18
|
+
}
|
|
19
|
+
return output;
|
|
20
|
+
}
|
|
12
21
|
// Runtime Google Search tool creation - bypasses TypeScript strict typing
|
|
13
22
|
function createGoogleSearchTools() {
|
|
14
23
|
const searchTool = {};
|
|
@@ -534,6 +543,9 @@ export const directAgentTools = {
|
|
|
534
543
|
}
|
|
535
544
|
},
|
|
536
545
|
}),
|
|
546
|
+
// NOTE: executeBashCommand was moved to a separate opt-in export (bashTool) for security.
|
|
547
|
+
// It is only included in directAgentTools when NEUROLINK_ENABLE_BASH_TOOL=true or
|
|
548
|
+
// toolConfig.enableBashTool is explicitly set to true. See shouldEnableBashTool() in toolUtils.ts.
|
|
537
549
|
websearchGrounding: tool({
|
|
538
550
|
description: "Search the web for current information using Google Search grounding. Returns raw search data for AI processing.",
|
|
539
551
|
parameters: z.object({
|
|
@@ -657,6 +669,116 @@ export const directAgentTools = {
|
|
|
657
669
|
},
|
|
658
670
|
}),
|
|
659
671
|
};
|
|
672
|
+
/**
|
|
673
|
+
* Bash command execution tool - exported separately for opt-in use.
|
|
674
|
+
*
|
|
675
|
+
* SECURITY: This tool is NOT included in directAgentTools by default.
|
|
676
|
+
* It must be explicitly enabled via:
|
|
677
|
+
* - Environment variable: NEUROLINK_ENABLE_BASH_TOOL=true
|
|
678
|
+
* - Config: toolConfig.enableBashTool = true
|
|
679
|
+
*
|
|
680
|
+
* Import this directly when you need bash execution capabilities:
|
|
681
|
+
* import { bashTool } from '../agent/directTools.js';
|
|
682
|
+
*/
|
|
683
|
+
export const bashTool = tool({
|
|
684
|
+
description: "Execute a bash/shell command and return stdout, stderr, and exit code. Supports full shell syntax including pipes, redirects, and variable expansion. Requires HITL confirmation when enabled.",
|
|
685
|
+
parameters: z.object({
|
|
686
|
+
command: z
|
|
687
|
+
.string()
|
|
688
|
+
.describe("The shell command to execute (supports pipes, redirects, etc.)"),
|
|
689
|
+
timeout: z
|
|
690
|
+
.number()
|
|
691
|
+
.optional()
|
|
692
|
+
.default(30000)
|
|
693
|
+
.describe("Timeout in milliseconds (default: 30000, max: 120000)"),
|
|
694
|
+
cwd: z
|
|
695
|
+
.string()
|
|
696
|
+
.optional()
|
|
697
|
+
.describe("Working directory (defaults to process.cwd())"),
|
|
698
|
+
}),
|
|
699
|
+
execute: async ({ command, timeout = 30000, cwd }) => {
|
|
700
|
+
try {
|
|
701
|
+
const effectiveTimeout = Math.min(Math.max(timeout, 100), 120000);
|
|
702
|
+
const resolvedCwd = cwd ? path.resolve(cwd) : process.cwd();
|
|
703
|
+
const currentCwd = process.cwd();
|
|
704
|
+
// Verify cwd exists before resolving symlinks
|
|
705
|
+
if (!fs.existsSync(resolvedCwd) ||
|
|
706
|
+
!fs.statSync(resolvedCwd).isDirectory()) {
|
|
707
|
+
return {
|
|
708
|
+
success: false,
|
|
709
|
+
code: -1,
|
|
710
|
+
stdout: "",
|
|
711
|
+
stderr: "",
|
|
712
|
+
error: `Directory does not exist: ${resolvedCwd}`,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
// Security: resolve symlinks and prevent execution outside current directory
|
|
716
|
+
try {
|
|
717
|
+
const realCwd = fs.realpathSync(currentCwd);
|
|
718
|
+
const realResolvedCwd = fs.realpathSync(resolvedCwd);
|
|
719
|
+
if (!realResolvedCwd.startsWith(realCwd)) {
|
|
720
|
+
return {
|
|
721
|
+
success: false,
|
|
722
|
+
code: -1,
|
|
723
|
+
stdout: "",
|
|
724
|
+
stderr: "",
|
|
725
|
+
error: "Access denied: Cannot execute commands outside current directory",
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
catch {
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
code: -1,
|
|
733
|
+
stdout: "",
|
|
734
|
+
stderr: "",
|
|
735
|
+
error: "Access denied: Cannot resolve directory path",
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
// Use /bin/bash -c to support full shell syntax (pipes, redirects, etc.)
|
|
739
|
+
return await new Promise((resolve) => {
|
|
740
|
+
execFile("/bin/bash", ["-c", command], {
|
|
741
|
+
timeout: effectiveTimeout,
|
|
742
|
+
cwd: resolvedCwd,
|
|
743
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
744
|
+
}, (error, stdout, stderr) => {
|
|
745
|
+
if (error) {
|
|
746
|
+
const exitCode = typeof error.code === "number" ? error.code : 1;
|
|
747
|
+
resolve({
|
|
748
|
+
success: false,
|
|
749
|
+
code: exitCode,
|
|
750
|
+
stdout: truncateOutput(stdout || ""),
|
|
751
|
+
stderr: truncateOutput(stderr || error.message),
|
|
752
|
+
error: error.killed ? "Command timed out" : error.message,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
resolve({
|
|
757
|
+
success: true,
|
|
758
|
+
code: 0,
|
|
759
|
+
stdout: truncateOutput(stdout),
|
|
760
|
+
stderr: truncateOutput(stderr),
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
return {
|
|
768
|
+
success: false,
|
|
769
|
+
code: -1,
|
|
770
|
+
stdout: "",
|
|
771
|
+
stderr: "",
|
|
772
|
+
error: error instanceof Error ? error.message : String(error),
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
// Conditionally inject executeBashCommand into directAgentTools when opted in.
|
|
778
|
+
// This ensures the tool is only available to SDK consumers who explicitly enable it.
|
|
779
|
+
if (shouldEnableBashTool()) {
|
|
780
|
+
directAgentTools.executeBashCommand = bashTool;
|
|
781
|
+
}
|
|
660
782
|
// eslint-disable-next-line no-redeclare
|
|
661
783
|
export function getToolsForCategory(category = "all") {
|
|
662
784
|
switch (category) {
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { ChatMessage, ConversationMemoryConfig } from "../types/conversation.js";
|
|
12
12
|
import type { CompactionResult, CompactionConfig } from "../types/contextTypes.js";
|
|
13
|
-
export type { CompactionResult,
|
|
14
|
-
export type CompactionStage = "prune" | "deduplicate" | "summarize" | "truncate";
|
|
13
|
+
export type { CompactionConfig, CompactionResult, CompactionStage, } from "../types/contextTypes.js";
|
|
15
14
|
export declare class ContextCompactor {
|
|
16
15
|
private config;
|
|
17
16
|
constructor(config?: CompactionConfig);
|
|
@@ -15,8 +15,8 @@ import { SpanSerializer, SpanType, SpanStatus, } from "../observability/index.js
|
|
|
15
15
|
import { getMetricsAggregator } from "../observability/index.js";
|
|
16
16
|
import { pruneToolOutputs } from "./stages/toolOutputPruner.js";
|
|
17
17
|
import { deduplicateFileReads } from "./stages/fileReadDeduplicator.js";
|
|
18
|
-
import { summarizeMessages } from "./stages/structuredSummarizer.js";
|
|
19
18
|
import { truncateWithSlidingWindow } from "./stages/slidingWindowTruncator.js";
|
|
19
|
+
import { summarizeMessages } from "./stages/structuredSummarizer.js";
|
|
20
20
|
const DEFAULT_CONFIG = {
|
|
21
21
|
enablePrune: true,
|
|
22
22
|
enableDeduplicate: true,
|
|
@@ -106,6 +106,7 @@ export class ContextCompactor {
|
|
|
106
106
|
model: this.config.summarizationModel,
|
|
107
107
|
keepRecentRatio: this.config.keepRecentRatio,
|
|
108
108
|
memoryConfig,
|
|
109
|
+
targetTokens,
|
|
109
110
|
}), 120_000, "LLM summarization timed out after 120s");
|
|
110
111
|
if (summarizeResult.summarized) {
|
|
111
112
|
currentMessages = summarizeResult.messages;
|
|
@@ -146,6 +147,11 @@ export class ContextCompactor {
|
|
|
146
147
|
const stageTokensBefore = estimateMessagesTokens(currentMessages, provider);
|
|
147
148
|
const truncResult = truncateWithSlidingWindow(currentMessages, {
|
|
148
149
|
fraction: this.config.truncationFraction,
|
|
150
|
+
currentTokens: stageTokensBefore,
|
|
151
|
+
targetTokens: targetTokens,
|
|
152
|
+
provider: provider,
|
|
153
|
+
adaptiveBuffer: 0.15,
|
|
154
|
+
maxIterations: 3,
|
|
149
155
|
});
|
|
150
156
|
if (truncResult.truncated) {
|
|
151
157
|
currentMessages = truncResult.messages;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Summarization Prompt Builder
|
|
3
3
|
*
|
|
4
|
-
* Builds prompts for summarizing conversation context into a
|
|
4
|
+
* Builds prompts for summarizing conversation context into a 10-section structure.
|
|
5
5
|
* Supports both initial summarization and incremental merging with existing summaries.
|
|
6
6
|
*/
|
|
7
7
|
import type { SummarizationPromptOptions } from "../../types/contextTypes.js";
|
|
8
8
|
export type { SummarizationPromptOptions } from "../../types/contextTypes.js";
|
|
9
|
-
declare const
|
|
9
|
+
declare const SUMMARY_SECTIONS: string[];
|
|
10
10
|
/**
|
|
11
11
|
* Builds a summarization prompt based on the provided options.
|
|
12
12
|
*
|
|
@@ -14,4 +14,4 @@ declare const NINE_SECTIONS: string[];
|
|
|
14
14
|
* @returns The constructed prompt string
|
|
15
15
|
*/
|
|
16
16
|
export declare function buildSummarizationPrompt(options: SummarizationPromptOptions): string;
|
|
17
|
-
export {
|
|
17
|
+
export { SUMMARY_SECTIONS };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Summarization Prompt Builder
|
|
3
3
|
*
|
|
4
|
-
* Builds prompts for summarizing conversation context into a
|
|
4
|
+
* Builds prompts for summarizing conversation context into a 10-section structure.
|
|
5
5
|
* Supports both initial summarization and incremental merging with existing summaries.
|
|
6
6
|
*/
|
|
7
|
-
const
|
|
7
|
+
const SUMMARY_SECTIONS = [
|
|
8
8
|
"Primary Request and Intent",
|
|
9
9
|
"Key Technical Concepts",
|
|
10
10
|
"Files and Code Sections",
|
|
@@ -14,6 +14,7 @@ const NINE_SECTIONS = [
|
|
|
14
14
|
"Current Work",
|
|
15
15
|
"Next Step",
|
|
16
16
|
"Required Files",
|
|
17
|
+
"Constraints and Established Rules",
|
|
17
18
|
];
|
|
18
19
|
function buildFileContextSection(filesRead, filesModified) {
|
|
19
20
|
const hasFiles = (filesRead && filesRead.length > 0) ||
|
|
@@ -38,7 +39,7 @@ function buildFileContextSection(filesRead, filesModified) {
|
|
|
38
39
|
}
|
|
39
40
|
function buildInitialPrompt(options) {
|
|
40
41
|
const fileContext = buildFileContextSection(options.filesRead, options.filesModified);
|
|
41
|
-
return `You are a context summarization assistant. Your task is to analyze the conversation and create a structured summary following a
|
|
42
|
+
return `You are a context summarization assistant. Your task is to analyze the conversation and create a structured summary following a 10-section format.
|
|
42
43
|
|
|
43
44
|
Create a summary with the following sections:
|
|
44
45
|
|
|
@@ -67,7 +68,12 @@ What is being actively worked on right now?
|
|
|
67
68
|
What is the immediate next action to take?
|
|
68
69
|
|
|
69
70
|
### 9. Required Files
|
|
70
|
-
What files will need to be accessed or modified to continue
|
|
71
|
+
What files will need to be accessed or modified to continue?
|
|
72
|
+
|
|
73
|
+
### 10. Constraints and Established Rules
|
|
74
|
+
What user-imposed constraints, behavioral directives, coding standards, agreed-upon decisions, or established rules must persist across the conversation? Include any "always do X", "never do Y", naming conventions, architectural patterns, or preferences the user has stated.${fileContext}
|
|
75
|
+
|
|
76
|
+
IMPORTANT: Section 10 (Constraints and Established Rules) must ALWAYS be preserved in full. User constraints and established agreements are never "no longer relevant" — they remain in effect for the entire session unless the user explicitly revokes them.
|
|
71
77
|
|
|
72
78
|
Analyze the conversation thoroughly and fill in each section with relevant information. If a section is not applicable, write "N/A" for that section.`;
|
|
73
79
|
}
|
|
@@ -80,18 +86,19 @@ Existing Summary:
|
|
|
80
86
|
${options.previousSummary}
|
|
81
87
|
---
|
|
82
88
|
|
|
83
|
-
Update sections with new information while preserving important context from the existing summary. Maintain the same
|
|
89
|
+
Update sections with new information while preserving important context from the existing summary. Maintain the same 10-section structure.
|
|
84
90
|
|
|
85
91
|
Instructions:
|
|
86
92
|
1. Review the existing summary above
|
|
87
93
|
2. Analyze the new conversation content
|
|
88
94
|
3. MERGE the new information into the appropriate sections
|
|
89
95
|
4. Update sections with relevant new information
|
|
90
|
-
5. Remove information that is no longer relevant
|
|
96
|
+
5. Remove information that is no longer relevant EXCEPT for Section 10 (Constraints and Established Rules) — user constraints and established agreements must ALWAYS be preserved unless the user explicitly revoked them
|
|
91
97
|
6. Keep the summary concise but comprehensive
|
|
92
|
-
7. Maintain the
|
|
98
|
+
7. Maintain the 10-section format
|
|
99
|
+
8. Always carry forward ALL entries from Section 10 of the existing summary, adding any new constraints found in the new content${fileContext}
|
|
93
100
|
|
|
94
|
-
Output the updated summary following the same
|
|
101
|
+
Output the updated summary following the same 10-section structure.`;
|
|
95
102
|
}
|
|
96
103
|
/**
|
|
97
104
|
* Builds a summarization prompt based on the provided options.
|
|
@@ -107,5 +114,5 @@ export function buildSummarizationPrompt(options) {
|
|
|
107
114
|
}
|
|
108
115
|
return buildInitialPrompt(options);
|
|
109
116
|
}
|
|
110
|
-
export {
|
|
117
|
+
export { SUMMARY_SECTIONS };
|
|
111
118
|
//# sourceMappingURL=summarizationPrompt.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stage 3: Structured LLM Summarization
|
|
3
3
|
*
|
|
4
|
-
* Uses the structured
|
|
4
|
+
* Uses the structured 10-section prompt to summarize older messages
|
|
5
5
|
* while preserving recent ones.
|
|
6
6
|
*/
|
|
7
|
-
import type { ChatMessage } from "../../types/conversation.js";
|
|
8
7
|
import type { SummarizeConfig, SummarizeResult } from "../../types/contextTypes.js";
|
|
8
|
+
import type { ChatMessage } from "../../types/conversation.js";
|
|
9
9
|
export type { SummarizeConfig, SummarizeResult, } from "../../types/contextTypes.js";
|
|
10
10
|
export declare function summarizeMessages(messages: ChatMessage[], config?: SummarizeConfig): Promise<SummarizeResult>;
|
|
@@ -1,19 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Stage 3: Structured LLM Summarization
|
|
3
3
|
*
|
|
4
|
-
* Uses the structured
|
|
4
|
+
* Uses the structured 10-section prompt to summarize older messages
|
|
5
5
|
* while preserving recent ones.
|
|
6
6
|
*/
|
|
7
|
-
import { generateSummary } from "../../utils/conversationMemory.js";
|
|
8
7
|
import { randomUUID } from "crypto";
|
|
8
|
+
import { generateSummary } from "../../utils/conversationMemory.js";
|
|
9
|
+
import { estimateTokens } from "../../utils/tokenEstimation.js";
|
|
10
|
+
import { logger } from "../../utils/logger.js";
|
|
11
|
+
/**
|
|
12
|
+
* Find the split index using token counting — walk backward from the end,
|
|
13
|
+
* accumulating token counts until we've reserved `targetRecentTokens` worth
|
|
14
|
+
* of recent content. Everything before the split index gets summarized.
|
|
15
|
+
*/
|
|
16
|
+
function findSplitIndexByTokens(messages, targetRecentTokens, provider) {
|
|
17
|
+
let recentTokens = 0;
|
|
18
|
+
let splitIndex = messages.length;
|
|
19
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
20
|
+
const content = typeof messages[i].content === "string"
|
|
21
|
+
? messages[i].content
|
|
22
|
+
: JSON.stringify(messages[i].content);
|
|
23
|
+
const msgTokens = estimateTokens(content, provider);
|
|
24
|
+
if (recentTokens + msgTokens > targetRecentTokens) {
|
|
25
|
+
splitIndex = i + 1;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
recentTokens += msgTokens;
|
|
29
|
+
}
|
|
30
|
+
// Ensure at least one message is summarized
|
|
31
|
+
return Math.max(1, splitIndex);
|
|
32
|
+
}
|
|
9
33
|
export async function summarizeMessages(messages, config) {
|
|
10
34
|
const keepRecentRatio = config?.keepRecentRatio ?? 0.3;
|
|
11
35
|
if (messages.length <= 4) {
|
|
12
36
|
return { summarized: false, messages };
|
|
13
37
|
}
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
38
|
+
// Determine split point: prefer token-based when a target budget is available,
|
|
39
|
+
// fall back to message-count-based split for backward compatibility.
|
|
40
|
+
let splitIndex;
|
|
41
|
+
if (config?.targetTokens && config.targetTokens > 0) {
|
|
42
|
+
// Keep `keepRecentRatio` fraction of the target budget as recent context
|
|
43
|
+
const targetRecentTokens = Math.floor(config.targetTokens * keepRecentRatio);
|
|
44
|
+
// NOTE: config.provider is the summarization provider, not the generation
|
|
45
|
+
// provider. Ideally we'd use the generation/budget provider for accurate
|
|
46
|
+
// token estimation, but SummarizeConfig doesn't carry a separate
|
|
47
|
+
// budgetProvider field. This is a known design limitation.
|
|
48
|
+
splitIndex = findSplitIndexByTokens(messages, targetRecentTokens, config.provider);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
// Legacy: message-count-based split
|
|
52
|
+
const keepCount = Math.max(4, Math.ceil(messages.length * keepRecentRatio));
|
|
53
|
+
splitIndex = messages.length - keepCount;
|
|
54
|
+
}
|
|
55
|
+
// Clamp so at least the last message is always preserved (never summarize everything)
|
|
56
|
+
splitIndex = Math.min(splitIndex, messages.length - 1);
|
|
17
57
|
if (splitIndex <= 0) {
|
|
18
58
|
return { summarized: false, messages };
|
|
19
59
|
}
|
|
@@ -21,30 +61,40 @@ export async function summarizeMessages(messages, config) {
|
|
|
21
61
|
const recentMessages = messages.slice(splitIndex);
|
|
22
62
|
// Find previous summary if exists
|
|
23
63
|
const previousSummary = messagesToSummarize.find((m) => m.metadata?.isSummary)?.content;
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
64
|
+
// Build effective memory config: use provided memoryConfig, or construct from provider/model
|
|
65
|
+
const effectiveMemoryConfig = config?.memoryConfig ? { ...config.memoryConfig } : {};
|
|
66
|
+
// Fill in summarization provider/model from compactor config if not already set
|
|
67
|
+
if (!effectiveMemoryConfig.summarizationProvider && config?.provider) {
|
|
68
|
+
effectiveMemoryConfig.summarizationProvider = config.provider;
|
|
69
|
+
}
|
|
70
|
+
if (!effectiveMemoryConfig.summarizationModel && config?.model) {
|
|
71
|
+
effectiveMemoryConfig.summarizationModel = config.model;
|
|
72
|
+
}
|
|
73
|
+
// Only skip if there's genuinely no provider available
|
|
74
|
+
if (!effectiveMemoryConfig.summarizationProvider &&
|
|
75
|
+
!effectiveMemoryConfig.summarizationModel) {
|
|
76
|
+
logger.debug("[ContextCompactor] Stage 3 skipped: no summarization provider or model available");
|
|
77
|
+
return { summarized: false, messages };
|
|
78
|
+
}
|
|
79
|
+
const summaryText = await generateSummary(messagesToSummarize, effectiveMemoryConfig, "[ContextCompactor]", previousSummary);
|
|
80
|
+
if (!summaryText) {
|
|
81
|
+
return { summarized: false, messages };
|
|
82
|
+
}
|
|
83
|
+
const summaryMessage = {
|
|
84
|
+
id: `summary-${randomUUID()}`,
|
|
85
|
+
role: "user",
|
|
86
|
+
content: `[Previous conversation summary]:\n\n${summaryText}`,
|
|
87
|
+
timestamp: new Date().toISOString(),
|
|
88
|
+
metadata: {
|
|
89
|
+
isSummary: true,
|
|
90
|
+
summarizesFrom: messagesToSummarize[0]?.id,
|
|
91
|
+
summarizesTo: messagesToSummarize[messagesToSummarize.length - 1]?.id,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
return {
|
|
95
|
+
summarized: true,
|
|
96
|
+
messages: [summaryMessage, ...recentMessages],
|
|
97
|
+
summaryText,
|
|
98
|
+
};
|
|
49
99
|
}
|
|
50
100
|
//# sourceMappingURL=structuredSummarizer.js.map
|
|
@@ -103,6 +103,14 @@ export declare class MCPToolRegistry extends MCPRegistry {
|
|
|
103
103
|
* Get tools by category
|
|
104
104
|
*/
|
|
105
105
|
getToolsByCategory(category: string): ToolInfo[];
|
|
106
|
+
/**
|
|
107
|
+
* NL-001: Get available tools, filtering out those with OPEN circuit breakers.
|
|
108
|
+
* Returns both the filtered tools and the list of unavailable tool names.
|
|
109
|
+
*/
|
|
110
|
+
getAvailableTools(circuitBreakers: Map<string, import("../utils/errorHandling.js").CircuitBreaker>): {
|
|
111
|
+
tools: ToolInfo[];
|
|
112
|
+
unavailableTools: string[];
|
|
113
|
+
};
|
|
106
114
|
/**
|
|
107
115
|
* Check if tool exists
|
|
108
116
|
*/
|
|
@@ -565,6 +565,26 @@ export class MCPToolRegistry extends MCPRegistry {
|
|
|
565
565
|
}
|
|
566
566
|
return Array.from(uniqueTools.values());
|
|
567
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* NL-001: Get available tools, filtering out those with OPEN circuit breakers.
|
|
570
|
+
* Returns both the filtered tools and the list of unavailable tool names.
|
|
571
|
+
*/
|
|
572
|
+
getAvailableTools(circuitBreakers) {
|
|
573
|
+
const allTools = Array.from(this.tools.values());
|
|
574
|
+
const unavailableTools = [];
|
|
575
|
+
const tools = [];
|
|
576
|
+
for (const tool of allTools) {
|
|
577
|
+
const breakerKey = `${tool.serverId || "unknown"}.${tool.name}`;
|
|
578
|
+
const breaker = circuitBreakers.get(breakerKey);
|
|
579
|
+
if (breaker && breaker.getState() === "open") {
|
|
580
|
+
unavailableTools.push(tool.name);
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
tools.push(tool);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return { tools, unavailableTools };
|
|
587
|
+
}
|
|
568
588
|
/**
|
|
569
589
|
* Check if tool exists
|
|
570
590
|
*/
|
package/dist/lib/neurolink.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ export declare class NeuroLink {
|
|
|
32
32
|
private externalServerManager;
|
|
33
33
|
private toolCache;
|
|
34
34
|
private readonly toolCacheDuration;
|
|
35
|
+
private modelAliasConfig?;
|
|
36
|
+
private lastCompactionMessageCount;
|
|
37
|
+
/** Extract sessionId from options context for compaction watermark keying */
|
|
38
|
+
private getCompactionSessionId;
|
|
35
39
|
private toolCircuitBreakers;
|
|
36
40
|
private toolExecutionMetrics;
|
|
37
41
|
private currentStreamToolExecutions;
|
|
@@ -1140,6 +1144,12 @@ export declare class NeuroLink {
|
|
|
1140
1144
|
averageExecutionTime: number;
|
|
1141
1145
|
lastExecutionTime: number;
|
|
1142
1146
|
}>;
|
|
1147
|
+
/**
|
|
1148
|
+
* NL-004: Set model alias/deprecation configuration.
|
|
1149
|
+
* Models in the alias map will be warned, redirected, or blocked based on their action.
|
|
1150
|
+
* @param config - Model alias configuration with aliases map
|
|
1151
|
+
*/
|
|
1152
|
+
setModelAliasConfig(config: import("./types/generateTypes.js").ModelAliasConfig): void;
|
|
1143
1153
|
/**
|
|
1144
1154
|
* Get circuit breaker status for all tools
|
|
1145
1155
|
* @returns Object with circuit breaker status for each tool
|