@juspay/neurolink 12.55.3 → 12.56.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/browser/neurolink.min.js +415 -415
  3. package/dist/cli/commands/setup.d.ts +3 -3
  4. package/dist/cli/commands/setup.js +4 -5
  5. package/dist/constants/enums.d.ts +4 -9
  6. package/dist/constants/enums.js +5 -10
  7. package/dist/factories/providerDescriptors.js +7 -48
  8. package/dist/factories/providerRegistry.js +1 -9
  9. package/dist/models/manifestRegistry.js +0 -2
  10. package/dist/neurolink.d.ts +12 -8
  11. package/dist/neurolink.js +89 -25
  12. package/dist/providers/catalog/index.generated.d.ts +1 -1
  13. package/dist/providers/catalog/index.generated.js +6 -2
  14. package/dist/providers/catalog/provider-catalog.schema.json +54 -1
  15. package/dist/providers/catalog/schema.js +154 -8
  16. package/dist/providers/catalog/xor.json +86 -0
  17. package/dist/providers/configuredDecision.d.ts +2 -2
  18. package/dist/providers/configuredDecision.js +22 -5
  19. package/dist/providers/decide/baseUrl.d.ts +3 -1
  20. package/dist/providers/decide/baseUrl.js +43 -0
  21. package/dist/providers/decide/errors.js +42 -16
  22. package/dist/types/index.d.ts +1 -0
  23. package/dist/types/index.js +1 -0
  24. package/dist/types/ollamaDiagnostics.d.ts +31 -0
  25. package/dist/types/ollamaDiagnostics.js +1 -0
  26. package/dist/types/providerCatalog.d.ts +27 -1
  27. package/dist/types/providerCatalog.generated.d.ts +2 -2
  28. package/dist/types/providers.d.ts +4 -10
  29. package/dist/utils/modelChoices.js +1 -9
  30. package/dist/utils/ollamaDiagnostics.d.ts +69 -0
  31. package/dist/utils/ollamaDiagnostics.js +192 -0
  32. package/dist/utils/providerConfig.d.ts +0 -5
  33. package/dist/utils/providerConfig.js +0 -17
  34. package/dist/utils/providerHealth.d.ts +33 -19
  35. package/dist/utils/providerHealth.js +224 -85
  36. package/docs-site/static/search-index.json +7 -7
  37. package/package.json +3 -1
  38. package/dist/models/manifests/xor.d.ts +0 -12
  39. package/dist/models/manifests/xor.js +0 -30
  40. package/dist/providers/xor.d.ts +0 -48
  41. package/dist/providers/xor.js +0 -192
@@ -13,9 +13,9 @@ import type { ProviderConfigOptions, SetupArgs } from "../../types/index.js";
13
13
  * perplexity, sambanova, together-ai, xai — mistral is the 9th catalog
14
14
  * provider but keeps its own handleMistralSetup() flow above, so it's
15
15
  * excluded here), spread directly from the catalog JSON via
16
- * buildCatalogConfigOptions(); decide-kind catalog entries (laya) are spread
17
- * the same way from getDecideCatalogEntries(). The hand rows listed first
18
- * reuse the existing createXConfig() factories in providerConfig.ts; the
16
+ * buildCatalogConfigOptions(); decide-kind catalog entries (laya, xor) are
17
+ * spread the same way from getDecideCatalogEntries(). The hand rows listed
18
+ * first reuse the existing createXConfig() factories in providerConfig.ts; the
19
19
  * remaining 5 (ollama, litellm, sagemaker, lm-studio, llamacpp) don't have a
20
20
  * factory and are defined inline using their real env var names. lm-studio
21
21
  * and llamacpp previously
@@ -21,7 +21,7 @@ import { handleHuggingFaceSetup } from "./setup-huggingface.js";
21
21
  import { handleMistralSetup } from "./setup-mistral.js";
22
22
  import { OpenRouterModels, } from "../../constants/enums.js";
23
23
  import { PROVIDER_DESCRIPTORS_BY_NAME } from "../../factories/providerDescriptors.js";
24
- import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createXorConfig, createPerplexityDeciderConfig, createCloudflareClefConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
24
+ import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createPerplexityDeciderConfig, createCloudflareClefConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
25
25
  import { getCatalogJsonEntries, getDecideCatalogEntries, buildCatalogConfigOptions, } from "../../providers/catalog/loader.js";
26
26
  // Provider information database
