@juspay/neurolink 9.75.0 → 9.76.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.
@@ -5,7 +5,7 @@
5
5
  * Enhanced AI provider system with natural MCP tool access.
6
6
  * Uses real MCP infrastructure for tool discovery and execution.
7
7
  */
8
- import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor } from "./types/index.js";
8
+ import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig } from "./types/index.js";
9
9
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
10
10
  import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
11
11
  import { ExternalServerManager } from "./mcp/externalServerManager.js";
@@ -102,6 +102,7 @@ export declare class NeuroLink {
102
102
  private conversationMemoryConfig?;
103
103
  private toolRoutingConfig?;
104
104
  private toolRoutingCacheInstance?;
105
+ private toolDedupConfig?;
105
106
  private enableOrchestration;
106
107
  private authProvider?;
107
108
  private pendingAuthConfig?;
@@ -1089,6 +1090,19 @@ export declare class NeuroLink {
1089
1090
  * @see {@link NeuroLink.executeTool} for events related to tool execution
1090
1091
  */
1091
1092
  getEventEmitter(): TypedEventEmitter<NeuroLinkEvents>;
1093
+ /**
1094
+ * Returns the instance-level tool-dedup configuration, or `undefined` when
1095
+ * toolDedup was not provided at construction time.
1096
+ *
1097
+ * The stored object is returned as-is whenever `toolDedup` was supplied,
1098
+ * including when `enabled: false` — only the complete absence of a `toolDedup`
1099
+ * option results in `undefined`.
1100
+ *
1101
+ * Called by `BaseProvider.applyToolFiltering` so the dedup pass uses the
1102
+ * same config for every generate/stream call without threading an extra
1103
+ * parameter through the full call stack.
1104
+ */
1105
+ getToolDedupConfig(): ToolDedupConfig | undefined;
1092
1106
  /**
1093
1107
  * Curator P1-1: synchronous credential health check for a single provider.
1094
1108
  *
@@ -445,6 +445,8 @@ export class NeuroLink {
445
445
  // Lazy-initialized routing decision cache (ITEM C). Created on first use so
446
446
  // instances that don't use routing pay no overhead.
447
447
  toolRoutingCacheInstance;
448
+ // Opt-in tool-signature deduplication config.
449
+ toolDedupConfig;
448
450
  // Add orchestration property
449
451
  enableOrchestration;
450
452
  // Authentication provider for secure access control
@@ -857,6 +859,9 @@ export class NeuroLink {
857
859
  // multiple NeuroLink instances.
858
860
  this.toolRoutingConfig = { ...config.toolRouting };
859
861
  }
862
+ if (config?.toolDedup) {
863
+ this.toolDedupConfig = { ...config.toolDedup };
864
+ }
860
865
  logger.setEventEmitter(this.emitter);
861
866
  // Read tool cache duration from environment variables, with a default
862
867
  const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
@@ -7513,6 +7518,21 @@ Current user's request: ${currentInput}`;
7513
7518
  getEventEmitter() {
7514
7519
  return this.emitter;
7515
7520
  }
7521
+ /**
7522
+ * Returns the instance-level tool-dedup configuration, or `undefined` when
7523
+ * toolDedup was not provided at construction time.
7524
+ *
7525
+ * The stored object is returned as-is whenever `toolDedup` was supplied,
7526
+ * including when `enabled: false` — only the complete absence of a `toolDedup`
7527
+ * option results in `undefined`.
7528
+ *
7529
+ * Called by `BaseProvider.applyToolFiltering` so the dedup pass uses the
7530
+ * same config for every generate/stream call without threading an extra
7531
+ * parameter through the full call stack.
7532
+ */
7533
+ getToolDedupConfig() {
7534
+ return this.toolDedupConfig;
7535
+ }
7516
7536
  /**
7517
7537
  * Curator P1-1: synchronous credential health check for a single provider.
7518
7538
  *
@@ -9,7 +9,6 @@ import type { ConversationMemoryConfig } from "./conversation.js";
9
9
  import type { ObservabilityConfig } from "./observability.js";
10
10
  import type { AuthProvider, AuthProviderType, AuthProviderConfig, Auth0Config, ClerkConfig, FirebaseConfig, SupabaseConfig, WorkOSConfig, BetterAuthConfig, JWTConfig, OAuth2Config, CognitoConfig, KeycloakConfig, AuthenticatedContext } from "./auth.js";
11
11
  import type { NeurolinkCredentials } from "./providers.js";
12
- import type { ToolRoutingConfig } from "./toolRouting.js";
13
12
  /**
14
13
  * Main NeuroLink configuration type
15
14
  */
@@ -74,6 +73,19 @@ export type NeurolinkConstructorConfig = {
74
73
  * any router failure. See {@link ToolRoutingConfig}.
75
74
  */
76
75
  toolRouting?: ToolRoutingConfig;
76
+ /**
77
+ * Opt-in tool-signature deduplication. When enabled, tools whose
78
+ * canonical signatures are sufficiently similar (Jaccard ≥ threshold) are
79
+ * collapsed to a single representative before being sent to the model,
80
+ * reducing token cost and model confusion caused by near-identical tools.
81
+ *
82
+ * Disabled by default — enabling this changes nothing unless you
83
+ * explicitly set `enabled: true`. Always fails open: any error in the
84
+ * dedup pass returns the original tool set unchanged.
85
+ *
86
+ * See {@link ToolDedupConfig}.
87
+ */
88
+ toolDedup?: ToolDedupConfig;
77
89
  };
78
90
  /**
79
91
  * Configuration for MCP enhancement modules wired into generate()/stream() paths.
@@ -338,7 +350,7 @@ export type ConfigUpdateOptions = {
338
350
  */
339
351
  export declare const DEFAULT_CONFIG: NeuroLinkConfig;
340
352
  import { TOOL_TIMEOUTS, BACKOFF_CONFIG, PERFORMANCE_PROFILES, PROVIDER_OPERATION_CONFIGS, MCP_OPERATION_CONFIGS } from "../constants/index.js";
341
- import type { ToolMiddleware, CacheStrategy, RoutingStrategy } from "./index.js";
353
+ import type { ToolMiddleware, CacheStrategy, RoutingStrategy, ToolDedupConfig, ToolRoutingConfig } from "./index.js";
342
354
  import type { ModelAliasConfig } from "./generate.js";
343
355
  /** Timeout category keys from TOOL_TIMEOUTS. */
344
356
  export type TimeoutCategory = keyof typeof TOOL_TIMEOUTS;
@@ -50,6 +50,7 @@ export * from "./stream.js";
50
50
  export * from "./subscription.js";
51
51
  export * from "./task.js";
52
52
  export * from "./taskClassification.js";
53
+ export * from "./toolDedup.js";
53
54
  export * from "./toolRouting.js";
54
55
  export * from "./tools.js";
55
56
  export * from "./voice.js";
@@ -51,6 +51,7 @@ export * from "./stream.js";
51
51
  export * from "./subscription.js";
52
52
  export * from "./task.js";
53
53
  export * from "./taskClassification.js";
54
+ export * from "./toolDedup.js";
54
55
  export * from "./toolRouting.js";
55
56
  export * from "./tools.js";
56
57
  export * from "./voice.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Tool signature deduplication types.
3
+ *
4
+ * When many MCP servers are registered, it is common for tools from different
5
+ * servers to be functionally identical — same purpose, same parameters, only a
6
+ * slightly different name or description phrasing. Sending all of them to the
7
+ * model wastes tokens and can confuse the model into picking the wrong variant.
8
+ *
9
+ * The dedup pass is OPT-IN and FAIL-OPEN. When disabled (the default) the
10
+ * tool set is returned byte-for-byte unchanged. Any error in the dedup logic
11
+ * also returns the original tool set.
12
+ */
13
+ /** Configuration for the opt-in tool-signature deduplication pass. */
14
+ export type ToolDedupConfig = {
15
+ /**
16
+ * Master switch. Dedup runs only when `true`.
17
+ * Default: `false` (disabled — no change in behaviour).
18
+ */
19
+ enabled?: boolean;
20
+ /**
21
+ * Jaccard similarity threshold in [0, 1]. Pairs of tools whose token-set
22
+ * Jaccard similarity over their canonical signatures meets or exceeds this
23
+ * value are treated as near-duplicates; only one representative per cluster
24
+ * (the first in stable input order) is forwarded to the model.
25
+ *
26
+ * Default: `0.9`
27
+ */
28
+ threshold?: number;
29
+ };
30
+ /** Record produced for each tool collapsed by the dedup pass. */
31
+ export type ToolDedupRemoved = {
32
+ /** Name of the tool that was collapsed. */
33
+ name: string;
34
+ /** Name of the representative tool that this one was collapsed into. */
35
+ duplicateOf: string;
36
+ /** Similarity score that triggered the collapse (in [0, 1]). */
37
+ similarity: number;
38
+ };
39
+ /** Return type of `dedupeTools()`. */
40
+ export type ToolDedupResult<T extends Record<string, unknown>> = {
41
+ /** Deduplicated tool set (or original set when dedup is disabled/errored). */
42
+ tools: T;
43
+ /** Tools that were removed along with the reason. Empty when dedup is off. */
44
+ removed: ToolDedupRemoved[];
45
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Tool signature deduplication types.
3
+ *
4
+ * When many MCP servers are registered, it is common for tools from different
5
+ * servers to be functionally identical — same purpose, same parameters, only a
6
+ * slightly different name or description phrasing. Sending all of them to the
7
+ * model wastes tokens and can confuse the model into picking the wrong variant.
8
+ *
9
+ * The dedup pass is OPT-IN and FAIL-OPEN. When disabled (the default) the
10
+ * tool set is returned byte-for-byte unchanged. Any error in the dedup logic
11
+ * also returns the original tool set.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=toolDedup.js.map
@@ -5,7 +5,7 @@
5
5
  * Enhanced AI provider system with natural MCP tool access.
6
6
  * Uses real MCP infrastructure for tool discovery and execution.
7
7
  */
8
- import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor } from "./types/index.js";
8
+ import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig } from "./types/index.js";
9
9
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
10
10
  import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
11
11
  import { ExternalServerManager } from "./mcp/externalServerManager.js";
@@ -102,6 +102,7 @@ export declare class NeuroLink {
102
102
  private conversationMemoryConfig?;
103
103
  private toolRoutingConfig?;
104
104
  private toolRoutingCacheInstance?;
105
+ private toolDedupConfig?;
105
106
  private enableOrchestration;
106
107
  private authProvider?;
107
108
  private pendingAuthConfig?;
@@ -1089,6 +1090,19 @@ export declare class NeuroLink {
1089
1090
  * @see {@link NeuroLink.executeTool} for events related to tool execution
1090
1091
  */
1091
1092
  getEventEmitter(): TypedEventEmitter<NeuroLinkEvents>;
1093
+ /**
1094
+ * Returns the instance-level tool-dedup configuration, or `undefined` when
1095
+ * toolDedup was not provided at construction time.
1096
+ *
1097
+ * The stored object is returned as-is whenever `toolDedup` was supplied,
1098
+ * including when `enabled: false` — only the complete absence of a `toolDedup`
1099
+ * option results in `undefined`.
1100
+ *
1101
+ * Called by `BaseProvider.applyToolFiltering` so the dedup pass uses the
1102
+ * same config for every generate/stream call without threading an extra
1103
+ * parameter through the full call stack.
1104
+ */
1105
+ getToolDedupConfig(): ToolDedupConfig | undefined;
1092
1106
  /**
1093
1107
  * Curator P1-1: synchronous credential health check for a single provider.
1094
1108
  *
package/dist/neurolink.js CHANGED
@@ -445,6 +445,8 @@ export class NeuroLink {
445
445
  // Lazy-initialized routing decision cache (ITEM C). Created on first use so
446
446
  // instances that don't use routing pay no overhead.
447
447
  toolRoutingCacheInstance;
448
+ // Opt-in tool-signature deduplication config.
449
+ toolDedupConfig;
448
450
  // Add orchestration property
449
451
  enableOrchestration;
450
452
  // Authentication provider for secure access control
@@ -857,6 +859,9 @@ export class NeuroLink {
857
859
  // multiple NeuroLink instances.
858
860
  this.toolRoutingConfig = { ...config.toolRouting };
859
861
  }
862
+ if (config?.toolDedup) {
863
+ this.toolDedupConfig = { ...config.toolDedup };
864
+ }
860
865
  logger.setEventEmitter(this.emitter);
861
866
  // Read tool cache duration from environment variables, with a default
862
867
  const cacheDurationEnv = process.env.NEUROLINK_TOOL_CACHE_DURATION;
@@ -7513,6 +7518,21 @@ Current user's request: ${currentInput}`;
7513
7518
  getEventEmitter() {
7514
7519
  return this.emitter;
7515
7520
  }
7521
+ /**
7522
+ * Returns the instance-level tool-dedup configuration, or `undefined` when
7523
+ * toolDedup was not provided at construction time.
7524
+ *
7525
+ * The stored object is returned as-is whenever `toolDedup` was supplied,
7526
+ * including when `enabled: false` — only the complete absence of a `toolDedup`
7527
+ * option results in `undefined`.
7528
+ *
7529
+ * Called by `BaseProvider.applyToolFiltering` so the dedup pass uses the
7530
+ * same config for every generate/stream call without threading an extra
7531
+ * parameter through the full call stack.
7532
+ */
7533
+ getToolDedupConfig() {
7534
+ return this.toolDedupConfig;
7535
+ }
7516
7536
  /**
7517
7537
  * Curator P1-1: synchronous credential health check for a single provider.
7518
7538
  *
@@ -9,7 +9,6 @@ import type { ConversationMemoryConfig } from "./conversation.js";
9
9
  import type { ObservabilityConfig } from "./observability.js";
10
10
  import type { AuthProvider, AuthProviderType, AuthProviderConfig, Auth0Config, ClerkConfig, FirebaseConfig, SupabaseConfig, WorkOSConfig, BetterAuthConfig, JWTConfig, OAuth2Config, CognitoConfig, KeycloakConfig, AuthenticatedContext } from "./auth.js";
11
11
  import type { NeurolinkCredentials } from "./providers.js";
12
- import type { ToolRoutingConfig } from "./toolRouting.js";
13
12
  /**
14
13
  * Main NeuroLink configuration type
15
14
  */
@@ -74,6 +73,19 @@ export type NeurolinkConstructorConfig = {
74
73
  * any router failure. See {@link ToolRoutingConfig}.
75
74
  */
76
75
  toolRouting?: ToolRoutingConfig;
76
+ /**
77
+ * Opt-in tool-signature deduplication. When enabled, tools whose
78
+ * canonical signatures are sufficiently similar (Jaccard ≥ threshold) are
79
+ * collapsed to a single representative before being sent to the model,
80
+ * reducing token cost and model confusion caused by near-identical tools.
81
+ *
82
+ * Disabled by default — enabling this changes nothing unless you
83
+ * explicitly set `enabled: true`. Always fails open: any error in the
84
+ * dedup pass returns the original tool set unchanged.
85
+ *
86
+ * See {@link ToolDedupConfig}.
87
+ */
88
+ toolDedup?: ToolDedupConfig;
77
89
  };
78
90
  /**
79
91
  * Configuration for MCP enhancement modules wired into generate()/stream() paths.
@@ -338,7 +350,7 @@ export type ConfigUpdateOptions = {
338
350
  */
339
351
  export declare const DEFAULT_CONFIG: NeuroLinkConfig;
340
352
  import { TOOL_TIMEOUTS, BACKOFF_CONFIG, PERFORMANCE_PROFILES, PROVIDER_OPERATION_CONFIGS, MCP_OPERATION_CONFIGS } from "../constants/index.js";
341
- import type { ToolMiddleware, CacheStrategy, RoutingStrategy } from "./index.js";
353
+ import type { ToolMiddleware, CacheStrategy, RoutingStrategy, ToolDedupConfig, ToolRoutingConfig } from "./index.js";
342
354
  import type { ModelAliasConfig } from "./generate.js";
343
355
  /** Timeout category keys from TOOL_TIMEOUTS. */
344
356
  export type TimeoutCategory = keyof typeof TOOL_TIMEOUTS;
@@ -50,6 +50,7 @@ export * from "./stream.js";
50
50
  export * from "./subscription.js";
51
51
  export * from "./task.js";
52
52
  export * from "./taskClassification.js";
53
+ export * from "./toolDedup.js";
53
54
  export * from "./toolRouting.js";
54
55
  export * from "./tools.js";
55
56
  export * from "./voice.js";
@@ -51,6 +51,7 @@ export * from "./stream.js";
51
51
  export * from "./subscription.js";
52
52
  export * from "./task.js";
53
53
  export * from "./taskClassification.js";
54
+ export * from "./toolDedup.js";
54
55
  export * from "./toolRouting.js";
55
56
  export * from "./tools.js";
56
57
  export * from "./voice.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Tool signature deduplication types.
3
+ *
4
+ * When many MCP servers are registered, it is common for tools from different
5
+ * servers to be functionally identical — same purpose, same parameters, only a
6
+ * slightly different name or description phrasing. Sending all of them to the
7
+ * model wastes tokens and can confuse the model into picking the wrong variant.
8
+ *
9
+ * The dedup pass is OPT-IN and FAIL-OPEN. When disabled (the default) the
10
+ * tool set is returned byte-for-byte unchanged. Any error in the dedup logic
11
+ * also returns the original tool set.
12
+ */
13
+ /** Configuration for the opt-in tool-signature deduplication pass. */
14
+ export type ToolDedupConfig = {
15
+ /**
16
+ * Master switch. Dedup runs only when `true`.
17
+ * Default: `false` (disabled — no change in behaviour).
18
+ */
19
+ enabled?: boolean;
20
+ /**
21
+ * Jaccard similarity threshold in [0, 1]. Pairs of tools whose token-set
22
+ * Jaccard similarity over their canonical signatures meets or exceeds this
23
+ * value are treated as near-duplicates; only one representative per cluster
24
+ * (the first in stable input order) is forwarded to the model.
25
+ *
26
+ * Default: `0.9`
27
+ */
28
+ threshold?: number;
29
+ };
30
+ /** Record produced for each tool collapsed by the dedup pass. */
31
+ export type ToolDedupRemoved = {
32
+ /** Name of the tool that was collapsed. */
33
+ name: string;
34
+ /** Name of the representative tool that this one was collapsed into. */
35
+ duplicateOf: string;
36
+ /** Similarity score that triggered the collapse (in [0, 1]). */
37
+ similarity: number;
38
+ };
39
+ /** Return type of `dedupeTools()`. */
40
+ export type ToolDedupResult<T extends Record<string, unknown>> = {
41
+ /** Deduplicated tool set (or original set when dedup is disabled/errored). */
42
+ tools: T;
43
+ /** Tools that were removed along with the reason. Empty when dedup is off. */
44
+ removed: ToolDedupRemoved[];
45
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tool signature deduplication types.
3
+ *
4
+ * When many MCP servers are registered, it is common for tools from different
5
+ * servers to be functionally identical — same purpose, same parameters, only a
6
+ * slightly different name or description phrasing. Sending all of them to the
7
+ * model wastes tokens and can confuse the model into picking the wrong variant.
8
+ *
9
+ * The dedup pass is OPT-IN and FAIL-OPEN. When disabled (the default) the
10
+ * tool set is returned byte-for-byte unchanged. Any error in the dedup logic
11
+ * also returns the original tool set.
12
+ */
13
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.75.0",
3
+ "version": "9.76.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {
@@ -120,11 +120,13 @@
120
120
  "test:envguard": "npx tsx test/helpers/envGuard.test.ts",
121
121
  "test:tool-routing-cli:vitest": "pnpm exec vitest run test/toolRoutingCli.test.ts",
122
122
  "test:tool-routing-cli": "pnpm run test:tool-routing-cli:vitest && npx tsx test/continuous-test-suite-tool-routing-cli.ts",
123
+ "test:tool-dedup:vitest": "pnpm exec vitest run test/toolDedup.test.ts",
124
+ "test:tool-dedup": "pnpm run test:tool-dedup:vitest && npx tsx test/continuous-test-suite-tool-dedup.ts",
123
125
  "test:ci": "pnpm run test && pnpm run test:client && pnpm run test:hitl",
124
126
  "// CI tier — fast, no live AI calls, safe for every commit": "",
125
127
  "test:unit:vitest": "pnpm exec vitest run test/toolRouting.test.ts",
126
128
  "test:tool-routing": "pnpm run test:unit:vitest && npx tsx test/continuous-test-suite-tool-routing.ts",
127
- "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest",
129
+ "test:unit": "pnpm run test:envguard && pnpm run test:bugfixes && pnpm run test:mcp:infra && pnpm run test:mcp:bash && pnpm run test:mcp:limits && pnpm run test:mcp:spans && pnpm run test:autoresearch:redis && pnpm run test:unit:vitest && pnpm run test:tool-routing-cli:vitest && pnpm run test:tool-dedup:vitest",
128
130
  "// CI tier — live providers, runs only when API keys are present (test:credentials and test:dynamic make real provider calls when keys are set, so they live here, not in test:unit)": "",
129
131
  "test:live": "pnpm run test:providers && pnpm run test:mcp:http && pnpm run test:mcp:sdk && pnpm run test:mcp:cli && pnpm run test:observability && pnpm run test:context && pnpm run test:memory && pnpm run test:tool-reliability && pnpm run test:evaluation && pnpm run test:autoresearch && pnpm run test:credentials && pnpm run test:dynamic",
130
132
  "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run": "",