@juspay/neurolink 12.51.3 → 12.52.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.
@@ -100,6 +100,7 @@ export declare enum AIProviderName {
100
100
  WAFER = "wafer",
101
101
  WANDB_INFERENCE = "wandb-inference",
102
102
  XAI = "xai",
103
+ XIAOMI_MIMO = "xiaomi-mimo",
103
104
  Z_AI = "z-ai",
104
105
  COHERE = "cohere",
105
106
  REPLICATE = "replicate",
@@ -1751,6 +1752,9 @@ export declare enum XaiModels {
1751
1752
  GROK_2_VISION_LATEST = "grok-2-vision-latest",
1752
1753
  GROK_BETA = "grok-beta"
1753
1754
  }
1755
+ export declare enum XiaomiMimoModels {
1756
+ MIMO_V2_6_FLASH = "mimo-v2.6-flash"
1757
+ }
1754
1758
  export declare enum ZAiModels {
1755
1759
  GLM_5_3 = "glm-5.3",
1756
1760
  GLM_5_3_FLASH = "glm-5.3-flash",
@@ -105,6 +105,7 @@ export var AIProviderName;
105
105
  AIProviderName["WAFER"] = "wafer";
106
106
  AIProviderName["WANDB_INFERENCE"] = "wandb-inference";
107
107
  AIProviderName["XAI"] = "xai";
108
+ AIProviderName["XIAOMI_MIMO"] = "xiaomi-mimo";
108
109
  AIProviderName["Z_AI"] = "z-ai";
109
110
  // ── END GENERATED(provider-members) ──
110
111
  AIProviderName["COHERE"] = "cohere";
@@ -2071,6 +2072,10 @@ export var XaiModels;
2071
2072
  XaiModels["GROK_2_VISION_LATEST"] = "grok-2-vision-latest";
2072
2073
  XaiModels["GROK_BETA"] = "grok-beta";
2073
2074
  })(XaiModels || (XaiModels = {}));
2075
+ export var XiaomiMimoModels;
2076
+ (function (XiaomiMimoModels) {
2077
+ XiaomiMimoModels["MIMO_V2_6_FLASH"] = "mimo-v2.6-flash";
2078
+ })(XiaomiMimoModels || (XiaomiMimoModels = {}));
2074
2079
  export var ZAiModels;