27
27
  const PROVIDERS = [
@@ -150,9 +150,9 @@ const PROVIDERS = [
150
150
  * perplexity, sambanova, together-ai, xai — mistral is the 9th catalog
151
151
  * provider but keeps its own handleMistralSetup() flow above, so it's
152
152
  * excluded here), spread directly from the catalog JSON via
153
- * buildCatalogConfigOptions(); decide-kind catalog entries (laya) are spread
154
- * the same way from getDecideCatalogEntries(). The hand rows listed first
155
- * reuse the existing createXConfig() factories in providerConfig.ts; the
153
+ * buildCatalogConfigOptions(); decide-kind catalog entries (laya, xor) are
154
+ * spread the same way from getDecideCatalogEntries(). The hand rows listed
155
+ * first reuse the existing createXConfig() factories in providerConfig.ts; the
156
156
  * remaining 5 (ollama, litellm, sagemaker, lm-studio, llamacpp) don't have a
157
157
  * factory and are defined inline using their real env var names. lm-studio
158
158
  * and llamacpp previously
@@ -172,7 +172,6 @@ export const EXTRA_PROVIDER_CONFIGS = {
172
172
  ideogram: createIdeogramConfig(),
173
173
  recraft: createRecraftConfig(),
174
174
  typesafe: createTypeSafeConfig(),
175
- xor: createXorConfig(),
176
175
  "perplexity-decider": createPerplexityDeciderConfig(),
177
176
  "cloudflare-clef": createCloudflareClefConfig(),
178
177
  ...Object.fromEntries(getDecideCatalogEntries().map((e) => [e.id, buildCatalogConfigOptions(e)])),
@@ -103,6 +103,7 @@ export declare enum AIProviderName {
103
103
  WANDB_INFERENCE = "wandb-inference",
104
104
  XAI = "xai",
105
105
  XIAOMI_MIMO = "xiaomi-mimo",
106
+ XOR = "xor",
106
107
  Z_AI = "z-ai",
107
108
  COHERE = "cohere",
108
109
  REPLICATE = "replicate",
@@ -113,8 +114,6 @@ export declare enum AIProviderName {
113
114
  RECRAFT = "recraft",
114
115
  /** TypeSafe (Jev) — serves the `decide` inference type only. */
115
116
  TYPESAFE = "typesafe",
116
- /** XOR (Juspay, open weights) — serves the `decide` inference type only. */
117
- XOR = "xor",
118
117
  /**
119
118
  * Perplexity Decisions API (`pplx-decider-v1-27b`) — serves the `decide`
120
119
  * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
@@ -1763,6 +1762,9 @@ export declare enum XaiModels {
1763
1762
  export declare enum XiaomiMimoModels {
1764
1763
  MIMO_V2_6_FLASH = "mimo-v2.6-flash"
1765
1764
  }
1765
+ export declare enum XorModels {
1766
+ XOR_1_1 = "xor-1.1"
1767
+ }
1766
1768
  export declare enum ZAiModels {
1767
1769
  GLM_5_3 = "glm-5.3",
1768
1770
  GLM_5_3_FLASH = "glm-5.3-flash",
@@ -1788,13 +1790,6 @@ export declare enum TypeSafeModels {
1788
1790
  JEV_LATEST = "jev-latest",
1789
1791
  JEV_PREVIEW = "jev-preview"
1790
1792
  }
1791
- /**
1792
- * XOR decision models. Hand-written: XOR is a Tier-3 provider, so it is not in
1793
- * the provider catalog and codegen never touches this.
1794
- */
1795
- export declare enum XorModels {
1796
- XOR_1_1 = "xor-1.1"
1797
- }
1798
1793
  /**
1799
1794
  * Perplexity decision models. Hand-written: the Decisions API is a Tier-3
1800
1795
  * provider, so it is not in the provider catalog and codegen never touches
@@ -108,6 +108,7 @@ export var AIProviderName;
108
108
  AIProviderName["WANDB_INFERENCE"] = "wandb-inference";
109
109
  AIProviderName["XAI"] = "xai";
110
110
  AIProviderName["XIAOMI_MIMO"] = "xiaomi-mimo";
111
+ AIProviderName["XOR"] = "xor";
111
112
  AIProviderName["Z_AI"] = "z-ai";
112
113
  // ── END GENERATED(provider-members) ──
113
114
  AIProviderName["COHERE"] = "cohere";
@@ -119,8 +120,6 @@ export var AIProviderName;
119
120
  AIProviderName["RECRAFT"] = "recraft";
120
121
  /** TypeSafe (Jev) — serves the `decide` inference type only. */
121
122
  AIProviderName["TYPESAFE"] = "typesafe";
122
- /** XOR (Juspay, open weights) — serves the `decide` inference type only. */
123
- AIProviderName["XOR"] = "xor";
124
123
  /**
125
124
  * Perplexity Decisions API (`pplx-decider-v1-27b`) — serves the `decide`
126
125
  * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
@@ -2086,6 +2085,10 @@ export var XiaomiMimoModels;
2086
2085
  (function (XiaomiMimoModels) {
2087
2086
  XiaomiMimoModels["MIMO_V2_6_FLASH"] = "mimo-v2.6-flash";
2088
2087
  })(XiaomiMimoModels || (XiaomiMimoModels = {}));
2088
+ export var XorModels;
2089
+ (function (XorModels) {
2090
+ XorModels["XOR_1_1"] = "xor-1.1";
2091
+ })(XorModels || (XorModels = {}));
2089
2092
  export var ZAiModels;
2090
2093
  (function (ZAiModels) {
2091
2094
  ZAiModels["GLM_5_3"] = "glm-5.3";
@@ -2114,14 +2117,6 @@ export var TypeSafeModels;
2114
2117
  TypeSafeModels["JEV_LATEST"] = "jev-latest";
2115
2118
  TypeSafeModels["JEV_PREVIEW"] = "jev-preview";
2116
2119
  })(TypeSafeModels || (TypeSafeModels = {}));
2117
- /**
2118
- * XOR decision models. Hand-written: XOR is a Tier-3 provider, so it is not in
2119
- * the provider catalog and codegen never touches this.
2120
- */
2121
- export var XorModels;
2122
- (function (XorModels) {
2123
- XorModels["XOR_1_1"] = "xor-1.1";
2124
- })(XorModels || (XorModels = {}));
2125
2120
  /**
2126
2121
  * Perplexity decision models. Hand-written: the Decisions API is a Tier-3
2127
2122
  * provider, so it is not in the provider catalog and codegen never touches
@@ -1,5 +1,5 @@
1
1
  import { AIProviderName } from "../constants/enums.js";
2
- import { GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, TypeSafeModels, XorModels, PerplexityDeciderModels, CloudflareClefModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
2
+ import { GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, TypeSafeModels, PerplexityDeciderModels, CloudflareClefModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
3
3
  import { API_KEY_FORMATS } from "../utils/providerConfig.js";
4
4
  import { getCatalogJsonEntries, getDecideCatalogEntries, catalogCredentialsKey, catalogEnvVar, } from "../providers/catalog/loader.js";
5
5
  import { DEFAULT_INFERENCE_KINDS } from "../types/index.js";
@@ -467,64 +467,23 @@ const HAND_DESCRIPTORS = [
467
467
  },
468
468
  setupUrl: "https://console.typesafe.ai/keys",
469
469
  },
470
- // Decision precedence is decideOrder, not position: typesafe 1, laya 2 (from
471
- // catalog/laya.json), xor 3, perplexity-decider 4, cloudflare-clef 5.
470
+ // Decision precedence is decideOrder, not position: typesafe 1, laya 2 and
471
+ // xor 3 (from catalog/laya.json and catalog/xor.json), perplexity-decider 4,
472
+ // cloudflare-clef 5.
472
473
  // resolveDefaultDecisionProvider() returns the first configured
473
474
  // DECISION_PROVIDERS entry in that order, so a host configured for several
474
475
  // keeps Jev for every built-in consumer and reaches the others only by
475
476
  // naming them. Renumbering silently changes which model routes, compacts
476
477
  // and plans for every such host.
477
- {
478
- name: AIProviderName.XOR,
479
- aliases: [],
480
- credentialsKey: "xor",
481
- envVars: {
482
- apiKey: "XOR_API_KEY",
483
- baseURL: "XOR_BASE_URL",
484
- // XOR has no built-in endpoint (it is reached at a deployment or behind a
485
- // proxy), so a key is useless without a base URL. Both are required for
486
- // it to count as configured, from the environment or credentials.xor.
487
- extraRequired: ["XOR_BASE_URL"],
488
- model: "XOR_MODEL",
489
- },
490
- defaultModel: XorModels.XOR_1_1,
491
- // Serves only `decide`, like TypeSafe and Laya — the one declaration that
492
- // keeps it out of every generation code path.
493
- inferenceKinds: ["decide"],
494
- decideOrder: 3,
495
- toolSupport: "none",
496
- localRuntime: false,
497
- healthCheck: "env-only",
498
- // Deliberately NO autoSelectPriority / autoSelectPreference /
499
- // defaultHealthSweepPriority, for the same reason as TypeSafe above.
500
- timeouts: { decideMs: 5000 },
501
- // The deployment's prefill is 250,000 tokens, shared by the state, the
502
- // questions and any media. 200,000 leaves 20% for those, and a state
503
- // between this and the server's 4 MB would otherwise pass validation and
504
- // fail inside the engine as a 500, which the base class would retry. There
505
- // is no question cap (the server only limits a choice to 2..255 options).
506
- // Non-ASCII text is charged per character because the four-characters-
507
- // per-token estimate is several times too generous for CJK on this
508
- // tokenizer. One token per character is a deliberate upper bound, to be
509
- // replaced by a live measurement.
510
- decisionLimits: {
511
- maxStateTokens: 200_000,
512
- nonAsciiTokensPerChar: 1,
513
- // The server takes 1..8 images and one video, and refuses a body over
514
- // 8 MB.
515
- media: { maxImages: 8, video: true, maxRequestBytes: 8 * 1024 * 1024 },
516
- },
517
- setupUrl: "https://huggingface.co/juspay/xor",
518
- },
519
478
  {
520
479
  name: AIProviderName.PERPLEXITY_DECIDER,
521
480
  aliases: [],
522
481
  credentialsKey: "perplexityDecider",
523
482
  envVars: {
524
483
  // The same key the `perplexity` text provider reads, so an ambient
525
- // PERPLEXITY_API_KEY configures this provider too. It sits after XOR in
526
- // this list, which is what keeps that from displacing any other decision
527
- // provider a host has configured.
484
+ // PERPLEXITY_API_KEY configures this provider too. Its decideOrder (4)
485
+ // sits after XOR's (3), which is what keeps that from displacing any other
486
+ // decision provider a host has configured.
528
487
  apiKey: "PERPLEXITY_API_KEY",
529
488
  // Optional: the API is hosted at a public endpoint, so a key alone is
530
489
  // enough and there is no extraRequired.
@@ -1,6 +1,6 @@
1
1
  import { ProviderFactory } from "./providerFactory.js";
2
2
  import { logger } from "../utils/logger.js";
3
- import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, TypeSafeModels, XorModels, PerplexityDeciderModels, CloudflareClefModels, ReplicateModels, } from "../constants/enums.js";
3
+ import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, TypeSafeModels, PerplexityDeciderModels, CloudflareClefModels, ReplicateModels, } from "../constants/enums.js";
4
4
  import { PROVIDER_DESCRIPTORS_BY_NAME } from "./providerDescriptors.js";
5
5
  import { OPENAI_COMPAT_CATALOG } from "../providers/openaiCompatCatalog.js";
6
6
  import { providerChoicesFor } from "./mediaHandlerCatalog.js";
@@ -250,14 +250,6 @@ export class ProviderRegistry {
250
250
  const { TypeSafeProvider } = await import("../providers/typesafe.js");
251
251
  return new TypeSafeProvider(modelName, sdk, undefined, typesafeCreds);
252
252
  }, process.env.TYPESAFE_MODEL || TypeSafeModels.JEV_LATEST, ["jev", "typesafe-ai"], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.TYPESAFE));
253
- // Register XOR — the third `decide` provider, reached at whatever base
254
- // URL its config names. Its descriptor declares inferenceKinds:
255
- // ["decide"], so nothing in the generation fallback chain can reach it.
256
- ProviderFactory.registerProvider(AIProviderName.XOR, async (modelName, _providerName, sdk, _region, credentials) => {
257
- const xorCreds = credentials;
258
- const { XorProvider } = await import("../providers/xor.js");
259
- return new XorProvider(modelName, sdk, undefined, xorCreds);
260
- }, process.env.XOR_MODEL || XorModels.XOR_1_1, [], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.XOR));
261
253
  // Register the Perplexity Decisions API — a `decide` provider reached at
262
254
  // Perplexity's public endpoint. Its descriptor declares inferenceKinds:
263
255
  // ["decide"], so nothing in the generation fallback chain can reach it,
@@ -20,7 +20,6 @@ import { llamacppManifest } from "./manifests/llamacpp.js";
20
20
  import { xaiManifest } from "./manifests/xai.js";
21
21
  import { groqManifest } from "./manifests/groq.js";
22
22
  import { typesafeManifest } from "./manifests/typesafe.js";
23
- import { xorManifest } from "./manifests/xor.js";
24
23
  import { perplexityDeciderManifest } from "./manifests/perplexityDecider.js";
25
24
  import { cloudflareClefManifest } from "./manifests/cloudflareClef.js";
26
25
  import { cohereManifest } from "./manifests/cohere.js";
@@ -160,7 +159,6 @@ export const MANIFEST_REGISTRY = {
160
159
  groq: groqManifest,
161
160
  typesafe: typesafeManifest,
162
161
  ...Object.fromEntries(getDecideCatalogEntries().map((e) => [e.id, buildDecideManifest(e)])),
163
- xor: xorManifest,
164
162
  "perplexity-decider": perplexityDeciderManifest,
165
163
  "cloudflare-clef": cloudflareClefManifest,
166
164
  cerebras: catalogManifest("cerebras"),
@@ -1,10 +1,3 @@
1
- /**
2
- * NeuroLink - Unified AI Interface with Real MCP Tool Integration
3
- *
4
- * REDESIGNED FALLBACK CHAIN - NO CIRCULAR DEPENDENCIES
5
- * Enhanced AI provider system with natural MCP tool access.
6
- * Uses real MCP infrastructure for tool discovery and execution.
7
- */
8
1
  import type { DecisionCallerOptions, DecisionLimitsQuery, DecisionLimitsReading, DecisionOptions, DecisionResult, AgentDefinition, AgentNetworkConfig, AgentRunOptions, AgentRunOutcome, AgentToolRegistrationOptions, IsolatedAgentDefinition, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk, WorkerInstanceOptions } from "./types/index.js";
9
2
  import type { CompactionConfig, CompactionResult, ArtifactStore, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ConversationSeedMessage, ConversationSeedOptions, RecordConversationTurnOptions, RecordConversationTurnResult, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus, ChecklistState, ArtifactPageRequest, BankArtifactOptions, BankedArtifactRef, DelegateCollectRequest, DelegateCollectResult, DelegateHandle, DelegateRegistrationOptions, DelegateSpawnOptions, BackgroundCommandHandle, BackgroundCommandOptions, BackgroundCommandOutputPage, BackgroundCommandPageRequest, BackgroundCommandPolicy, BackgroundCommandStatus, GitToolResult, GitToolsetOptions } from "./types/index.js";
10
3
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
@@ -612,6 +605,12 @@ export declare class NeuroLink {
612
605
  * @returns Modified options with orchestrated provider marked in context, or empty object if validation fails
613
606
  */
614
607
  private applyStreamOrchestration;
608
+ /**
609
+ * Read a nonempty orchestration preference only when provider is absent/auto.
610
+ * An explicit provider always takes precedence over incoming or generated
611
+ * context; empty, non-string and auto preferences are ignored.
612
+ */
613
+ private getOrchestratedPreferredProvider;
615
614
  /**
616
615
  * MAIN ENTRY POINT: Enhanced generate method with new function signature
617
616
  * Replaces both generateText and legacy methods
@@ -937,6 +936,7 @@ export declare class NeuroLink {
937
936
  *
938
937
  * The router is skipped when:
939
938
  * - no `requestRouter` is configured on this instance, or
939
+ * - a valid orchestration preference already owns the provider/model pair, or
940
940
  * - the caller explicitly set both `options.provider` AND `options.model`
941
941
  * (we only skip the router if BOTH are set; a caller setting only one
942
942
  * still lets the router fill in the other field).
@@ -1118,6 +1118,8 @@ export declare class NeuroLink {
1118
1118
  private generateWithMCPProvider;
1119
1119
  /**
1120
1120
  * Direct provider generation (no MCP, no recursion)
1121
+ * An explicit provider precedes orchestration context; otherwise a valid
1122
+ * orchestration preference is tried before the existing provider fallback list.
1121
1123
  */
1122
1124
  private directProviderGeneration;
1123
1125
  /**
@@ -1324,7 +1326,9 @@ export declare class NeuroLink {
1324
1326
  */
1325
1327
  private emitStreamStartEvents;
1326
1328
  /**
1327
- * Create MCP stream
1329
+ * Create the provider-backed MCP stream with the existing tool lifecycle.
1330
+ * An explicit provider precedes a valid orchestration context preference;
1331
+ * absent either, getBestProvider retains its normal automatic selection.
1328
1332
  */
1329
1333
  private createMCPStream;
1330
1334
  /**
package/dist/neurolink.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { beginOllamaDiagnostic, captureOllamaDiagnosticContext, completeOllamaDiagnostic, getOllamaDiagnosticErrorMessage, } from "./utils/ollamaDiagnostics.js";
1
2
  /**
2
3
  * NeuroLink - Unified AI Interface with Real MCP Tool Integration
3
4
  *
@@ -203,13 +204,6 @@ const isNonRetryableForPool = sharedIsNonRetryableForPool;
203
204
  */
204
205
  const MEMORY_WRITE_TIMEOUT_MS = 30_000;
205
206
  /** Native tags share the health check's endpoint precedence and origin. */
206
- function getOllamaTagsUrl() {
207
- const baseUrl = process.env.OLLAMA_BASE_URL ||
208
- process.env.OLLAMA_API_BASE ||
209
- "http://localhost:11434";
210
- // Native /api/tags is at the origin even if the compatible base ends /v1.
211
- return new URL("/api/tags", baseUrl).toString();
212
- }
213
207
  /**
214
208
  * NeuroLink - Universal AI Development Platform
215
209
  *
@@ -2737,8 +2731,17 @@ Current user's request: ${currentInput}`;
2737
2731
  }
2738
2732
  // For Ollama, check if service is running and model is available
2739
2733
  if (route.provider === "ollama") {
2734
+ let ollamaAttempt;
2735
+ let ollamaOutcome = "not-run";
2740
2736
  try {
2741
- const response = await fetch(getOllamaTagsUrl(), {
2737
+ const ollamaContext = captureOllamaDiagnosticContext();
2738
+ const gate = beginOllamaDiagnostic(ollamaContext);
2739
+ if (gate.kind === "suppressed") {
2740
+ return {};
2741
+ }
2742
+ ollamaAttempt = gate;
2743
+ ollamaOutcome = "transport-failure";
2744
+ const response = await fetch(ollamaContext.url, {
2742
2745
  method: "GET",
2743
2746
  signal: AbortSignal.timeout(2000),
2744
2747
  });
@@ -2752,6 +2755,7 @@ Current user's request: ${currentInput}`;
2752
2755
  });
2753
2756
  return {}; // Return empty object to preserve existing fallback behavior
2754
2757
  }
2758
+ ollamaOutcome = "protocol-failure";
2755
2759
  const responseData = await response.json();
2756
2760
  const models = responseData?.models;
2757
2761
  // Runtime-safe guard: ensure models is an array with valid objects
@@ -2762,6 +2766,7 @@ Current user's request: ${currentInput}`;
2762
2766
  });
2763
2767
  return {}; // Return empty object for fallback behavior
2764
2768
  }
2769
+ ollamaOutcome = "success";
2765
2770
  // Filter and validate models before comparison
2766
2771
  const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
2767
2772
  const targetModel = route.model;
@@ -2807,12 +2812,17 @@ Current user's request: ${currentInput}`;
2807
2812
  routedProvider: route.provider,
2808
2813
  routedModel: route.model,
2809
2814
  reason: error instanceof Error
2810
- ? error.message
2815
+ ? getOllamaDiagnosticErrorMessage(error)
2811
2816
  : "Ollama service check failed",
2812
2817
  orchestrationTime: `${Date.now() - startTime}ms`,
2813
2818
  });
2814
2819
  return {}; // Return empty object to preserve existing fallback behavior
2815
2820
  }
2821
+ finally {
2822
+ if (ollamaAttempt) {
2823
+ completeOllamaDiagnostic(ollamaAttempt, ollamaOutcome);
2824
+ }
2825
+ }
2816
2826
  }
2817
2827
  logger.debug("Orchestration route determined", {
2818
2828
  taskType: classification.type,
@@ -2875,8 +2885,17 @@ Current user's request: ${currentInput}`;
2875
2885
  }
2876
2886
  // For Ollama, check if service is running and model is available
2877
2887
  if (route.provider === "ollama") {
2888
+ let ollamaAttempt;
2889
+ let ollamaOutcome = "not-run";
2878
2890
  try {
2879
- const response = await fetch(getOllamaTagsUrl(), {
2891
+ const ollamaContext = captureOllamaDiagnosticContext();
2892
+ const gate = beginOllamaDiagnostic(ollamaContext);
2893
+ if (gate.kind === "suppressed") {
2894
+ return {};
2895
+ }
2896
+ ollamaAttempt = gate;
2897
+ ollamaOutcome = "transport-failure";
2898
+ const response = await fetch(ollamaContext.url, {
2880
2899
  method: "GET",
2881
2900
  signal: AbortSignal.timeout(2000),
2882
2901
  });
@@ -2890,6 +2909,7 @@ Current user's request: ${currentInput}`;
2890
2909
  });
2891
2910
  return {}; // Return empty object to preserve existing fallback behavior
2892
2911
  }
2912
+ ollamaOutcome = "protocol-failure";
2893
2913
  const responseData = await response.json();
2894
2914
  const models = responseData?.models;
2895
2915
  // Runtime-safe guard: ensure models is an array with valid objects
@@ -2900,6 +2920,7 @@ Current user's request: ${currentInput}`;
2900
2920
  });
2901
2921
  return {}; // Return empty object for fallback behavior
2902
2922
  }
2923
+ ollamaOutcome = "success";
2903
2924
  // Filter and validate models before comparison
2904
2925
  const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
2905
2926
  const targetModel = route.model;
@@ -2945,12 +2966,17 @@ Current user's request: ${currentInput}`;
2945
2966
  routedProvider: route.provider,
2946
2967
  routedModel: route.model,
2947
2968
  reason: error instanceof Error
2948
- ? error.message
2969
+ ? getOllamaDiagnosticErrorMessage(error)
2949
2970
  : "Ollama service check failed",
2950
2971
  orchestrationTime: `${Date.now() - startTime}ms`,
2951
2972
  });
2952
2973
  return {}; // Return empty object to preserve existing fallback behavior
2953
2974
  }
2975
+ finally {
2976
+ if (ollamaAttempt) {
2977
+ completeOllamaDiagnostic(ollamaAttempt, ollamaOutcome);
2978
+ }
2979
+ }
2954
2980
  }
2955
2981
  logger.debug("Stream orchestration route determined", {
2956
2982
  taskType: classification.type,
@@ -2978,6 +3004,22 @@ Current user's request: ${currentInput}`;
2978
3004
  throw error;
2979
3005
  }
2980
3006
  }
3007
+ /**
3008
+ * Read a nonempty orchestration preference only when provider is absent/auto.
3009
+ * An explicit provider always takes precedence over incoming or generated
3010
+ * context; empty, non-string and auto preferences are ignored.
3011
+ */
3012
+ getOrchestratedPreferredProvider(options) {
3013
+ if (options.provider && options.provider !== "auto") {
3014
+ return undefined;
3015
+ }
3016
+ const preferred = options.context?.__orchestratedPreferredProvider;
3017
+ return typeof preferred === "string" &&
3018
+ preferred.length > 0 &&
3019
+ preferred !== "auto"
3020
+ ? preferred
3021
+ : undefined;
3022
+ }
2981
3023
  /**
2982
3024
  * MAIN ENTRY POINT: Enhanced generate method with new function signature
2983
3025
  * Replaces both generateText and legacy methods
@@ -4339,6 +4381,7 @@ Current user's request: ${currentInput}`;
4339
4381
  *
4340
4382
  * The router is skipped when:
4341
4383
  * - no `requestRouter` is configured on this instance, or
4384
+ * - a valid orchestration preference already owns the provider/model pair, or
4342
4385
  * - the caller explicitly set both `options.provider` AND `options.model`
4343
4386
  * (we only skip the router if BOTH are set; a caller setting only one
4344
4387
  * still lets the router fill in the other field).
@@ -4358,8 +4401,11 @@ Current user's request: ${currentInput}`;
4358
4401
  }
4359
4402
  // Stand down if the classifier router already selected a provider/model on
4360
4403
  // this turn — one explicit precedence order: classifierRouter wins.
4361
- if (options.context
4362
- ?.__classifierRouted) {
4404
+ if (options.context?.__classifierRouted) {
4405
+ return;
4406
+ }
4407
+ // Keep an orchestration-selected model with the provider that owns it.
4408
+ if (this.getOrchestratedPreferredProvider(options)) {
4363
4409
  return;
4364
4410
  }
4365
4411
  // Skip if the caller explicitly set both provider AND model — they know
@@ -6577,6 +6623,8 @@ Current user's request: ${currentInput}`;
6577
6623
  }
6578
6624
  /**
6579
6625
  * Direct provider generation (no MCP, no recursion)
6626
+ * An explicit provider precedes orchestration context; otherwise a valid
6627
+ * orchestration preference is tried before the existing provider fallback list.
6580
6628
  */
6581
6629
  async directProviderGeneration(options) {
6582
6630
  const startTime = Date.now();
@@ -6593,13 +6641,8 @@ Current user's request: ${currentInput}`;
6593
6641
  "ollama",
6594
6642
  ];
6595
6643
  const requestedProvider = options.provider === "auto" ? undefined : options.provider;
6596
- // Check for orchestrated preferred provider in context
6597
- const preferredOrchestrated = options.context &&
6598
- typeof options.context === "object" &&
6599
- "__orchestratedPreferredProvider" in options.context
6600
- ? options.context
6601
- .__orchestratedPreferredProvider
6602
- : undefined;
6644
+ // Read the shared preference while keeping an explicit provider first.
6645
+ const preferredOrchestrated = this.getOrchestratedPreferredProvider(options);
6603
6646
  // Build provider list with orchestrated preference first, then fallback to full list
6604
6647
  const tryProviders = preferredOrchestrated
6605
6648
  ? [
@@ -9425,11 +9468,16 @@ Current user's request: ${currentInput}`;
9425
9468
  this.emitter.emit("message", `Starting ${options.provider || "auto"} stream...`);
9426
9469
  }
9427
9470
  /**
9428
- * Create MCP stream
9471
+ * Create the provider-backed MCP stream with the existing tool lifecycle.
9472
+ * An explicit provider precedes a valid orchestration context preference;
9473
+ * absent either, getBestProvider retains its normal automatic selection.
9429
9474
  */
9430
9475
  async createMCPStream(options) {
9431
- // Simplified placeholder - in the actual implementation this would contain the complex MCP stream logic
9432
- const providerName = await getBestProvider(options.provider);
9476
+ // Consume the validated orchestration choice after an explicit provider.
9477
+ const requestedProvider = options.provider && options.provider !== "auto"
9478
+ ? options.provider
9479
+ : this.getOrchestratedPreferredProvider(options);
9480
+ const providerName = await getBestProvider(requestedProvider);
9433
9481
  const provider = await AIProviderFactory.createProvider(providerName, options.model, !options.disableTools, // Pass disableTools as inverse of enableMCP
9434
9482
  this, // Pass SDK instance
9435
9483
  options.region, // Pass region parameter
@@ -11900,14 +11948,24 @@ Current user's request: ${currentInput}`;
11900
11948
  }
11901
11949
  // Special handling for Ollama
11902
11950
  if (providerName === "ollama") {
11951
+ let ollamaAttempt;
11952
+ let ollamaOutcome = "not-run";
11903
11953
  try {
11904
- const response = await fetch(getOllamaTagsUrl(), {
11954
+ const ollamaContext = captureOllamaDiagnosticContext();
11955
+ const gate = beginOllamaDiagnostic(ollamaContext);
11956
+ if (gate.kind === "suppressed") {
11957
+ throw new Error(gate.reason);
11958
+ }
11959
+ ollamaAttempt = gate;
11960
+ ollamaOutcome = "transport-failure";
11961
+ const response = await fetch(ollamaContext.url, {
11905
11962
  method: "GET",
11906
11963
  signal: AbortSignal.timeout(PROVIDER_TIMEOUTS.AUTH_MS),
11907
11964
  });
11908
11965
  if (!response.ok) {
11909
11966
  throw new Error("Ollama service not responding");
11910
11967
  }
11968
+ ollamaOutcome = "protocol-failure";
11911
11969
  const responseData = await response.json();
11912
11970
  const models = responseData?.models;
11913
11971
  // Runtime-safe guard: ensure models is an array with valid objects
@@ -11918,6 +11976,7 @@ Current user's request: ${currentInput}`;
11918
11976
  });
11919
11977
  throw new Error("Invalid models format from Ollama API");
11920
11978
  }
11979
+ ollamaOutcome = "success";
11921
11980
  // Filter and validate models before comparison
11922
11981
  const validModels = models.filter((m) => m && typeof m === "object" && typeof m.name === "string");
11923
11982
  if (validModels.length > 0) {
@@ -11948,11 +12007,16 @@ Current user's request: ${currentInput}`;
11948
12007
  configured: false,
11949
12008
  authenticated: false,
11950
12009
  error: error instanceof Error
11951
- ? error.message
12010
+ ? getOllamaDiagnosticErrorMessage(error)
11952
12011
  : "Ollama service not running",
11953
12012
  responseTime: Date.now() - startTime,
11954
12013
  };
11955
12014
  }
12015
+ finally {
12016
+ if (ollamaAttempt) {
12017
+ completeOllamaDiagnostic(ollamaAttempt, ollamaOutcome);
12018
+ }
12019
+ }
11956
12020
  }
11957
12021
  // Test other providers with actual generation call. The losing
11958
12022
  // side of the race is aborted in `finally` below — previously the
@@ -2,4 +2,4 @@ import type { DecideCatalogJson, ProviderCatalogJson } from "../../types/index.j
2
2
  export declare const CATALOG_JSON_ENTRIES: ProviderCatalogJson[];
3
3
  export declare const CATALOG_PROVIDER_IDS: readonly ["a2agent", "abliteration", "above-dev", "ai21", "aiand", "aihubmix", "aionlabs", "ambient", "api-route", "arcee", "atlas-cloud", "avian-io", "baidu-qianfan", "baseten", "bee-heossi", "byteplus-modelark", "bytez", "cerebras", "charm-hyper", "chutes", "cloudflare", "crusoe", "dashscope", "deepinfra", "deepseek", "empiriolabs", "featherless-ai", "fireworks", "friendli", "gmicloud", "gradientai", "groq", "hetzner-inference", "huggingface", "inception-labs", "inceptron", "inco", "inference-net", "io-intelligence", "koscompute", "lemonfox-ai", "lilac", "llmtech", "longcat", "mancer", "meta-model-api", "minimax", "mistral", "moark", "modelscope", "moonshot-ai", "morph", "naver-clova-studio", "nebius", "neuralwatt", "novita", "ovhcloud", "parasail", "pareto-inference", "perplexity", "poolside", "prime-intellect", "reka", "runinfra", "sakana-ai", "sambanova", "sarvam", "scaleway", "siliconflow", "stackit", "stepfun", "subconscious", "synthetic", "telnyx", "thinking-machines", "tinfoil", "together-ai", "umans-ai", "upstage", "venice-ai", "vispark", "vultr-inference", "wafer", "wandb-inference", "xai", "xiaomi-mimo", "z-ai"];
4
4
  export declare const DECIDE_CATALOG_JSON_ENTRIES: DecideCatalogJson[];
5
- export declare const DECIDE_CATALOG_PROVIDER_IDS: readonly ["laya"];
5
+ export declare const DECIDE_CATALOG_PROVIDER_IDS: readonly ["laya", "xor"];
@@ -87,6 +87,7 @@ import waferJson from "./wafer.json" with { type: "json" };
87
87
  import wandbInferenceJson from "./wandb-inference.json" with { type: "json" };
88
88
  import xaiJson from "./xai.json" with { type: "json" };
89
89
  import xiaomiMimoJson from "./xiaomi-mimo.json" with { type: "json" };
90
+ import xorJson from "./xor.json" with { type: "json" };
90
91
  import zAiJson from "./z-ai.json" with { type: "json" };
91
92
  import { isDecideCatalogEntry, parseCatalogEntry } from "./schema.js";
92
93
  const asDecideEntry = (raw) => {
@@ -274,5 +275,8 @@ export const CATALOG_PROVIDER_IDS = [
274
275
  "xiaomi-mimo",
275
276
  "z-ai",
276
277
  ];
277
- export const DECIDE_CATALOG_JSON_ENTRIES = [layaJson].map(asDecideEntry);
278
- export const DECIDE_CATALOG_PROVIDER_IDS = ["laya"];
278
+ export const DECIDE_CATALOG_JSON_ENTRIES = [
279
+ layaJson,
280
+ xorJson,
281
+ ].map(asDecideEntry);
282
+ export const DECIDE_CATALOG_PROVIDER_IDS = ["laya", "xor"];