@juspay/neurolink
Version:
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 applicatio
1,211 lines • 86.4 kB
TypeScript
/**
* NeuroLink - Unified AI Interface with Real MCP Tool Integration
*
* REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES
* Enhanced AI provider system with natural MCP tool access.
* Uses real MCP infrastructure for tool discovery and execution.
*/
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 } from "./types/index.js";
import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
import { ExternalServerManager } from "./mcp/externalServerManager.js";
import { MCPToolRegistry } from "./mcp/toolRegistry.js";
import type { DynamicOptions } from "./types/index.js";
import { TaskManager } from "./tasks/taskManager.js";
/**
* Curator P2-4 dedup (concurrency-safe): native providers emit
* `generation:end` on the shared SDK emitter. We attach a fresh
* mutable `dedupContext` object directly to the per-call
* `StreamOptions` (under `_streamDedupContext`) so each stream gets
* its own instance — concurrent streams have different option objects
* and therefore different contexts, so they cannot interfere.
*
* Native provider emit sites read `options._streamDedupContext` and
* flip `.providerEmitted = true` before emitting; the orchestration's
* finally block reads the same closed-over reference and skips its
* own emit when the flag is set.
*
* This avoids the AsyncLocalStorage approach which doesn't reliably
* propagate through async-generator yield boundaries when iteration
* happens from outside the original `run()` scope (e.g. when the
* consumer drives `for await of result.stream` after `sdk.stream(...)`
* returns).
*/
export declare const STREAM_DEDUP_CONTEXT_KEY: "_streamDedupContext";
/**
* Native providers call this from their `generation:end` emit sites,
* passing the same `options` object they received. Safe no-op when
* the field isn't set.
*/
export declare function markStreamProviderEmittedGenerationEnd(options: {
_streamDedupContext?: StreamGenerationEndContext;
} | undefined): void;
/**
* Symbol-based brand for cross-module identification without circular imports.
*
* Provider constructors receive `sdk?: unknown` (the factory layer's
* contract). Rather than duck-typing via `"getInMemoryServers" in sdk`,
* use `isNeuroLink(value)` from this module to do a brand check —
* survives minification AND doesn't rely on method-name stability.
*/
export declare const NEUROLINK_BRAND: unique symbol;
/**
* Type-guard for opaque values that should be a {@link NeuroLink} instance.
*
* Designed for the provider-factory boundary where TS can't carry the type
* through `UnknownRecord` without forcing every caller into a circular
* dependency. Cheap to call and unaffected by minification.
*/
export declare function isNeuroLink(value: unknown): value is NeuroLink;
export declare class NeuroLink {
/** @internal Brand for cross-module identification — see {@link isNeuroLink}. */
readonly [NEUROLINK_BRAND]: true;
private mcpInitialized;
private mcpSkipped;
private mcpInitPromise;
private emitter;
private _taskManager?;
private _taskManagerConfig?;
private toolRegistry;
private autoDiscoveredServerInfos;
private externalServerManager;
private toolCache;
private readonly toolCacheDuration;
private modelAliasConfig?;
private lastCompactionMessageCount;
/** Extract sessionId from options context for compaction watermark keying */
private getCompactionSessionId;
private mcpToolResultCache?;
private mcpToolRouter?;
private mcpToolBatcher?;
private mcpEnhancedDiscovery?;
private mcpToolMiddlewares;
/** Artifact store for externalized MCP tool outputs (set when strategy=externalize). */
private mcpArtifactStore?;
private _disableToolCacheForCurrentRequest;
private mcpEnhancementsConfig?;
private toolCircuitBreakers;
private toolExecutionMetrics;
private currentStreamToolExecutions;
private toolExecutionHistory;
private activeToolExecutions;
/**
* Helper method to emit tool end event in a consistent way
* Used by executeTool in both success and error paths
* @param toolName - Name of the tool
* @param startTime - Timestamp when tool execution started
* @param success - Whether the tool execution was successful
* @param result - The result of the tool execution (optional)
* @param error - The error if execution failed (optional)
*/
private emitToolEndEvent;
conversationMemory?: ConversationMemoryManager | RedisConversationMemoryManager | null;
private conversationMemoryNeedsInit;
private conversationMemoryConfig?;
private enableOrchestration;
private authProvider?;
private pendingAuthConfig?;
private authInitPromise?;
private credentials?;
private readonly fallbackConfig;
/**
* Merge instance-level credentials with per-call credentials.
*
* Semantics: **deep merge at the provider level.** For each provider key
* present in both `this.credentials` and `callCredentials`, the per-call
* fields are merged ON TOP of the instance-level fields, so fields not
* mentioned in the per-call slice are preserved.
*
* Example:
* ```
* instance: { openai: { apiKey: "key1", baseURL: "url1" } }
* per-call: { openai: { apiKey: "key2" } }
* merged: { openai: { apiKey: "key2", baseURL: "url1" } } // baseURL preserved
* ```
*
* Providers present only in one source are carried through unchanged.
* Unrelated providers (not overridden in callCredentials) are carried through
* from instance credentials unchanged.
*/
private resolveCredentials;
private hitlManager?;
private _sessionCostUsd;
private fileRegistry;
private cachedFileTools;
private memoryInstance?;
private memorySDKConfig?;
/**
* Extract and set Langfuse context from options with proper async scoping
*/
private setLangfuseContextFromOptions;
private createMetricsTraceContext;
private enforceSessionBudget;
private assertInputText;
private applyAuthenticatedRequestContext;
private applyGenerateLifecycleMiddleware;
private applyStreamLifecycleMiddleware;
private initializeMemoryConfig;
/**
* Lazy initialization for memory — called during generate/stream.
*/
private ensureMemoryReady;
/**
* Context storage for tool execution
* This context will be merged with any runtime context passed by the AI model
*/
private toolExecutionContext?;
/**
* Creates a new NeuroLink instance for AI text generation with MCP tool integration.
*
* @param config - Optional configuration object
* @param config.conversationMemory - Configuration for conversation memory features
* @param config.conversationMemory.enabled - Whether to enable conversation memory (default: false)
* @param config.conversationMemory.maxSessions - Maximum number of concurrent sessions (default: 100)
* @param config.conversationMemory.maxTurnsPerSession - Maximum conversation turns per session (default: 50)
* @param config.enableOrchestration - Whether to enable smart model orchestration (default: false)
* @param config.hitl - Configuration for Human-in-the-Loop safety features
* @param config.hitl.enabled - Whether to enable HITL tool confirmation (default: false)
* @param config.hitl.dangerousActions - Keywords that trigger confirmation (default: ['delete', 'remove', 'drop'])
* @param config.hitl.timeout - Confirmation timeout in milliseconds (default: 30000)
* @param config.hitl.allowArgumentModification - Allow users to modify tool parameters (default: true)
* @param config.toolRegistry - Optional tool registry instance for advanced use cases (default: new MCPToolRegistry())
*
* @example
* ```typescript
* // Basic usage
* const neurolink = new NeuroLink();
*
* // With conversation memory
* const neurolink = new NeuroLink({
* conversationMemory: {
* enabled: true,
* maxSessions: 50,
* maxTurnsPerSession: 20
* }
* });
*
* // With orchestration enabled
* const neurolink = new NeuroLink({
* enableOrchestration: true
* });
*
* // With HITL safety features
* const neurolink = new NeuroLink({
* hitl: {
* enabled: true,
* dangerousActions: ['delete', 'remove', 'drop', 'truncate'],
* timeout: 30000,
* allowArgumentModification: true
* }
* });
* ```
*
* @throws {Error} When provider registry setup fails
* @throws {Error} When conversation memory initialization fails (if enabled)
* @throws {Error} When external server manager initialization fails
* @throws {Error} When HITL configuration is invalid (if enabled)
*/
private observabilityConfig?;
private metricsAggregator;
/**
* Per-request metrics trace context backed by AsyncLocalStorage.
* Safe for concurrent requests on the same SDK instance.
* Context is set via metricsTraceContextStorage.run() in generate/stream.
*/
private get _metricsTraceContext();
constructor(config?: NeurolinkConstructorConfig);
/**
* TaskManager — scheduled and self-running tasks.
* Lazy-initialized on first access. Configurable via constructor `tasks` option.
* The actual async initialization (Redis connect, backend start) happens
* lazily inside TaskManager on first operation.
*/
get tasks(): TaskManager;
/**
* Initialize provider registry with security settings
*/
private initializeProviderRegistry;
/**
* Initialize conversation memory if enabled
*/
private initializeConversationMemory;
/**
* Initialize HITL (Human-in-the-Loop) if enabled
*/
private initializeHITL;
/**
* Initialize MCP enhancement modules (cache, router, batcher, discovery).
* Wires standalone MCP modules into the core SDK execution path.
*/
private initializeMCPEnhancements;
/**
* Register file reference tools with the MCP tool registry.
*
* Creates file access tools (list_attached_files, read_file_section,
* search_in_file, get_file_preview) bound to the FileReferenceRegistry
* and registers them as direct tools so they're available to LLMs.
*/
private registerFileTools;
/**
* Register task management tools bound to a TaskManager instance.
* Follows the same factory + registry pattern as registerFileTools().
* Called when TaskManager is created (eagerly or lazily via the `tasks` getter).
*/
private registerTaskTools;
/**
* Register memory retrieval tools that allow the AI to access
* conversation history, including full tool outputs.
* Only registered when Redis conversation memory is active.
*/
private registerMemoryRetrievalTools;
/** Format memory context for prompt inclusion */
private formatMemoryContext;
/**
* Format memory context from multiple users into a labeled block.
*/
private formatMultiUserMemoryContext;
/**
* Determine whether memory should be read (retrieved) for this call.
* Respects both the global memory SDK config and per-call overrides.
*/
private shouldReadMemory;
/**
* Determine whether memory should be written (stored) for this call.
* Respects both the global memory SDK config and per-call overrides.
*/
private shouldWriteMemory;
/**
* Retrieve condensed memory for a user (and optionally additional users).
* Returns the input text enhanced with memory context, or unchanged if no memory.
*/
private retrieveMemory;
/**
* Store a conversation turn in memory (non-blocking).
* Calls add(userId, content) which internally condenses old + new via LLM.
* Supports additional users with per-user prompt and maxWords overrides.
*/
private storeMemoryInBackground;
/**
* Set up HITL event forwarding to main emitter
*/
private setupHITLEventForwarding;
/**
* Initialize external server manager with event handlers
*/
private initializeExternalServerManager;
/**
* Setup event handlers for external server manager
*/
private setupExternalServerEventHandlers;
/**
* Initialize Langfuse observability for AI operations tracking
*/
private initializeLangfuse;
/**
* Log constructor completion with final state summary
*/
private logConstructorComplete;
/**
* Initialize MCP registry with enhanced error handling and resource cleanup
* Uses isolated async context to prevent hanging
*/
private initializeMCP;
/**
* Actual one-shot MCP initialization logic. Called at most once per
* NeuroLink instance lifetime (unless cleanup() resets the flag).
*/
private performMCPInitializationOnce;
/**
* Import performance manager with error handling
*/
private importPerformanceManager;
/**
* Perform main MCP initialization logic
*/
private performMCPInitialization;
/**
* Initialize tool registry with timeout protection
*/
private initializeToolRegistryInternal;
/**
* Initialize provider registry
*/
private initializeProviderRegistryInternal;
/**
* Register direct tools server
*/
private registerDirectToolsServerInternal;
/**
* Load MCP configuration from .mcp-config.json with parallel loading for improved performance
*/
private loadMCPConfigurationInternal;
/**
* Log MCP initialization completion
*/
private logMCPInitComplete;
/**
* Apply orchestration to determine optimal provider and model
* @param options - Original GenerateOptions
* @returns Modified options with orchestrated provider marked in context, or empty object if validation fails
*/
private applyOrchestration;
/**
* Apply orchestration to determine optimal provider and model for streaming
* @param options - Original StreamOptions
* @returns Modified options with orchestrated provider marked in context, or empty object if validation fails
*/
private applyStreamOrchestration;
/**
* MAIN ENTRY POINT: Enhanced generate method with new function signature
* Replaces both generateText and legacy methods
*/
/**
* Extracts the original prompt text from the provided input.
* If a string is provided, it returns the string directly.
* If a GenerateOptions object is provided, it returns the input text from the object.
* @param optionsOrPrompt The prompt input, either as a string or a GenerateOptions object.
* @returns The original prompt text as a string.
*/
private _extractOriginalPrompt;
/**
* Generate AI content using the best available provider with MCP tool integration.
* This is the primary method for text generation with full feature support.
*
* @param optionsOrPrompt - Either a string prompt or a comprehensive GenerateOptions object
* @param optionsOrPrompt.input - Input configuration object
* @param optionsOrPrompt.input.text - The text prompt to send to the AI (required)
* @param optionsOrPrompt.provider - AI provider to use ('auto', 'openai', 'anthropic', etc.)
* @param optionsOrPrompt.model - Specific model to use (e.g., 'gpt-4', 'claude-3-opus')
* @param optionsOrPrompt.temperature - Randomness in response (0.0 = deterministic, 2.0 = very random)
* @param optionsOrPrompt.maxTokens - Maximum tokens in response
* @param optionsOrPrompt.systemPrompt - System message to set AI behavior
* @param optionsOrPrompt.disableTools - Whether to disable MCP tool usage
* @param optionsOrPrompt.enableAnalytics - Whether to include usage analytics
* @param optionsOrPrompt.enableEvaluation - Whether to include response quality evaluation
* @param optionsOrPrompt.context - Additional context for the request
* @param optionsOrPrompt.evaluationDomain - Domain for specialized evaluation
* @param optionsOrPrompt.toolUsageContext - Context for tool usage decisions
*
* @returns Promise resolving to GenerateResult with content, usage data, and optional analytics
*
* @example
* ```typescript
* // Simple usage with string prompt
* const result = await neurolink.generate("What is artificial intelligence?");
* console.log(result.content);
*
* // Advanced usage with options
* const result = await neurolink.generate({
* input: { text: "Explain quantum computing" },
* provider: "openai",
* model: "gpt-4",
* temperature: 0.7,
* maxTokens: 500,
* enableAnalytics: true,
* enableEvaluation: true,
* context: { domain: "science", level: "intermediate" }
* });
*
* // Access analytics and evaluation data
* console.log(result.analytics?.usage);
* console.log(result.evaluation?.relevance);
* ```
*
* @throws {Error} When input text is missing or invalid
* @throws {Error} When all providers fail to generate content
* @throws {Error} When conversation memory operations fail (if enabled)
*/
/**
* Get observability configuration
*/
getObservabilityConfig(): ObservabilityConfig | undefined;
/**
* Check if Langfuse telemetry is enabled
* Centralized utility to avoid duplication across providers
*/
isTelemetryEnabled(): boolean;
/**
* Get comprehensive telemetry status including Langfuse, OTel, and exporter health
*/
getTelemetryStatus(): {
enabled: boolean;
langfuse?: {
enabled: boolean;
baseUrl?: string;
environment?: string;
};
openTelemetry?: {
enabled: boolean;
endpoint?: string;
serviceName?: string;
};
exporters?: Array<{
name: string;
enabled: boolean;
healthy: boolean;
pendingSpans: number;
lastExportTime?: string;
latencyMs?: number;
errors?: string[];
}>;
};
/**
* Get aggregated observability metrics (latency, tokens, cost, success rate)
*/
getMetrics(): MetricsSummary;
/**
* Get all recorded spans
*/
getSpans(): SpanData[];
/**
* Get traces (spans grouped by traceId with parent-child hierarchy)
*/
getTraces(): TraceView[];
/**
* Reset all collected metrics and spans
*/
resetMetrics(): void;
/**
* Record a span for metrics tracking
*/
recordMetricsSpan(span: SpanData): void;
/**
* Record a memory operation span to both instance and global metrics aggregators.
* This ensures memory spans are visible via sdk.getSpans() and getMetricsAggregator().getSpans().
*/
private recordMemorySpan;
/**
* Public method to initialize Langfuse observability
* This method can be called externally to ensure Langfuse is properly initialized
*/
initializeLangfuseObservability(): Promise<void>;
/**
* Gracefully shutdown NeuroLink and all MCP connections
*/
shutdown(): Promise<void>;
/**
* Initialize event listeners that feed span data to MetricsAggregator.
* Listens to generation:end, stream:complete, and tool:end events.
*/
private initializeMetricsListeners;
/**
* Generate AI response with comprehensive feature support.
*
* Primary method for AI generation with support for all NeuroLink features:
* - Multi-provider support (14+ providers)
* - MCP tool integration
* - Structured JSON output with Zod schemas
* - Conversation memory (Redis or in-memory)
* - HITL security workflows
* - Middleware execution
* - Multimodal inputs (images, PDFs, CSV)
*
* @category Generation
*
* @param optionsOrPrompt - Generation options or simple text prompt
* @param optionsOrPrompt.input - Input text and optional files
* @param optionsOrPrompt.provider - AI provider name (e.g., 'vertex', 'openai', 'anthropic')
* @param optionsOrPrompt.model - Model name to use
* @param optionsOrPrompt.tools - MCP tools to enable for this generation
* @param optionsOrPrompt.schema - Zod schema for structured output validation
* @param optionsOrPrompt.temperature - Sampling temperature (0-2, default: 1.0)
* @param optionsOrPrompt.maxTokens - Maximum tokens to generate
* @param optionsOrPrompt.thinkingConfig - Extended thinking configuration (thinkingLevel: 'minimal'|'low'|'medium'|'high')
* @param optionsOrPrompt.context - Context with conversationId and userId for memory
* @returns Promise resolving to generation result with content and metadata
*
* @example Basic text generation
* ```typescript
* const result = await neurolink.generate({
* input: { text: 'Explain quantum computing' }
* });
* console.log(result.content);
* ```
*
* @example With specific provider
* ```typescript
* const result = await neurolink.generate({
* input: { text: 'Write a poem' },
* provider: 'anthropic',
* model: 'claude-3-opus'
* });
* ```
*
* @example With MCP tools
* ```typescript
* const result = await neurolink.generate({
* input: { text: 'Read README.md and summarize it' },
* tools: ['readFile']
* });
* ```
*
* @example With structured output
* ```typescript
* import { z } from 'zod';
*
* const schema = z.object({
* name: z.string(),
* age: z.number(),
* city: z.string()
* });
*
* const result = await neurolink.generate({
* input: { text: 'Extract person info: John is 30 years old from NYC' },
* schema: schema
* });
* // result.structuredData is type-safe!
* ```
*
* @example With conversation memory
* ```typescript
* const result = await neurolink.generate({
* input: { text: 'What did we discuss earlier?' },
* context: {
* conversationId: 'conv-123',
* userId: 'user-456'
* }
* });
* ```
*
* @example With multimodal input
* ```typescript
* const result = await neurolink.generate({
* input: {
* text: 'Describe this image',
* images: ['/path/to/image.jpg']
* },
* provider: 'vertex'
* });
* ```
*
* @throws {Error} When input text is missing or invalid
* @throws {Error} When all providers fail to generate content
* @throws {Error} When structured output validation fails
* @throws {Error} When HITL approval is denied
*
* @see {@link GenerateOptions} for all available options
* @see {@link GenerateResult} for result structure
* @see {@link stream} for streaming generation
* @since 1.0.0
*/
generate(optionsOrPrompt: GenerateOptions | DynamicOptions | string): Promise<GenerateResult>;
private fireConsumerOnErrorIfNotFired;
/**
* Curator P2-3: wraps a generate/stream call with the fallback
* orchestration (`providerFallback` callback + `modelChain` walker).
*
* On a model-access-denied error from the inner call:
* 1. Resolve the effective callback (per-call > instance > synthesised
* from modelChain) and the effective chain (per-call > instance).
* 2. Walk attempts: invoke callback (or pop next chain entry) → emit
* `model.fallback` event → re-call inner with the new {provider,
* model}.
* 3. Stop on first success, on a callback returning null, or after
* exhausting the chain (throw the most recent error).
*/
private runWithFallbackOrchestration;
private attemptInner;
private executeGenerateWithMetricsContext;
private executeGenerateRequest;
private prepareGenerateRequest;
private maybeHandleEarlyGenerateResult;
private runStandardGenerateRequest;
private maybeApplyGenerateOrchestration;
private prepareGenerateAugmentations;
private buildGenerateTextOptions;
private finalizeGenerateRequestResult;
private emitGenerateErrorEvent;
/**
* Schedule non-blocking memory storage after generate completes.
*/
private scheduleGenerateMemoryStorage;
/**
* Handle PPT generation mode
*/
private generateWithPPT;
/**
* Dispatch a music-generation request to the registered music handler
* for the provider named in `options.output.music.provider`.
*/
private generateWithMusic;
/**
* Dispatch an avatar (lip-sync) request to the registered avatar handler
* for the provider named in `options.output.avatar.provider`.
*/
private generateWithAvatar;
/**
* Generate with workflow engine integration
* Returns both original and processed responses for AB testing
*/
private generateWithWorkflow;
/**
* Stream with workflow engine integration
* Progressive streaming: yields preliminary response (first model) then final synthesis
*/
private streamWithWorkflow;
/**
* BACKWARD COMPATIBILITY: Legacy generateText method
* Internally calls generate() and converts result format
*/
generateText(options: TextGenerationOptions): Promise<TextGenerationResult>;
/**
* REDESIGNED INTERNAL GENERATION - NO CIRCULAR DEPENDENCIES
*
* This method implements a clean fallback chain:
* 1. Initialize conversation memory if enabled
* 2. Inject conversation history into prompt
* 3. Try MCP-enhanced generation if available
* 4. Fall back to direct provider generation
* 5. Store conversation turn for future context
*/
private generateTextInternal;
private executeGenerateTextInternalWithSpan;
private initializeGenerateTextInternalContext;
private runGenerateTextInternalFlow;
private captureOriginalConversationMessagesForRecovery;
private finalizeGenerateTextInternalResult;
private handleGenerateTextInternalFailure;
private tryRecoverGenerateTextOverflow;
/**
* Log generateTextInternal start with comprehensive analysis
*/
private logGenerateTextInternalStart;
/**
* Emit generation start events
*/
private emitGenerationStartEvents;
/**
* Initialize conversation memory for generation
* Lazily initializes memory if needed from constructor flags
*/
private initializeConversationMemoryForGeneration;
/**
* Attempt MCP generation with retry logic
*/
private attemptMCPGeneration;
/**
* Perform MCP generation with retry logic
*/
private performMCPGenerationRetries;
/**
* Try MCP-enhanced generation (no fallback recursion)
*/
private tryMCPGeneration;
private prepareMCPGenerationContext;
private logMCPConversationSummary;
private ensureMCPGenerationBudget;
private compactMCPConversationForBudget;
private generateWithMCPProvider;
/**
* Direct provider generation (no MCP, no recursion)
*/
private directProviderGeneration;
/**
* Create tool-aware system prompt that informs AI about available tools
*/
/**
* Apply per-call tool filtering (whitelist/blacklist) to a ToolInfo array.
* Used to filter the tool list before building the system prompt.
*/
private applyToolInfoFiltering;
private createToolAwareSystemPrompt;
/**
* Execute tools if available through centralized registry
* Simplified approach without domain detection - relies on tool registry
*/
private detectAndExecuteTools;
/**
* BACKWARD COMPATIBILITY: Legacy streamText method
* Internally calls stream() and converts result format
*/
streamText(prompt: string, options?: Partial<StreamOptions>): Promise<AsyncIterable<string>>;
/**
* Stream AI-generated content in real-time using the best available provider.
* This method provides real-time streaming of AI responses with full MCP tool integration.
*
* @param options - Stream configuration options
* @param options.input - Input configuration object
* @param options.input.text - The text prompt to send to the AI (required)
* @param options.provider - AI provider to use ('auto', 'openai', 'anthropic', etc.)
* @param options.model - Specific model to use (e.g., 'gpt-4', 'claude-3-opus')
* @param options.temperature - Randomness in response (0.0 = deterministic, 2.0 = very random)
* @param options.maxTokens - Maximum tokens in response
* @param options.systemPrompt - System message to set AI behavior
* @param options.disableTools - Whether to disable MCP tool usage
* @param options.enableAnalytics - Whether to include usage analytics
* @param options.enableEvaluation - Whether to include response quality evaluation
* @param options.context - Additional context for the request
* @param options.evaluationDomain - Domain for specialized evaluation
*
* @returns Promise resolving to StreamResult with an async iterable stream
*
* @example
* ```typescript
* // Basic streaming usage
* const result = await neurolink.stream({
* input: { text: "Tell me a story about space exploration" }
* });
*
* // Consume the stream
* for await (const chunk of result.stream) {
* process.stdout.write(chunk.content);
* }
*
* // Advanced streaming with options
* const result = await neurolink.stream({
* input: { text: "Explain machine learning" },
* provider: "openai",
* model: "gpt-4",
* temperature: 0.7,
* enableAnalytics: true,
* context: { domain: "education", audience: "beginners" }
* });
*
* // Access metadata and analytics
* console.log(result.provider);
* console.log(result.analytics?.usage);
* ```
*
* @throws {Error} When input text is missing or invalid
* @throws {Error} When all providers fail to generate content
* @throws {Error} When conversation memory operations fail (if enabled)
*/
stream(options: StreamOptions | DynamicOptions): Promise<StreamResult>;
/**
* Curator P2-3 / Reviewer Finding #2: stream-fallback that also covers
* errors thrown during async iteration (e.g. LiteLLM throwing inside
* `createLiteLLMTransformedStream`). The standard
* `runWithFallbackOrchestration` only catches errors thrown while the
* `StreamResult` is being created — once we hand the iterator back to
* the caller, errors raised during consumption used to bypass
* `providerFallback` / `modelChain`.
*
* This wrapper runs the orchestration to get an initial StreamResult,
* then wraps `result.stream` so that:
* - chunks are forwarded transparently while consumption succeeds
* - if iteration throws a model-access-denied error AND no chunks
* have been yielded yet, we resolve the next fallback target,
* emit `model.fallback`, and recurse
* - if chunks were already yielded, the error propagates (mid-stream
* recovery isn't safe — the consumer has half a response)
*/
private streamWithIterationFallback;
private executeStreamRequest;
private validateStreamRequestOptions;
private maybeHandleWorkflowStreamRequest;
private runStandardStreamRequest;
/**
* TTS Mode 2 synthesis helper for the stream() pipeline.
*
* m5 — extracted from runStandardStreamRequest so the surrounding generator
* stays under the max-lines-per-function lint budget. Behaviour preserved
* exactly:
* - When Mode 2 is enabled (`tts.enabled && tts.useAiResponse`) AND the
* model produced non-empty content: synthesises one final audio buffer
* and returns it as an `audioChunk` for the caller to `yield`. Resolves
* `ttsResolver` with the `TTSResult`.
* - When Mode 2 is enabled but synthesis fails: logs a warning and resolves
* `ttsResolver` with `undefined`.
* - When Mode 2 is requested but skipped (empty content / wrong mode):
* resolves `ttsResolver` with `undefined` early so callers awaiting
* `result.audio` unblock before the surrounding `finally` cleanup
* completes (Issue 7 latency micro-opt — the finally block also resolves
* defensively, so this is a redundant early signal, not a coverage fix).
*/
private synthesizeStreamModeTwo;
/**
* Prepare stream options: initialize memory, MCP, retrieval, orchestration,
* Ollama tool auto-disable, factory processing, and tool detection.
*/
private prepareStreamOptions;
/**
* Auto-disable tools for Ollama models that don't support them (stream mode).
* Prevents overwhelming smaller models with massive tool descriptions in the system message.
*/
private autoDisableOllamaStreamTools;
/**
* Set up event listeners for stream event capture (tool calls, HITL, UI components).
* Returns the shared event sequence array and a cleanup function to remove all listeners.
*/
private setupStreamEventListeners;
/**
* Handle fallback when the primary stream returns 0 chunks.
* Yields chunks from a fallback provider and updates metadata accordingly.
*/
private handleStreamFallback;
/**
* Store conversation memory after stream consumption is complete (called from finally block).
* Handles conversation memory storage in the background.
*/
private storeStreamConversationMemory;
/**
* Validate stream input with comprehensive error reporting
*/
private validateStreamInput;
/**
* Emit stream start events
*/
private emitStreamStartEvents;
/**
* Create MCP stream
*/
private createMCPStream;
/**
* Process stream result
*/
private processStreamResult;
/**
* Emit stream end events
*/
private emitStreamEndEvents;
/**
* Create stream response
*/
private createStreamResponse;
/**
* Handle stream error with fallback
*/
private handleStreamError;
/**
* Get the EventEmitter instance to listen to NeuroLink events for real-time monitoring and debugging.
* This method provides access to the internal event system that emits events during AI generation,
* tool execution, streaming, and other operations for comprehensive observability.
*
* @returns EventEmitter instance that emits various NeuroLink operation events
*
* @example
* ```typescript
* // Basic event listening setup
* const neurolink = new NeuroLink();
* const emitter = neurolink.getEventEmitter();
*
* // Listen to generation events
* emitter.on('generation:start', (event) => {
* console.log(`Generation started with provider: ${event.provider}`);
* console.log(`Started at: ${new Date(event.timestamp)}`);
* });
*
* emitter.on('generation:end', (event) => {
* console.log(`Generation completed in ${event.responseTime}ms`);
* console.log(`Tools used: ${event.toolsUsed?.length || 0}`);
* });
*
* // Listen to streaming events
* emitter.on('stream:start', (event) => {
* console.log(`Streaming started with provider: ${event.provider}`);
* });
*
* emitter.on('stream:end', (event) => {
* console.log(`Streaming completed in ${event.responseTime}ms`);
* if (event.fallback) console.log('Used fallback streaming');
* });
*
* // Listen to tool execution events
* emitter.on('tool:start', (event) => {
* console.log(`Tool execution started: ${event.toolName}`);
* });
*
* emitter.on('tool:end', (event) => {
* console.log(`Tool ${event.toolName} ${event.success ? 'succeeded' : 'failed'}`);
* console.log(`Execution time: ${event.responseTime}ms`);
* });
*
* // Listen to tool registration events
* emitter.on('tools-register:start', (event) => {
* console.log(`Registering tool: ${event.toolName}`);
* });
*
* emitter.on('tools-register:end', (event) => {
* console.log(`Tool registration ${event.success ? 'succeeded' : 'failed'}: ${event.toolName}`);
* });
*
* // Listen to external MCP server events
* emitter.on('externalMCP:serverConnected', (event) => {
* console.log(`External MCP server connected: ${event.serverId}`);
* console.log(`Tools available: ${event.toolCount || 0}`);
* });
*
* emitter.on('externalMCP:serverDisconnected', (event) => {
* console.log(`External MCP server disconnected: ${event.serverId}`);
* console.log(`Reason: ${event.reason || 'Unknown'}`);
* });
*
* emitter.on('externalMCP:toolDiscovered', (event) => {
* console.log(`New tool discovered: ${event.toolName} from ${event.serverId}`);
* });
*
* // Advanced usage with error handling
* emitter.on('error', (error) => {
* console.error('NeuroLink error:', error);
* });
*
* // Clean up event listeners when done
* function cleanup() {
* emitter.removeAllListeners();
* }
*
* process.on('SIGINT', cleanup);
* process.on('SIGTERM', cleanup);
* ```
*
* @example
* ```typescript
* // Advanced monitoring with metrics collection
* const neurolink = new NeuroLink();
* const emitter = neurolink.getEventEmitter();
* const metrics = {
* generations: 0,
* totalResponseTime: 0,
* toolExecutions: 0,
* failures: 0
* };
*
* // Collect performance metrics
* emitter.on('generation:end', (event) => {
* metrics.generations++;
* metrics.totalResponseTime += event.responseTime;
* metrics.toolExecutions += event.toolsUsed?.length || 0;
* });
*
* emitter.on('tool:end', (event) => {
* if (!event.success) {
* metrics.failures++;
* }
* });
*
* // Log metrics every 10 seconds
* setInterval(() => {
* const avgResponseTime = metrics.generations > 0
* ? metrics.totalResponseTime / metrics.generations
* : 0;
*
* console.log('NeuroLink Metrics:', {
* totalGenerations: metrics.generations,
* averageResponseTime: `${avgResponseTime.toFixed(2)}ms`,
* totalToolExecutions: metrics.toolExecutions,
* failureRate: `${((metrics.failures / (metrics.toolExecutions || 1)) * 100).toFixed(2)}%`
* });
* }, 10000);
* ```
*
* **Available Events:**
*
* **Generation Events:**
* - `generation:start` - Fired when text generation begins
* - `{ provider: string, timestamp: number }`
* - `generation:end` - Fired when text generation completes (or fails / is aborted)
* - `{ provider: string, responseTime: number, toolsUsed?: string[], timestamp: number, success?: boolean, aborted?: boolean, error?: string }`
* - `success` is `false` for both failures and client aborts; `aborted: true`
* distinguishes the latter so consumers can route cancellations
* differently from real errors. Pipeline B's metrics span maps
* `aborted: true` events to `SpanStatus.WARNING` (not ERROR).
*
* **Streaming Events:**
* - `stream:start` - Fired when streaming begins
* - `{ provider: string, timestamp: number }`
* - `stream:end` - Fired when streaming completes
* - `{ provider: string, responseTime: number, fallback?: boolean }`
*
* **Tool Events:**
* - `tool:start` - Fired when tool execution begins
* - `{ toolName: string, timestamp: number }`
* - `tool:end` - Fired when tool execution completes
* - `{ toolName: string, responseTime: number, success: boolean, timestamp: number }`
* - `tools-register:start` - Fired when tool registration begins
* - `{ toolName: string, timestamp: number }`
* - `tools-register:end` - Fired when tool registration completes
* - `{ toolName: string, success: boolean, timestamp: number }`
*
* **External MCP Events:**
* - `externalMCP:serverConnected` - Fired when external MCP server connects
* - `{ serverId: string, toolCount?: number, timestamp: number }`
* - `externalMCP:serverDisconnected` - Fired when external MCP server disconnects
* - `{ serverId: string, reason?: string, timestamp: number }`
* - `externalMCP:serverFailed` - Fired when external MCP server fails
* - `{ serverId: string, error: string, timestamp: number }`
* - `externalMCP:toolDiscovered` - Fired when external MCP tool is discovered
* - `{ toolName: string, serverId: string, timestamp: number }`
* - `externalMCP:toolRemoved` - Fired when external MCP tool is removed
* - `{ toolName: string, serverId: string, timestamp: number }`
* - `externalMCP:serverAdded` - Fired when external MCP server is added
* - `{ serverId: string, config: MCPServerInfo, toolCount: number, timestamp: number }`
* - `externalMCP:serverRemoved` - Fired when external MCP server is removed
* - `{ serverId: string, timestamp: number }`
*
* **Error Events:**
* - `error` - Fired when an error occurs
* - `{ error: Error, context?: object }`
*
* @throws {Error} This method does not throw errors as it returns the internal EventEmitter
*
* @since 1.0.0
* @see {@link https://nodejs.org/api/events.html} Node.js EventEmitter documentation
* @see {@link NeuroLink.generate} for events related to text generation
* @see {@link NeuroLink.stream} for events related to streaming
* @see {@link NeuroLink.executeTool} for events related to tool execution
*/
getEventEmitter(): TypedEventEmitter<NeuroLinkEvents>;
/**
* Curator P1-1: synchronous credential health check for a single provider.
*
* Drives a tiny real call against the provider (1-token completion or
* `/models` listing depending on provider) to confirm the configured
* credentials are valid. Useful at startup so a service can refuse to
* boot if its primary provider's credentials are broken instead of
* discovering the problem on first user request.
*
* @example
* ```ts
* const health = await neurolink.checkCredentials({ provider: "litellm" });
* if (health.status !== "ok") {
* throw new Error(`provider not ready: ${health.detail}`);
* }
* ```
*
* @param input - the provider to check
* @returns `{ provider, status, detail }`. Possible status values:
* - `"ok"` — credentials valid and provider reachable
* - `"missing"` — required env / credentials not configured
* - `"expired"` — credentials present but rejected (401/403)
* - `"denied"` — credentials valid but team not whitelisted for any model
* - `"network"` — provider unreachable (timeout, ECONNREFUSED, DNS)
* - `"unknown"` — other error; consult `detail`
*/
checkCredentials(input: {
provider: string;
model?: string;
}): Promise<{
provider: string;
status: "ok" | "missing" | "expired" | "denied" | "network" | "unknown";
detail: string;
}>;
/**
* Emit tool start event with execution tracking
* @param toolName - Name of the tool being executed
* @param input - Input parameters for the tool
* @param startTime - Timestamp when execution started
* @returns executionId for tracking this specific execution
*/
emitToolStart(toolName: string, input: unknown, startTime?: number): string;
/**
* Emit tool end event with execution summary
* @param toolName - Name of the tool that finished
* @param result - Result from the tool execution
* @param error - Error message if execution failed
* @param startTime - When execution started
* @param endTime - When execution finished
* @param executionId - Optional execution ID for tracking
*/
emitToolEnd(toolName: string, result?: unknown, error?: string, startTime?: number, endTime?: number, executionId?: string): void;
/**
* Get current tool execution contexts for stream metadata
*/
getCurrentToolExecutions(): ToolExecutionContext[];
/**
* Get tool execution history
*/
getToolExecutionHistory(): ToolExecutionSummary[];
/**
* Clear current stream tool executions (called at stream start)
*/
clearCurrentStreamExecutions(): void;
/**
* Register a custom tool that will be available to all AI providers
* @param name - Unique name for the tool
* @param tool - Tool in MCPExecutableTool format (unified MCP protocol type)
*/
registerTool(name: string, tool: MCPExecutableTool, options?: ToolRegistrationOptions): void;
/**
* Set the context that will be passed to tools during execution
* This context will be merged with any runtime context passed by the AI model
* @param context - Context object containing session info, tokens, shop data, etc.
*/
setToolContext(context: Record<string, unknown>): void;
/**
* Get the current tool execution context
* @returns Current context or undefined if not set
*/
getToolContext(): Record<string, unknown> | undefined;
/**
* Clear the tool execution context
*/
clearToolContext(): void;
/**
* Register multiple tools at once - Supports both object and array formats
* @param tools - Object mapping tool names to MCPExecutableTool format OR Array of tools with names
*
* Object format (existing): { toolName: MCPExecutableTool, ... }
* Array format (Lighthouse compatible): [{ name: string, tool: MCPExecutableTool }, ...]
*/
registerTools(tools: Record<string, MCPExecutableTool> | Array<{
name: string;
tool: MCPExecutableTool;
}>): void;
/**
* Unregister a custom tool
* @param name - Name of the tool to remove
* @returns true if the tool was removed, false if it didn't exist
*/
unregisterTool(name: string): boolean;
/**
* Register a global tool middleware that runs on every tool execution.
* Middleware receives the tool, params, context, and a next() function.
* @param middleware - The middleware function to register
* @returns this (for chaining)
*/
useToolMiddleware(middleware: import("./types/index.js").ToolMiddleware): this;
/**
* Get all registered tool middlewares
*/
getToolMiddlewares(): import("./types/index.js").ToolMiddleware[];
/**
* Flush any pending batched tool calls immediately
*/
flushToolBatch(): Promise<void>;
/**
* Get the current MCP enhancements configuration
*/
getMCPEnhancementsConfig(): MCPEnhancementsConfig | undefined;
/**
* Update agentic loop report metadata for a conversation session.
* Upserts a report entry by reportId — updates existing or adds new.
* Only supported when using Redis conversation memory.
*
* @param sessionId The session identifier
* @param report The agentic loop report metadata to upsert
* @param userId Optional user identifier
* @throws Error if conversation memory is not initialized or is not Redis-backed
*
* @example
* ```typescript
* await neurolink.updateAgenticLoopReport("session-123", {
* reportId: "report-abc",
* reportType: "META",
* reportStatus: "INPROGRESS",
* });
* ```
*/
updateAgenticLoopReport(sessionId: string, report: import("./types/index.js").AgenticLoopReportMetadata, userId?: string): Promise<void>;
/**
* Get all registered custom tools
* @returns Map of tool names to MCPExecutableTool format
*/
getCustomTools(): Map<string, MCPExecutableTool>;
/**
* Add an in-memory MCP server (from git diff)
* Allows registration of pre-instantiated server objects
* @param serverId - Unique identifier for the server
* @param serverInfo - Server configuration
*/
addInMemoryMCPServer(serverId: string, serverInfo: MCPServerInfo): Promise<void>;
/**
* Get all registered in-memory servers as a Map for ID-based lookup.
*
* This method is primarily used when you need O(1) lookup by server ID,
* such as in `testMCPServer()` for checking if a specific server exists.
*
* @returns Map of server IDs to MCPServerInfo
* @see {@link getInMemoryServerInfos} for array-based access (useful for iteration/spreading)
*/
getInMemoryServers(): Map<string, MCPServerInfo>;
/