2075
2080
  (function (ZAiModels) {
2076
2081
  ZAiModels["GLM_5_3"] = "glm-5.3";
@@ -1,5 +1,5 @@
1
1
  import type { DecideCatalogJson, ProviderCatalogJson } from "../../types/index.js";
2
2
  export declare const CATALOG_JSON_ENTRIES: ProviderCatalogJson[];
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", "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", "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", "z-ai"];
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", "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", "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
5
  export declare const DECIDE_CATALOG_PROVIDER_IDS: readonly ["laya"];
@@ -84,6 +84,7 @@ import vultrInferenceJson from "./vultr-inference.json" with { type: "json" };
84
84
  import waferJson from "./wafer.json" with { type: "json" };
85
85
  import wandbInferenceJson from "./wandb-inference.json" with { type: "json" };
86
86
  import xaiJson from "./xai.json" with { type: "json" };
87
+ import xiaomiMimoJson from "./xiaomi-mimo.json" with { type: "json" };
87
88
  import zAiJson from "./z-ai.json" with { type: "json" };
88
89
  import { isDecideCatalogEntry, parseCatalogEntry } from "./schema.js";
89
90
  const asDecideEntry = (raw) => {
@@ -177,6 +178,7 @@ export const CATALOG_JSON_ENTRIES = [
177
178
  waferJson,
178
179
  wandbInferenceJson,
179
180
  xaiJson,
181
+ xiaomiMimoJson,
180
182
  zAiJson,
181
183
  ];
182
184
  export const CATALOG_PROVIDER_IDS = [
@@ -263,6 +265,7 @@ export const CATALOG_PROVIDER_IDS = [
263
265
  "wafer",
264
266
  "wandb-inference",
265
267
  "xai",
268
+ "xiaomi-mimo",
266
269
  "z-ai",
267
270
  ];
268
271
  export const DECIDE_CATALOG_JSON_ENTRIES = [layaJson].map(asDecideEntry);
@@ -0,0 +1,63 @@
1
+ {
2
+ "$schema": "./provider-catalog.schema.json",
3
+ "id": "xiaomi-mimo",
4
+ "displayName": "Xiaomi MiMo",
5
+ "aliases": [],
6
+ "tier": 2,
7
+ "wire": {
8
+ "baseURL": "https://api.xiaomimimo.com/v1",
9
+ "apiKeyFallbackEnvVars": ["MIMO_API_KEY"]
10
+ },
11
+ "models": {
12
+ "default": "mimo-v2.6-flash",
13
+ "fallbacks": ["mimo-v2.6-flash"],
14
+ "defaultContextWindow": 1048576,
15
+ "defaultMaxOutputTokens": 131072,
16
+ "catalog": {
17
+ "mimo-v2.6-flash": {
18
+ "contextWindow": 1048576,
19
+ "maxOutputTokens": 131072,
20
+ "vision": false,
21
+ "status": "production",
22
+ "description": "Xiaomi MiMo's usage-priced mimo-v2.6-flash, the vendor's named replacement for the retiring mimo-v2.5. The vendor's OpenClaw and OpenCode integration guides bind the integers 1048576 (context) and 131072 (output) to this exact id, and its API reference gives [1, 131072] as the required range for max_completion_tokens (visible plus reasoning tokens). The model table shows only the rounded labels 1M and 128K, so nothing was expanded from it. This pilot declares text generate/stream; tools, vision, structured output and thinking controls await separate evidence. Account/model access is unverified."
23
+ }
24
+ }
25
+ },
26
+ "capabilities": {
27
+ "text": true,
28
+ "streaming": true,
29
+ "tools": false,
30
+ "toolsWithStreaming": false,
31
+ "structuredOutput": false,
32
+ "structuredOutputWithTools": false,
33
+ "embeddings": false,
34
+ "thinking": false
35
+ },
36
+ "errorRules": [],
37
+ "setup": {
38
+ "url": "https://platform.xiaomimimo.com/#/console/api-keys",
39
+ "apiKeyFormat": null,
40
+ "billingPolicy": "promotional-credit",
41
+ "instructions": [
42
+ "1. Sign in to the Xiaomi MiMo platform console with a Xiaomi account and create a pay-as-you-go API key at https://platform.xiaomimimo.com/#/console/api-keys.",
43
+ "2. mimo-v2.6-flash is usage-priced against your account balance. The vendor's promotions page describes bonus credit, from Refer & Earn or new-user registration, that offsets pay-as-you-go API usage and is currently valid for 40 days. Eligibility and amounts vary by campaign; your own entitlement and balance are unverified, and this is not a free tier.",
44
+ "3. Set {apiKeyEnvVar}. The vendor's own examples name the variable MIMO_API_KEY, which is read as a fallback. The key is sent as Authorization: Bearer to https://api.xiaomimimo.com/v1/chat/completions (the vendor also accepts an api-key header, which this entry does not use).",
45
+ "4. Token Plan keys are a separate subscription product and are not covered by this entry. The declared scope is text generate and stream with mimo-v2.6-flash."
46
+ ],
47
+ "description": "Xiaomi MiMo text/stream pilot with a usage-priced mimo-v2.6-flash default. Bonus credit is a campaign-dependent promotion that offsets API usage; no account entitlement, roster, live inference or delivery is verified."
48
+ },
49
+ "evidence": {
50
+ "rosterVerified": {
51
+ "date": "2026-10-10",
52
+ "code": "PUBLIC_DOCS_ONLY_NOT_ROSTER_VERIFIED",
53
+ "method": "Public documentation GETs only: mimo.mi.com model.md, api/chat/openai-api.md, tokenplan/integration/openclaw.md and opencode.md, quick-start/faq/promotions.md, price/pay-as-you-go.md and updates/deprecate.md. No vendor roster, auth, billing or inference probe ran; this legacy field does not assert roster verification."
54
+ },
55
+ "billingProbe": {
56
+ "date": "2026-10-10",
57
+ "code": "PUBLIC_DOCS_ONLY_ACCOUNT_NOT_TESTED",
58
+ "method": "Public documentation only: the pay-as-you-go page states the API consumes the account balance by actual token usage, and the promotions page states bonus credit offsets API usage and is currently valid for 40 days with campaign-dependent amounts. No account was created and no credit amount or eligibility was checked."
59
+ },
60
+ "liveMatrix": null,
61
+ "addedInPR": "https://github.com/juspay/neurolink/pull/1977"
62
+ }
63
+ }
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Splits HTML content by semantic tags.
5
5
  */
6
+ import { removeHtmlMarkup } from "../../utils/htmlText.js";
6
7
  import { BaseChunker, DEFAULT_CHUNKER_CONFIG } from "./BaseChunker.js";
7
8
  /**
8
9
  * HTML Chunker
@@ -28,10 +29,12 @@ export class HTMLChunker extends BaseChunker {
28
29
  * Strip HTML tags from content
29
30
  */
30
31
  stripHtml(html) {
31
- return html
32
- .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "")
33
- .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "")
34
- .replace(/<[^>]+>/g, " ")
32
+ return removeHtmlMarkup(html, {
33
+ tagReplacement: " ",
34
+ commentReplacement: " ",
35
+ lineBreakEndTags: [],
36
+ lineBreakVoidTags: [],
37
+ })
35
38
  .replace(/\s+/g, " ")
36
39
  .trim();
37
40
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { randomUUID } from "crypto";
8
8
  import { decodeHtmlTextEntitiesOnce } from "../../utils/htmlEntities.js";
9
+ import { removeHtmlMarkup } from "../../utils/htmlText.js";
9
10
  /**
10
11
  * HTML-aware chunker implementation
11
12
  * Splits based on HTML structure (tags, elements)
@@ -171,16 +172,24 @@ export class HTMLChunker {
171
172
  * Extract plain text from HTML
172
173
  */
173
174
  extractText(html) {
174
- return decodeHtmlTextEntitiesOnce(html
175
- // Remove script and style elements
176
- .replace(/<script[\s\S]*?<\/script>/gi, "")
177
- .replace(/<style[\s\S]*?<\/style>/gi, "")
178
- // Remove HTML comments
179
- .replace(/<!--[\s\S]*?-->/g, "")
180
- // Replace block elements with newlines
181
- .replace(/<\/(p|div|br|h[1-6]|li|tr)>/gi, "\n")
182
- // Remove remaining tags
183
- .replace(/<[^>]+>/g, ""), { nbsp: " ", amp: "&", lt: "<", gt: ">", quot: '"', "#039": "'" })
175
+ return decodeHtmlTextEntitiesOnce(removeHtmlMarkup(html, {
176
+ tagReplacement: "",
177
+ commentReplacement: "",
178
+ lineBreakEndTags: [
179
+ "p",
180
+ "div",
181
+ "br",
182
+ "h1",
183
+ "h2",
184
+ "h3",
185
+ "h4",
186
+ "h5",
187
+ "h6",
188
+ "li",
189
+ "tr",
190
+ ],
191
+ lineBreakVoidTags: [],
192
+ }), { nbsp: " ", amp: "&", lt: "<", gt: ">", quot: '"', "#039": "'" })
184
193
  .replace(/\s+/g, " ")
185
194
  .trim();
186
195
  }
@@ -28,6 +28,7 @@ import { readFile } from "fs/promises";
28
28
  import { basename, extname } from "path";
29
29
  import { logger } from "../../utils/logger.js";
30
30
  import { decodeHtmlTextEntitiesOnce } from "../../utils/htmlEntities.js";
31
+ import { removeHtmlMarkup } from "../../utils/htmlText.js";
31
32
  import { CSVProcessor, stripBom, stripMetadataLine, splitCsvLines, dedupeColumnNames, sanitizeColumnName, } from "../../utils/csvProcessor.js";
32
33
  import { splitCSVFields } from "../../utils/csvUtils.js";
33
34
  import { MDocument } from "./MDocument.js";
@@ -429,17 +430,25 @@ export class WebLoader {
429
430
  * Convert HTML to plain text
430
431
  */
431
432
  htmlToText(html) {
432
- return decodeHtmlTextEntitiesOnce(html
433
- // Remove script and style elements
434
- .replace(/<script[\s\S]*?<\/script>/gi, "")
435
- .replace(/<style[\s\S]*?<\/style>/gi, "")
436
- // Remove HTML comments
437
- .replace(/<!--[\s\S]*?-->/g, "")
438
- // Replace common block elements with newlines
439
- .replace(/<\/(p|div|h[1-6]|br|li|tr|blockquote)>/gi, "\n")
440
- .replace(/<(br|hr)\s*\/?>/gi, "\n")
441
- // Remove remaining tags
442
- .replace(/<[^>]+>/g, ""), {
433
+ return decodeHtmlTextEntitiesOnce(removeHtmlMarkup(html, {
434
+ tagReplacement: "",
435
+ commentReplacement: "",
436
+ lineBreakEndTags: [
437
+ "p",
438
+ "div",
439
+ "h1",
440
+ "h2",
441
+ "h3",
442
+ "h4",
443
+ "h5",
444
+ "h6",
445
+ "br",
446
+ "li",
447
+ "tr",
448
+ "blockquote",
449
+ ],
450
+ lineBreakVoidTags: ["br", "hr"],
451
+ }), {
443
452
  nbsp: " ",
444
453
  amp: "&",
445
454
  lt: "<",
@@ -1,2 +1,2 @@
1
- export type CatalogProviderName = "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" | "laya" | "lemonfox-ai" | "lilac" | "llmtech" | "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" | "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" | "z-ai";
2
- export type CatalogCredentialKey = "a2agent" | "abliteration" | "aboveDev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "apiRoute" | "arcee" | "atlasCloud" | "avianIo" | "baiduQianfan" | "baseten" | "beeHeossi" | "byteplusModelark" | "bytez" | "cerebras" | "charmHyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherlessAi" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetznerInference" | "huggingFace" | "inceptionLabs" | "inceptron" | "inco" | "inferenceNet" | "ioIntelligence" | "koscompute" | "laya" | "lemonfoxAi" | "lilac" | "llmtech" | "mancer" | "metaModelApi" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshotAi" | "morph" | "naverClovaStudio" | "nebius" | "neuralwatt" | "novita" | "ovhcloud" | "parasail" | "paretoInference" | "perplexity" | "poolside" | "primeIntellect" | "reka" | "sakanaAi" | "sambanova" | "sarvam" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "synthetic" | "telnyx" | "thinkingMachines" | "tinfoil" | "together" | "umansAi" | "upstage" | "veniceAi" | "vispark" | "vultrInference" | "wafer" | "wandbInference" | "xai" | "zAi";
1
+ export type CatalogProviderName = "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" | "laya" | "lemonfox-ai" | "lilac" | "llmtech" | "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" | "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";
2
+ export type CatalogCredentialKey = "a2agent" | "abliteration" | "aboveDev" | "ai21" | "aiand" | "aihubmix" | "aionlabs" | "ambient" | "apiRoute" | "arcee" | "atlasCloud" | "avianIo" | "baiduQianfan" | "baseten" | "beeHeossi" | "byteplusModelark" | "bytez" | "cerebras" | "charmHyper" | "chutes" | "cloudflare" | "crusoe" | "dashscope" | "deepinfra" | "deepseek" | "empiriolabs" | "featherlessAi" | "fireworks" | "friendli" | "gmicloud" | "gradientai" | "groq" | "hetznerInference" | "huggingFace" | "inceptionLabs" | "inceptron" | "inco" | "inferenceNet" | "ioIntelligence" | "koscompute" | "laya" | "lemonfoxAi" | "lilac" | "llmtech" | "mancer" | "metaModelApi" | "minimax" | "mistral" | "moark" | "modelscope" | "moonshotAi" | "morph" | "naverClovaStudio" | "nebius" | "neuralwatt" | "novita" | "ovhcloud" | "parasail" | "paretoInference" | "perplexity" | "poolside" | "primeIntellect" | "reka" | "sakanaAi" | "sambanova" | "sarvam" | "scaleway" | "siliconflow" | "stackit" | "stepfun" | "subconscious" | "synthetic" | "telnyx" | "thinkingMachines" | "tinfoil" | "together" | "umansAi" | "upstage" | "veniceAi" | "vispark" | "vultrInference" | "wafer" | "wandbInference" | "xai" | "xiaomiMimo" | "zAi";
@@ -529,6 +529,10 @@ export type NeurolinkCredentials = {
529
529
  apiKey?: string;
530
530
  baseURL?: string;
531
531
  };
532
+ xiaomiMimo?: {
533
+ apiKey?: string;
534
+ baseURL?: string;
535
+ };
532
536
  zAi?: {
533
537
  apiKey?: string;
534
538
  baseURL?: string;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Single-pass markup removal for turning HTML into indexable text.
3
+ *
4
+ * This is text conversion, not output sanitization. The result is plain text
5
+ * for retrieval and chunking; it is not meant to be written back into a page,
6
+ * and the caller's entity decoding can legitimately turn `&lt;b&gt;` into
7
+ * `<b>`.
8
+ *
9
+ * Why one pass: the previous implementation chained several global regex
10
+ * replacements (script, style, comment, block end, generic tag). Each removal
11
+ * could join the text on either side of it into a new tag for the next
12
+ * replacement (`<scr<!---->ipt>`), and the lazy `[\s\S]*?` bodies rescanned
13
+ * the rest of the input once per unclosed `<script`, which is quadratic.
14
+ * Here the input is read left to right exactly once. Every character of the
15
+ * output is copied from an unremoved stretch of the input, a removed stretch
16
+ * is never re-read, and every search either consumes what it scans or is
17
+ * answered in constant time from an index computed up front, so the work is
18
+ * linear in the input length.
19
+ *
20
+ * Invariant: the output never matches `<[^>]+>`. A `<` survives only when no
21
+ * `>` follows it in the input, or when the next character is the `>` itself
22
+ * (`<>`, which is not a tag). A removed span therefore cannot leave behind,
23
+ * or assemble, a tag.
24
+ */
25
+ /**
26
+ * Remove HTML markup from `html` in one linear pass.
27
+ *
28
+ * - `<script>` / `<style>` elements are removed with their bodies. The end
29
+ * tag may carry whitespace or attributes (`</script >`). An element with
30
+ * no usable end tag loses only its start tag, as before.
31
+ * - `<!-- ... -->` is replaced with `commentReplacement`. An unterminated
32
+ * comment is treated like any other tag and ends at the next `>`.
33
+ * - A closing tag named in `lineBreakEndTags` and a void tag named in
34
+ * `lineBreakVoidTags` (`<br>`, `<hr />`) become a newline.
35
+ * - Any other `<...>` becomes `tagReplacement`. A `<` with no `>` after it,
36
+ * and the empty `<>`, are literal text and are kept.
37
+ *
38
+ * Tag-name lists are lower case.
39
+ */
40
+ export declare function removeHtmlMarkup(html: string, options: {
41
+ readonly tagReplacement: string;
42
+ readonly commentReplacement: string;
43
+ readonly lineBreakEndTags: readonly string[];
44
+ readonly lineBreakVoidTags: readonly string[];
45
+ }): string;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Single-pass markup removal for turning HTML into indexable text.
3
+ *
4
+ * This is text conversion, not output sanitization. The result is plain text
5
+ * for retrieval and chunking; it is not meant to be written back into a page,
6
+ * and the caller's entity decoding can legitimately turn `&lt;b&gt;` into
7
+ * `<b>`.
8
+ *
9
+ * Why one pass: the previous implementation chained several global regex
10
+ * replacements (script, style, comment, block end, generic tag). Each removal
11
+ * could join the text on either side of it into a new tag for the next
12
+ * replacement (`<scr<!---->ipt>`), and the lazy `[\s\S]*?` bodies rescanned
13
+ * the rest of the input once per unclosed `<script`, which is quadratic.
14
+ * Here the input is read left to right exactly once. Every character of the
15
+ * output is copied from an unremoved stretch of the input, a removed stretch
16
+ * is never re-read, and every search either consumes what it scans or is
17
+ * answered in constant time from an index computed up front, so the work is
18
+ * linear in the input length.
19
+ *
20
+ * Invariant: the output never matches `<[^>]+>`. A `<` survives only when no
21
+ * `>` follows it in the input, or when the next character is the `>` itself
22
+ * (`<>`, which is not a tag). A removed span therefore cannot leave behind,
23
+ * or assemble, a tag.
24
+ */
25
+ const SLASH = 0x2f;
26
+ const GREATER_THAN = 0x3e;
27
+ const WHITESPACE = /\s/;
28
+ function isAsciiAlphanumeric(code) {
29
+ return ((code >= 0x30 && code <= 0x39) ||
30
+ (code >= 0x41 && code <= 0x5a) ||
31
+ (code >= 0x61 && code <= 0x7a));
32
+ }
33
+ // The characters after which an HTML tokenizer ends a tag name.
34
+ function endsTagName(code) {
35
+ return (code === 0x09 ||
36
+ code === 0x0a ||
37
+ code === 0x0c ||
38
+ code === 0x0d ||
39
+ code === 0x20 ||
40
+ code === SLASH ||
41
+ code === GREATER_THAN);
42
+ }
43
+ function scanTagName(html, from) {
44
+ let end = from;
45
+ while (end < html.length && isAsciiAlphanumeric(html.charCodeAt(end))) {
46
+ end++;
47
+ }
48
+ return end;
49
+ }
50
+ /**
51
+ * Index of the next `</name` that is followed by whitespace, `/` or `>`
52
+ * (so `</script >` and `</SCRIPT foo>` count), or -1. Case-insensitive.
53
+ */
54
+ function findEndTag(html, name, from) {
55
+ let at = html.indexOf("</", from);
56
+ while (at !== -1) {
57
+ const nameEnd = at + 2 + name.length;
58
+ if (html.slice(at + 2, nameEnd).toLowerCase() === name &&
59
+ endsTagName(html.charCodeAt(nameEnd))) {
60
+ return at;
61
+ }
62
+ at = html.indexOf("</", at + 2);
63
+ }
64
+ return -1;
65
+ }
66
+ /**
67
+ * Remove HTML markup from `html` in one linear pass.
68
+ *
69
+ * - `<script>` / `<style>` elements are removed with their bodies. The end
70
+ * tag may carry whitespace or attributes (`</script >`). An element with
71
+ * no usable end tag loses only its start tag, as before.
72
+ * - `<!-- ... -->` is replaced with `commentReplacement`. An unterminated
73
+ * comment is treated like any other tag and ends at the next `>`.
74
+ * - A closing tag named in `lineBreakEndTags` and a void tag named in
75
+ * `lineBreakVoidTags` (`<br>`, `<hr />`) become a newline.
76
+ * - Any other `<...>` becomes `tagReplacement`. A `<` with no `>` after it,
77
+ * and the empty `<>`, are literal text and are kept.
78
+ *
79
+ * Tag-name lists are lower case.
80
+ */
81
+ export function removeHtmlMarkup(html, options) {
82
+ const { tagReplacement, commentReplacement } = options;
83
+ const lineBreakEndTags = new Set(options.lineBreakEndTags);
84
+ const lineBreakVoidTags = new Set(options.lineBreakVoidTags);
85
+ // Answers "is there any `>` / `-->` after here" without a scan. Once an
86
+ // element finds no usable end tag, no later element of that name can.
87
+ const lastGreaterThan = html.lastIndexOf(">");
88
+ const lastCommentEnd = html.lastIndexOf("-->");
89
+ const withoutEndTag = new Set();
90
+ const markupAt = (at) => {
91
+ if (html.startsWith("<!--", at) && lastCommentEnd >= at + 4) {
92
+ return {
93
+ end: html.indexOf("-->", at + 4) + 3,
94
+ replacement: commentReplacement,
95
+ };
96
+ }
97
+ const closing = html.charCodeAt(at + 1) === SLASH;
98
+ const nameStart = closing ? at + 2 : at + 1;
99
+ const nameEnd = scanTagName(html, nameStart);
100
+ const name = html.slice(nameStart, nameEnd).toLowerCase();
101
+ if (!closing &&
102
+ (name === "script" || name === "style") &&
103
+ endsTagName(html.charCodeAt(nameEnd)) &&
104
+ !withoutEndTag.has(name)) {
105
+ const startTagEnd = html.indexOf(">", nameEnd);
106
+ const endTag = findEndTag(html, name, startTagEnd + 1);
107
+ if (endTag !== -1 && endTag < lastGreaterThan) {
108
+ return { end: html.indexOf(">", endTag) + 1, replacement: "" };
109
+ }
110
+ withoutEndTag.add(name);
111
+ }
112
+ if (closing &&
113
+ html.charCodeAt(nameEnd) === GREATER_THAN &&
114
+ lineBreakEndTags.has(name)) {
115
+ return { end: nameEnd + 1, replacement: "\n" };
116
+ }
117
+ if (!closing && lineBreakVoidTags.has(name)) {
118
+ let probe = nameEnd;
119
+ while (WHITESPACE.test(html.charAt(probe))) {
120
+ probe++;
121
+ }
122
+ if (html.charCodeAt(probe) === SLASH) {
123
+ probe++;
124
+ }
125
+ if (html.charCodeAt(probe) === GREATER_THAN) {
126
+ return { end: probe + 1, replacement: "\n" };
127
+ }
128
+ }
129
+ const end = html.indexOf(">", at + 1);
130
+ return end === at + 1
131
+ ? null
132
+ : { end: end + 1, replacement: tagReplacement };
133
+ };
134
+ let output = "";
135
+ let copied = 0;
136
+ let open = html.indexOf("<");
137
+ while (open !== -1 && open < lastGreaterThan) {
138
+ const markup = markupAt(open);
139
+ if (markup === null) {
140
+ open = html.indexOf("<", open + 1);
141
+ continue;
142
+ }
143
+ output += html.slice(copied, open) + markup.replacement;
144
+ copied = markup.end;
145
+ open = html.indexOf("<", copied);
146
+ }
147
+ return output + html.slice(copied);
148
+ }
@@ -9775,6 +9775,15 @@
9775
9775
  {"objectID":"1ad1b971263952542f0624327b54b7599081e5d8de4d5afbcc62c4ca39ec6e41","title":"Provenance and remaining gates","url":"/docs/provider-integration/pilots/aihubmix-source-pilot#provenance-and-remaining-gates","content":"The legacy evidence.rosterVerified slot contains the explicit\nPUBLIC_DOCS_ONLY_NOT_ROSTER_VERIFIED marker and documentation methods. It has\nno HTTP status, auth probe or billing probe. liveMatrix is null and the PR\nfield is the canonical introduction PR URL\n(https://github.com/juspay/neurolink/pull/1966), set after the PR existed; it was\nPENDING_PR while the work was local. Q04 source admission accepts either the\nmarker or a canonical introduction PR URL. Its source-stage report keeps roster, live, installed-package and delivery\nclaims separate and gives no provider-count credit. Structural parsing or\nsource-stage admission does not certify current account/API availability.\n\nThe Q04 successor starts from 08c3c4e4e2881874fdf0e453923a3507b759532d,\nwhich descends from the checked live release\n8c78cd172a8b2036b75132488f9d24548f2a1e88. Its public fixture is byte-identical\nto the original source freeze. The profile's only semantic change is the PR\nmarker required by Q04. Shared schema, runtime, helpers and original source\nreceipts are preserved. Actual Q04 source admission passed and reports zero\nprovider-count, roster, live, installed-package and publication credit.\n\nCanonical catalog generation affects the index, provider union, provider/model\nenum regions and generated credential region. The standalone frozen install,\ncanonical generation and fixed point, fresh SDK/CLI/browser build, normal lint,\nstrict source/tools/CI types and all 16 guarded public cases passed. Source\noutside the generated regions is preserved. A command-delimiter failure before\nQ04 admission was retained and corrected by invoking the same production\nvalidator directly; no source or fixture repair was required.\n\nIntegrated source/build/required suites, normal hooks and commit, any separately\nassigned production sensitivity controls and clean installed-consumer acceptance\nremain pending. Accounts, funding, provider inference, external delivery and\ntarget credit are separate.","hierarchy":{"lvl0":"Provider Integration","lvl1":"AIHubMix text and streaming pilot","lvl2":"Provenance and remaining gates","lvl3":""}},
9776
9776
  {"objectID":"d7aebf13c33ee0a84a4651090aa0b463a2225173acb7dd93f2456adb58d06729","title":"NAVER CLOVA Studio source pilot","url":"/docs/provider-integration/pilots/naver-clova-source-pilot","content":"NAVER CLOVA Studio source pilot\n\nThis source entry uses the OpenAI-compatible chat route at\nhttps://clovastudio.stream.ntruss.com/v1/openai/chat/completions, a CLOVA\nStudio-issued Bearer API key, and the documented HCX-005 model. No actual\naccount, key, model roster, inference or payment operation was performed.\n\nThe vendor Chat Completions v3 reference\nstates that HCX-005 input and output together cannot exceed 128,000 tokens and\nthat requested output cannot exceed 4,096 tokens. The\nOpenAI-compatible guide\nuses that same exact model ID. These are explicit per-model constraints, not\nconversion of an approximate context label.\n\nThe CLOVA Studio prerequisites\nstate that model, purpose and token usage incur fees. The official\nnew-signup credit policy, retrieved on\nOctober 9, 2026, offers 100,000 KRW of conditional discount credit after\npayment-method registration and an application. Issued credit lasts three\nmonths. Eligibility follows the policy at application time; recipients of\nother discounts cannot apply. Free/trial products, Marketplace purchases and\nNAVER WORKS are excluded.\n\nThe bounded promotional-credit billing value records that offer without\nreclassifying it as a free tier or a card-only requirement. It preserves all\nthree existing billing values. It does not prove that a current account is\neligible, subscribed, funded, or able to call the API. Numeric model pricing is\nomitted rather than inventing a KRW-to-USD conversion.\n\nThe shim supports tool choice auto, none and a named tool object, but not\nrequired. The public SDK can express required, so the provider-scoped\nrejectRequiredToolChoice guard rejects that intent on a nonempty-tools\nrequest before sending. It preserves the accepted choices and leaves other\nproviders unchanged. It never changes required into auto.\n\nparallel_tool_calls: false is a documented endpoint limitation, but no\nordinary SDK generate/stream input channel for it was established; this change\nadds no unreachable parameter DSL. The shim ignores JSON-schema name and\nstrict, so this initial entry does not advertise native strict-schema or\nunproved tools-plus-schema support. Other models, native v3 payloads/events,\nembeddings, images, and SDK effort controls remain outside this pilot.\n\nThe dedicated public contract covers SDK and built CLI defaults and routes,\nunique user prompts and backend answers, effective stream modes, exact output\nbudgets, invalid-model rejection, tool nonce round trips for accepted choices,\nand intentional rejection of required. A paired existing-provider control\nmust still honor required. These are owned-endpoint proofs, not vendor live\navailability.\n\nDOCS_ONLY_ROSTER_NOT_OBSERVED labels the legacy required evidence field as\nsource documentation only. addedInPR names the introduction PR\n(https://github.com/juspay/neurolink/pull/1966); it is not proof of merge. Source, contract, installed package, actual account/live and\nreview/delivery states stay separate. Generated regions and heavy validation\nremain coordinator-owned after this source handoff.","hierarchy":{"lvl0":"Provider Integration","lvl1":"NAVER CLOVA Studio source pilot","lvl2":"","lvl3":""}},
9777
9777
  {"objectID":"9d81c7d704b297c3886df857adf195cfb90daed24c49f8e64a69db25064c84c2","title":"NAVER CLOVA Studio source pilot","url":"/docs/provider-integration/pilots/naver-clova-source-pilot#naver-clova-studio-source-pilot","content":"This source entry uses the OpenAI-compatible chat route at\nhttps://clovastudio.stream.ntruss.com/v1/openai/chat/completions, a CLOVA\nStudio-issued Bearer API key, and the documented HCX-005 model. No actual\naccount, key, model roster, inference or payment operation was performed.\n\nThe vendor Chat Completions v3 reference\nstates that HCX-005 input and output together cannot exceed 128,000 tokens and\nthat requested output cannot exceed 4,096 tokens. The\nOpenAI-compatible guide\nuses that same exact model ID. These are explicit per-model constraints, not\nconversion of an approximate context label.\n\nThe CLOVA Studio prerequisites\nstate that model, purpose and token usage incur fees. The official\nnew-signup credit policy, retrieved on\nOctober 9, 2026, offers 100,000 KRW of conditional discount credit after\npayment-method registration and an application. Issued credit lasts three\nmonths. Eligibility follows the policy at application time; recipients of\nother discounts cannot apply. Free/trial products, Marketplace purchases and\nNAVER WORKS are excluded.\n\nThe bounded promotional-credit billing value records that offer without\nreclassifying it as a free tier or a card-only requirement. It preserves all\nthree existing billing values. It does not prove that a current account is\neligible, subscribed, funded, or able to call the API. Numeric model pricing is\nomitted rather than inventing a KRW-to-USD conversion.\n\nThe shim supports tool choice auto, none and a named tool object, but not\nrequired. The public SDK can express required, so the provider-scoped\nrejectRequiredToolChoice guard rejects that intent on a nonempty-tools\nrequest before sending. It preserves the accepted choices and leaves other\nproviders unchanged. It never changes required into auto.\n\nparallel_tool_calls: false is a documented endpoint limitation, but no\nordinary SDK generate/stream input channel for it was established; this change\nadds no unreachable parameter DSL.","hierarchy":{"lvl0":"Provider Integration","lvl1":"NAVER CLOVA Studio source pilot","lvl2":"NAVER CLOVA Studio source pilot","lvl3":""}},
9778
+ {"objectID":"a2a1b3f3eace26df9887f72342446b632c131c811f5b8d9a34eea9f58c1c1ae8","title":"Xiaomi MiMo text and streaming pilot","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot","content":"Xiaomi MiMo text and streaming pilot\n\nThis local source pilot selects Xiaomi MiMo's usage-priced mimo-v2.6-flash\nmodel through https://api.xiaomimimo.com/v1/chat/completions. Its declared\nscope is text generation and streaming. Source preparation, public contract\nacceptance, installed-package acceptance, current account/API availability and\ndelivery are separate gates. This draft gives no live, delivered-provider or\nprovider-target credit.\n\nExact source facts\n\nAll pages below are on the vendor's own documentation host, mimo.mi.com, and\nwere retrieved on 2026-10-10 (hashes at the end).\nContext and output integers. The model table (model.md) shows only the\n rounded labels 1M and 128K, so nothing was expanded from it. The integers\n come from two vendor-authored client configurations that bind them to the\n exact id mimo-v2.6-flash: the OpenClaw guide (\"contextWindow\": 1048576,\n \"maxTokens\": 131072) and the OpenCode guide (\"limit\": { \"context\": 1048576,\n\"output\": 131072 }). Both name https://api.xiaomimimo.com/v1 as the\n pay-as-you-go base URL. The two pages use different client schemas and agree.\n The API reference independently gives [1, 131072] as the required range of\n max_completion_tokens and a default of 131072 for mimo-v2.6-flash; that\n parameter covers visible plus reasoning tokens. No first-party prose or spec\n table states the context integer; the OpenClaw and OpenCode snippets are the\n only bindings, and 1048576 equals 1024 * 1024.\nRoute and auth. The API reference documents\n https://api.xiaomimimo.com/v1/chat/completions and two alternative headers,\n api-key: $MIMO_API_KEY and Authorization: Bearer $MIMO_API_KEY. This entry\n uses the Bearer form.\nLifecycle. mimo-v2.6-flash is current and is the named replacement for\n the retiring mimo-v2.5; it is not itself on a retirement schedule. The\n deprecation page gives mimo-v2.5 and mimo-v2.5-pro a 10:00 (UTC+8)\n switch on 2026-10-14 in its table and 18:00 (UTC+8) in a note on the same page.\n The discrepancy is preserved here; no exact switch instant is certified.\n\nBilling\n\nThe API is usage-priced: the pay-as-you-go page says it \"consumes the account\nbalance based on the actual Token usage\". The promotions page describes bonus\ncredit, obtained through Refer & Earn or new-user registration, that \"can be\nused to offset pay-as-you-go API usage\" and is \"currently valid for 40 days\".\nIt also says eligibility, credit amounts and payout rules vary by campaign. The\ncatalog therefore uses promotional-credit, following the\nNAVER CLOVA Studio profile (src/lib/providers/catalog/naver-clova-studio.json).\nThis is not a free tier. Whether a payment method or identity verification is\nrequired to obtain a working key was not established for the real-time API, the\ncredit amount and eligibility were not checked on an account, and prices are\nnot recorded (the pay-as-you-go page lists CNY and USD and they were not\nconverted).\n\nScope that is deliberately not declared\n\nThe capability booleans limit the pilot's declared scope; false is not\nevidence that the vendor lacks a feature. Tools, vision, structured output and\nSDK thinking controls are not declared because none has been probed. The\ndeprecation page notes that in thinking mode mimo-v2.6-flash ignores custom\ntemperature and top_p, so a thinking mode exists; this entry exposes no\ncontrol for it. Token Plan keys and base URLs are a separate subscription\nproduct and are out of scope.\n\nKnown gaps the local contract cannot close\nOutput-budget field name. The vendor's request reference lists only\n max_completion_tokens. The SDK's shared chat-completions base sends\n max_tokens, and the suite pins that observed field. Whether the vendor\n honors max_tokens, ignores it (leaving the model's 131072 default) or\n rejects it is unverified. A catalog entry cannot rename the field; that would\n need a provider hook or a new catalog quirk, which is an operator decision.\nStreaming usage option. A streaming request carries\n stream_options: { \"include_usage\": true }. The vendor's reference documents\n stream but not stream_options. The suite does not observe how the vendor\n responds to the extra field.\nEnvironment variable. The vendor's own examples name MIMO_API_KEY. The\n catalog's derived primary variable is XIAOMI_MIMO_API_KEY, and MIMO_API_KEY\n is read as a fallback through wire.apiKeyFallbackEnvVars, the mechanism the\n Sakana AI entry also uses. Removing the fallback is a one-line change if the\n maintainers prefer the pure convention.\nCeilings are source-bound only. The shared wire builder fits max_tokens\n only to runtime-discovered ceilings, so the catalog's contextWindow and\n maxOutputTokens never appear on the wire, and no public CLI command or SDK\n export was found that reads them back. The owned endpoint cannot observe\n them; they rest on the cited source bindings alone.\n\nLocal public contract\n\ntest/continuous-test-suite-xiaomi-mimo-pilot.ts runs 12 cases through the\nbuilt public SDK and built CLI, with opt","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"","lvl3":""}},
9779
+ {"objectID":"e6f0fe17d0287ca08aa0b72ec5e47ce773e8c8baf82de0948002899e1fec6fe6","title":"Xiaomi MiMo text and streaming pilot","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#xiaomi-mimo-text-and-streaming-pilot","content":"This local source pilot selects Xiaomi MiMo's usage-priced mimo-v2.6-flash\nmodel through https://api.xiaomimimo.com/v1/chat/completions. Its declared\nscope is text generation and streaming. Source preparation, public contract\nacceptance, installed-package acceptance, current account/API availability and\ndelivery are separate gates. This draft gives no live, delivered-provider or\nprovider-target credit.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Xiaomi MiMo text and streaming pilot","lvl3":""}},
9780
+ {"objectID":"cab460c84439c33f9cb5dcc1bd16e9235de01e05431b2d30b17098ec5e025e2a","title":"Exact source facts","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#exact-source-facts","content":"All pages below are on the vendor's own documentation host, mimo.mi.com, and\nwere retrieved on 2026-10-10 (hashes at the end).\nContext and output integers. The model table (model.md) shows only the\n rounded labels 1M and 128K, so nothing was expanded from it. The integers\n come from two vendor-authored client configurations that bind them to the\n exact id mimo-v2.6-flash: the OpenClaw guide (\"contextWindow\": 1048576,\n \"maxTokens\": 131072) and the OpenCode guide (\"limit\": { \"context\": 1048576,\n\"output\": 131072 }). Both name https://api.xiaomimimo.com/v1 as the\n pay-as-you-go base URL. The two pages use different client schemas and agree.\n The API reference independently gives [1, 131072] as the required range of\n max_completion_tokens and a default of 131072 for mimo-v2.6-flash; that\n parameter covers visible plus reasoning tokens. No first-party prose or spec\n table states the context integer; the OpenClaw and OpenCode snippets are the\n only bindings, and 1048576 equals 1024 * 1024.\nRoute and auth. The API reference documents\n https://api.xiaomimimo.com/v1/chat/completions and two alternative headers,\n api-key: $MIMO_API_KEY and Authorization: Bearer $MIMO_API_KEY. This entry\n uses the Bearer form.\nLifecycle. mimo-v2.6-flash is current and is the named replacement for\n the retiring mimo-v2.5; it is not itself on a retirement schedule. The\n deprecation page gives mimo-v2.5 and mimo-v2.5-pro a 10:00 (UTC+8)\n switch on 2026-10-14 in its table and 18:00 (UTC+8) in a note on the same page.\n The discrepancy is preserved here; no exact switch instant is certified.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Exact source facts","lvl3":""}},
9781
+ {"objectID":"99997193af8d5761fed491df17f7f0a49a60df721d802908183c25373c08fbb8","title":"Billing","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#billing","content":"The API is usage-priced: the pay-as-you-go page says it \"consumes the account\nbalance based on the actual Token usage\". The promotions page describes bonus\ncredit, obtained through Refer & Earn or new-user registration, that \"can be\nused to offset pay-as-you-go API usage\" and is \"currently valid for 40 days\".\nIt also says eligibility, credit amounts and payout rules vary by campaign. The\ncatalog therefore uses promotional-credit, following the\nNAVER CLOVA Studio profile (src/lib/providers/catalog/naver-clova-studio.json).\nThis is not a free tier. Whether a payment method or identity verification is\nrequired to obtain a working key was not established for the real-time API, the\ncredit amount and eligibility were not checked on an account, and prices are\nnot recorded (the pay-as-you-go page lists CNY and USD and they were not\nconverted).","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Billing","lvl3":""}},
9782
+ {"objectID":"a5c82cf3d766314a9407f86e5f8129fbe40aad5857bc783b97c61d4f8bfcb3c2","title":"Scope that is deliberately not declared","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#scope-that-is-deliberately-not-declared","content":"The capability booleans limit the pilot's declared scope; false is not\nevidence that the vendor lacks a feature. Tools, vision, structured output and\nSDK thinking controls are not declared because none has been probed. The\ndeprecation page notes that in thinking mode mimo-v2.6-flash ignores custom\ntemperature and top_p, so a thinking mode exists; this entry exposes no\ncontrol for it. Token Plan keys and base URLs are a separate subscription\nproduct and are out of scope.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Scope that is deliberately not declared","lvl3":""}},
9783
+ {"objectID":"b1920fcbf8becee8dcb34965fc790f5ca16fe64514dc33885cc42c21c8f46209","title":"Known gaps the local contract cannot close","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#known-gaps-the-local-contract-cannot-close","content":"Output-budget field name. The vendor's request reference lists only\n max_completion_tokens. The SDK's shared chat-completions base sends\n max_tokens, and the suite pins that observed field. Whether the vendor\n honors max_tokens, ignores it (leaving the model's 131072 default) or\n rejects it is unverified. A catalog entry cannot rename the field; that would\n need a provider hook or a new catalog quirk, which is an operator decision.\nStreaming usage option. A streaming request carries\n stream_options: { \"include_usage\": true }. The vendor's reference documents\n stream but not stream_options. The suite does not observe how the vendor\n responds to the extra field.\nEnvironment variable. The vendor's own examples name MIMO_API_KEY. The\n catalog's derived primary variable is XIAOMI_MIMO_API_KEY, and MIMO_API_KEY\n is read as a fallback through wire.apiKeyFallbackEnvVars, the mechanism the\n Sakana AI entry also uses. Removing the fallback is a one-line change if the\n maintainers prefer the pure convention.\nCeilings are source-bound only. The shared wire builder fits max_tokens\n only to runtime-discovered ceilings, so the catalog's contextWindow and\n maxOutputTokens never appear on the wire, and no public CLI command or SDK\n export was found that reads them back. The owned endpoint cannot observe\n them; they rest on the cited source bindings alone.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Known gaps the local contract cannot close","lvl3":""}},
9784
+ {"objectID":"8d988c1b9c9f17a568ba163469be0dcabd60611fd93e92101cc9ea8f7c27a857","title":"Local public contract","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#local-public-contract","content":"test/continuous-test-suite-xiaomi-mimo-pilot.ts runs 12 cases through the\nbuilt public SDK and built CLI, with optional --package-root support for a\nlater installed consumer:\n\n| Cases | Count | Required observation |\n| ------------------------------------------- | ----: | --------------------------------------------------------------------------------------------------------------------------------------- |\n| SDK/CLI generate/stream default | 4 | Actual catalog default, route, Bearer header, unique user prompt, max_tokens: 32, stream mode, no tools/tool choice, and fresh answer |\n| SDK/CLI generate/stream invalid explicit id | 4 | All wire preconditions pass before HTTP 400 rejection; no fallback or successful reply |\n| SDK and CLI generate, only MIMO_API_KEY | 2 | The vendor-documented variable alone reaches the wire as the Bearer credential |\n| SDK generate, both variables set | 1 | XIAOMI_MIMO_API_KEY wins; the fallback's value never reaches the wire |\n| SDK generate, no key | 1 | Refusal before any request, naming XIAOMI_MIMO_API_KEY |\n\nThe fixture strips real credentials before imports, uses temporary homes and\nsets the provider route to its owned random-port listener. Public\nMODEL_CONFIG_URL uses that same listener's HEAD /health and\nGET /api/v1/models. A schema-valid empty registry keeps the provider's actual\ncatalog default in control. Positive metadata initialization requires the exact\nordered request pair; explicit CLI model paths require no metadata request.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Local public contract","lvl3":""}},
9785
+ {"objectID":"ffc6f8c30e69161df522917d0ff982175110507d3cfcc174b2e5144f23773699","title":"Provenance and remaining gates","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#provenance-and-remaining-gates","content":"The legacy evidence.rosterVerified slot carries the explicit\nPUBLIC_DOCS_ONLY_NOT_ROSTER_VERIFIED marker and documentation methods; it has\nno HTTP status, auth probe or roster. billingProbe is likewise documentation\nonly. liveMatrix is null. addedInPR holds the canonical introduction PR URL,\nhttps://github.com/juspay/neurolink/pull/1977; it was PENDING_PR in the\nfirst commit, before the PR existed. A URL alone is\nnot proof of merge, and source-stage admission gives no provider-count, roster,\nlive, installed-package or publication credit.\n\nAuthenticated roster, account creation, funding, live inference, installed\nconsumer acceptance and delivery are separate and not done here.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Provenance and remaining gates","lvl3":""}},
9786
+ {"objectID":"af08e2fec005ca76c90a90418848d429bab413b64e1b8a2b1501bcbcb7f58827","title":"Source receipts","url":"/docs/provider-integration/pilots/xiaomi-mimo-source-pilot#source-receipts","content":"Retrieved with a plain GET on 2026-10-10; the SHA-256 is of each response body.\n\n| First-party source | Body SHA256 |\n| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |\n| Model table | 26a7f23c7a12410e10cc75610dc9e6c364a637495161f5d41bc2a21e1075ed84 |\n| OpenClaw integration | 4fab15ca323ac46f8c9905d5a82d7cb02023220f17d20ba21c69dce5b540c924 |\n| OpenCode integration | d39cb672b9f9da03f3da00e83c964849bfdf3f9d06ca54efbbabd5374aa8c376 |\n| OpenAI-compatible API reference | 1be241b9a882b3c70954e8ec04031cb657b06c8e0b0bbb8511e5b6ec63b82ae7 |\n| Promotions FAQ | 63e8515066b5ec06a61e71262b4a5f41b945f9bd4e540b76299f3437880223e7 |\n| Pay-as-you-go pricing | f62427a339e91edbb252cc6b54cac92050d4254483b1e7eb97a234d6d32f947b |\n| Deprecation schedule | f5c856884116df8ebba552550ec5c6912b1d0bd8edad66e2bf1e973da6270068 |","hierarchy":{"lvl0":"Provider Integration","lvl1":"Xiaomi MiMo text and streaming pilot","lvl2":"Source receipts","lvl3":""}},
9778
9787
  {"objectID":"bf3e1f8f1233a9b3d12aedbdbec4bf8cd196d18bf56b7bb104b34e31e90b61e0","title":"Provider Onboarding Tiers","url":"/docs/provider-integration/tiers/README","content":"Provider Onboarding Tiers\n\nFour tiers, ordered by effort. Always pick the lowest tier that's\nactually true for the provider you're adding — a provider that's\nOpenAI-wire-compatible but gets built as a bespoke Tier 3 subclass \"to be\nsafe\" is exactly the copy-pasted-boilerplate problem this redesign\nexists to eliminate (see ../adr/0002-catalog-over-subclass-default.md).\n\nProvider Integration index\n\n| Tier | Example | Code required | Time |\n| -------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------- |\n| 1 — Aggregator passthrough | A new model id on an existing LiteLLM/OpenRouter route | None | Minutes |\n| 2 — Catalog entry | A new zero-quirk OpenAI-compatible vendor (the Groq/xAI/Together shape) | One data row + one mocked-test section | ~1 hour |\n| 3 — Adapter-based native | A vendor with its own SDK/wire format but a normal request/response HTTP lifecycle | One provider class | Days |\n| 4 — Full custom | SageMaker-class: non-HTTP protocol, SDK-signed auth, bespoke lifecycle | Custom executeStream/doGenerate, possibly own CLI subcommands | Days, needs written justification |\n\nTier 1 adds zero provider coverage — it is a usage change against an\nalready-counted aggregator, never reflected in\ndocs/reference/provider-comparison.md or the README's provider count.\n\nEvery tier that adds a new AIProviderName member (Tier 2 and above) ends\nthe same way: a manifest at\ndocs/provider-integration/manifests/<provider>.json\n(see ../manifests/README.md) and a green run\nof pnpm run verify:provider-onboarding (see\n../../../tools/verify-provider-onboarding.ts).\nTier 1 needs no manifest and no gate — see\ntier-1-aggregator-passthrough.md.\n\nUse ../../../tools/scaffold-provider.ts\n(pnpm run scaffold:provider) to generate the starting-point snippets for\nTiers 2–4 instead of copy-pasting from an existing provider by hand. Both\ntools ship in the tree; there is no manual-fallback era anymore — a PR\nthat skips the gate locally just fails it in CI.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Onboarding Tiers","lvl2":"","lvl3":""}},
9779
9788
  {"objectID":"de01fcaf93bee302e2fca5e18f85cba5def099685383ec01b61d18489dbca5aa","title":"Provider Onboarding Tiers","url":"/docs/provider-integration/tiers/README#provider-onboarding-tiers","content":"Four tiers, ordered by effort. Always pick the lowest tier that's\nactually true for the provider you're adding — a provider that's\nOpenAI-wire-compatible but gets built as a bespoke Tier 3 subclass \"to be\nsafe\" is exactly the copy-pasted-boilerplate problem this redesign\nexists to eliminate (see ../adr/0002-catalog-over-subclass-default.md).\n\nProvider Integration index\n\n| Tier | Example | Code required | Time |\n| -------------------------- | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------- | --------------------------------- |\n| 1 — Aggregator passthrough | A new model id on an existing LiteLLM/OpenRouter route | None | Minutes |\n| 2 — Catalog entry | A new zero-quirk OpenAI-compatible vendor (the Groq/xAI/Together shape) | One data row + one mocked-test section | ~1 hour |\n| 3 — Adapter-based native | A vendor with its own SDK/wire format but a normal request/response HTTP lifecycle | One provider class | Days |\n| 4 — Full custom | SageMaker-class: non-HTTP protocol, SDK-signed auth, bespoke lifecycle | Custom executeStream/doGenerate, possibly own CLI subcommands | Days, needs written justification |\n\nTier 1 adds zero provider coverage — it is a usage change against an\nalready-counted aggregator, never reflected in\ndocs/reference/provider-comparison.md or the README's provider count.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Onboarding Tiers","lvl2":"Provider Onboarding Tiers","lvl3":""}},
9780
9789
  {"objectID":"a9753e41ed762a57c15471c49e70dc9917e8388eca055e3dc1c4ce90db6804bb","title":"Tier 1 — Aggregator Passthrough","url":"/docs/provider-integration/tiers/tier-1-aggregator-passthrough","content":"Tier 1 — Aggregator Passthrough\n\nWhen this applies: the model you want is already reachable through a\nprovider NeuroLink already registers as a pass-through aggregator —\ntoday that's litellm (any backend the user's LiteLLM proxy exposes) or\nopenrouter (any model in OpenRouter's catalog). No new\nAIProviderName member, no new provider class, no new catalog row.\n\nWhat you're actually doing: picking a model id string and confirming\nit works — this is a usage change, not an integration change.\n\nChecklist\n[ ] Confirm the aggregator actually serves the model. For LiteLLM,\n check the proxy's /v1/models (or its config.yaml) for the\n model's model_name. For OpenRouter, check\n https://openrouter.ai/models for the exact vendor/model-id\n slug.\n[ ] No AIProviderName enum change. No PROVIDER_DESCRIPTORS change.\n No OPENAI_COMPAT_CATALOG change. If you find yourself editing any\n of those three for a \"Tier 1\" provider, it isn't Tier 1 — restart\n from ../tiers/README.md's decision tree.\n[ ] Optional: if the model needs a friendlier default alias, add it to\n MODEL_ALIASES / DEFAULT_MODEL_ALIASES in\n src/lib/models/modelRegistry.ts. Not required for the model to\n work.\n[ ] Optional: if the model needs a documented env var (e.g., a\n dedicated LiteLLM route), document it in\n docs/getting-started/environment-variables.md.\n[ ] Manually smoke-test the model end-to-end.\n[ ] No manifest file is required — tools/verify-provider-onboarding.ts\n (see ../../../tools/verify-provider-onboarding.ts) only gates\n new AIProviderName members, and Tier 1 never adds one.\n[ ] Confirmed: no edit to docs/reference/provider-comparison.md or\n README provider count for this change.\n\nVerification commands\n\nBoth should return a normal GenerateResult with non-empty content. If\neither 400s with an \"unknown model\" style error, the aggregator doesn't\nactually serve that model yet — fix the aggregator-side config, not\nNeuroLink.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Tier 1 — Aggregator Passthrough","lvl2":"","lvl3":""}},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.51.3",
3
+ "version": "12.52.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -162,8 +162,10 @@
162
162
  "test:provider-fallback-latency": "pnpm exec tsx test/continuous-test-suite-provider-fallback-latency.ts",
163
163
  "test:provider-wiring": "pnpm exec tsx test/continuous-test-suite-provider-wiring.ts",
164
164
  "test:embedding-response-validation": "pnpm exec tsx test/continuous-test-suite-embedding-response-validation.ts",
165
+ "test:xiaomi-mimo-pilot": "pnpm exec tsx test/continuous-test-suite-xiaomi-mimo-pilot.ts",
165
166
  "test:pdf-image-streaming": "pnpm exec tsx test/continuous-test-suite-pdf-image-streaming.ts",
166
167
  "test:rag": "pnpm exec tsx test/continuous-test-suite-rag.ts",
168
+ "test:rag-html-markup": "pnpm exec tsx test/continuous-test-suite-rag-html-markup.ts",
167
169
  "test:skills": "pnpm exec tsx test/continuous-test-suite-skills.ts",
168
170
  "test:decide": "pnpm exec tsx test/continuous-test-suite-decide.ts",
169
171
  "test:decide-catalog": "pnpm exec tsx test/continuous-test-suite-decide-catalog.ts",
@@ -218,7 +220,7 @@
218
220
  "// CI tier — fast, no live AI calls, safe for every commit (test:unit; also see the separate provider-safety-net CI job, which runs build + test:providers-mocked + test:provider-structure + test:error-classifier-contract on every PR)": "",
219
221
  "test:tool-routing": "pnpm exec tsx test/continuous-test-suite-tool-routing.ts",
220
222
  "test:tool-routing-semantic": "pnpm exec tsx test/continuous-test-suite-tool-routing-semantic.ts",
221
- "test:unit": "pnpm run test:bugfixes && pnpm run test:mcp:infra && pnpm run test:mcp:spans && pnpm run test:tool-routing && pnpm run test:tool-routing-cli && pnpm run test:tool-dedup && pnpm run test:model-pool && pnpm run test:classifier-router && pnpm run test:tool-routing-semantic && pnpm run test:mcp-result-cache && pnpm run test:mcp-direct-name-repair && pnpm run test:mcp-breaker-resolved-errors && pnpm run test:archive:security && pnpm run test:office:security && pnpm run test:image-exif && pnpm run test:vector-chroma && pnpm run test:vector-pgvector && pnpm run test:vector-pinecone && pnpm run test:provider-wiring && pnpm run test:docs-mcp && pnpm run test:retired-model-defaults && pnpm run test:cli-fallback && pnpm run test:embedding-response-validation",
223
+ "test:unit": "pnpm run test:bugfixes && pnpm run test:mcp:infra && pnpm run test:mcp:spans && pnpm run test:tool-routing && pnpm run test:tool-routing-cli && pnpm run test:tool-dedup && pnpm run test:model-pool && pnpm run test:classifier-router && pnpm run test:tool-routing-semantic && pnpm run test:mcp-result-cache && pnpm run test:mcp-direct-name-repair && pnpm run test:mcp-breaker-resolved-errors && pnpm run test:archive:security && pnpm run test:office:security && pnpm run test:image-exif && pnpm run test:vector-chroma && pnpm run test:vector-pgvector && pnpm run test:vector-pinecone && pnpm run test:provider-wiring && pnpm run test:docs-mcp && pnpm run test:retired-model-defaults && pnpm run test:cli-fallback && pnpm run test:embedding-response-validation && pnpm run test:xiaomi-mimo-pilot",
222
224
  "// 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; test:matrix, a different suite covering the full provider capability matrix, runs nightly via .github/workflows/live-matrix.yml — test:providers itself is still only wired into test:live, not any GitHub Actions workflow)": "",
223
225
  "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 && pnpm run test:model-not-found-retryable",
224
226
  "// CI tier — product output (image/video/TTS/PPT) — costs $$ per run (not wired into any GitHub Actions workflow as of this comment; run manually or add to live-matrix.yml if nightly coverage is needed)": "",