@mikedown/core 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +121 -2
- package/dist/ai/engines-page.d.ts +130 -0
- package/dist/ai/engines-page.js +10 -0
- package/dist/ai/engines-page.js.map +1 -0
- package/dist/ai/index.d.ts +1531 -0
- package/dist/ai/index.js +395 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai-ui-C7ENTWWU.js +3370 -0
- package/dist/ai-ui-C7ENTWWU.js.map +1 -0
- package/dist/chunk-7A5HEPLK.js +4910 -0
- package/dist/chunk-7A5HEPLK.js.map +1 -0
- package/dist/chunk-GWCRPPCX.js +18 -0
- package/dist/chunk-GWCRPPCX.js.map +1 -0
- package/dist/chunk-JZWKEBCI.js +3899 -0
- package/dist/chunk-JZWKEBCI.js.map +1 -0
- package/dist/chunk-L4FO63DF.js +113 -0
- package/dist/chunk-L4FO63DF.js.map +1 -0
- package/dist/chunk-LXZG7IKN.js +39 -0
- package/dist/chunk-LXZG7IKN.js.map +1 -0
- package/dist/chunk-QDVY7WW6.js +165 -0
- package/dist/chunk-QDVY7WW6.js.map +1 -0
- package/dist/chunk-QWKT57TR.js +62 -0
- package/dist/chunk-QWKT57TR.js.map +1 -0
- package/dist/chunk-TSP7BAV3.js +22 -0
- package/dist/chunk-TSP7BAV3.js.map +1 -0
- package/dist/index.d.ts +19 -463
- package/dist/index.js +6511 -1258
- package/dist/index.js.map +1 -1
- package/dist/schema-DYzxkuSb.d.ts +46 -0
- package/dist/shared/anchoring.js +4 -16
- package/dist/shared/anchoring.js.map +1 -1
- package/dist/shared/imagePaste.js +3 -57
- package/dist/shared/imagePaste.js.map +1 -1
- package/dist/shared/sha1.js +3 -57
- package/dist/shared/sha1.js.map +1 -1
- package/dist/shared/wordCount.js +4 -12
- package/dist/shared/wordCount.js.map +1 -1
- package/dist/styles.css +3642 -127
- package/dist/types-CgEeIUhi.d.ts +432 -0
- package/dist/types-CsVNVSNY.d.ts +597 -0
- package/package.json +11 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai/types.ts","../src/ai/tasks.ts","../src/ai/features.ts","../src/ai/capabilityHeuristics.ts","../src/ai/presets.ts","../src/ai/discovery.ts","../src/ai/schema.ts","../src/ai/router.ts","../src/ai/sse.ts","../src/ai/stats.ts","../src/ai/requests.ts","../src/ai/wire.ts","../src/ai/errors.ts","../src/ai/thinking.ts","../src/ai/prompts/rewrite.ts","../src/ai/prompts/explain.ts","../src/ai/prompts/structureCheck.ts","../src/ai/json.ts","../src/ai/documentBudget.ts","../src/ai/prompts/summary.ts","../src/ai/prompts/title.ts","../src/ai/prompts/properties.ts","../src/ai/privacy.ts","../src/ai/setup.ts"],"sourcesContent":["/**\n * AI provider layer contracts: the settings shape (engines, models, routing,\n * features) and the `ai` host group.\n *\n * Everything under `packages/core/src/ai/` is pure: no network, no DOM, no\n * provider SDK. Inference runs in the host (VS Code extension host, the\n * Desktop Rust side, or an npm consumer's own backend); core only resolves\n * which engine and model should serve a request and renders the UI.\n *\n * Open-ended by design: `routing.tasks` and `features` are open maps, and the\n * parser keeps keys it does not understand on every object (top level,\n * engines, models, routes, feature entries), so a newer version's settings\n * survive a save from an older one. Those extra keys ride along at runtime\n * and are not part of these types.\n */\n\n// ── Engines and models ──────────────────────────────────────────────────────\n\n/**\n * How the host talks to an engine. `openai-compatible` covers oMLX, vLLM,\n * llama.cpp server and the hosted OpenAI-style APIs (OpenRouter, Groq,\n * Gemini, Mistral, DeepSeek, xAI). `vscode-lm` is a model VS Code itself\n * offers (GitHub Copilot, through `vscode.lm`): no address, no key, VS Code\n * host only (see `AI_HOST_ENGINE_KINDS`). A settings file written by a newer\n * version may carry a kind this one does not know; such engines are kept on\n * save but never routed (see `isKnownEngineKind`).\n */\nexport type AiEngineKind = 'openai-compatible' | 'lmstudio' | 'ollama' | 'anthropic' | 'openai' | 'vscode-lm';\n\nexport const AI_ENGINE_KINDS: readonly AiEngineKind[] = ['openai-compatible', 'lmstudio', 'ollama', 'anthropic', 'openai', 'vscode-lm'];\n\n/**\n * Kinds only one host can run (no base URL, no key: the host's own model\n * API). A host runs one only when it reports a status for the engine\n * (`listEngines`); elsewhere such an engine is kept and never routed, the\n * same as an unknown kind. The kind picker offers them only to an engine\n * that already has one.\n */\nexport const AI_HOST_ENGINE_KINDS: readonly AiEngineKind[] = ['vscode-lm'];\n\n/** `auto` derives local or cloud from the base URL (see `detectLocality`). */\nexport type AiLocalitySetting = 'auto' | 'local' | 'cloud';\nexport type AiLocality = 'local' | 'cloud';\n\n/** Where a model's capability values came from. Re-discovery refreshes `endpoint` and `heuristic`, never `user`. */\nexport type AiCapabilitiesSource = 'endpoint' | 'heuristic' | 'user';\n\nexport interface AiModelCapabilities {\n /** Chat/instruct completion. */\n chat: boolean;\n /** SSE token streaming (assumed true unless the probe says otherwise). */\n stream: boolean;\n /** Image inputs. */\n vision: boolean;\n /** JSON mode or reliable structured output. */\n json: boolean;\n /** Function calling. */\n tools: boolean;\n /** Fill-in-the-middle / raw completion (ghost text). */\n fim: boolean;\n /** Embeddings endpoint. */\n embed: boolean;\n /** Context window in tokens; null when unknown (treated as `DEFAULT_CONTEXT_TOKENS`). */\n contextTokens: number | null;\n}\n\n/** The boolean capability flags, for chips and requirement checks. */\nexport type AiCapabilityFlag = Exclude<keyof AiModelCapabilities, 'contextTokens'>;\n\nexport const AI_CAPABILITY_FLAGS: readonly AiCapabilityFlag[] = ['chat', 'stream', 'vision', 'json', 'tools', 'fim', 'embed'];\n\n/** Context window assumed when neither the endpoint nor the id says (OPEN-10). */\nexport const DEFAULT_CONTEXT_TOKENS = 8192;\n\nexport interface AiModelConfig {\n /** The id the endpoint knows the model by (sent as `model`). Unique within its engine. */\n id: string;\n /** Display name. Absent means show the id. */\n label?: string;\n enabled: boolean;\n capabilities: AiModelCapabilities;\n capabilitiesSource: AiCapabilitiesSource;\n /**\n * Merged into the request body last, so it wins (OPEN-6): thinking controls\n * such as `reasoning_effort`, `thinking_budget`, `chat_template_kwargs`.\n */\n requestOverrides?: Record<string, unknown>;\n /** The last discovery did not list this model. Kept (greyed), never routed. */\n missing?: boolean;\n /** Added by hand. A discovery that does not list it leaves it alone (no `missing`). */\n manual?: boolean;\n}\n\nexport interface AiApiKeyFile {\n path: string;\n jsonPath: string;\n}\n\nexport interface AiEngineConfig {\n /** Stable, generated once, never shown. */\n id: string;\n name: string;\n kind: AiEngineKind;\n /** e.g. `http://127.0.0.1:8000/v1` or `https://api.anthropic.com`. */\n baseUrl: string;\n enabled: boolean;\n locality: AiLocalitySetting;\n /** Environment variable the host reads when no key is stored. The key itself is never in settings. */\n apiKeyEnv?: string;\n /**\n * A JSON file in the user's home folder the host reads when no key is stored and\n * `apiKeyEnv` is unset. `path` may start with `~/`; `jsonPath` is a dotted path to\n * a string value (`auth.api_key`). The key itself is never in settings.\n */\n apiKeyFile?: AiApiKeyFile;\n /** `auto` lists models from the endpoint and merges them with manual ones; `manual` never asks. */\n discovery: 'auto' | 'manual';\n models: AiModelConfig[];\n}\n\n// ── Routing and features ────────────────────────────────────────────────────\n\nexport interface AiRoute {\n engineId: string;\n modelId: string;\n}\n\n/**\n * What to do when a task's route is unusable. `none` disables the surface,\n * `default` tries the default route, `anyLocal` then any usable local\n * model, `any` then cloud engines that pass the gate.\n */\nexport type AiFallbackMode = 'none' | 'default' | 'anyLocal' | 'any';\n\nexport const AI_FALLBACK_MODES: readonly AiFallbackMode[] = ['none', 'default', 'anyLocal', 'any'];\n\nexport interface AiRouting {\n /** Null until the user picks one. */\n default: AiRoute | null;\n fallback: AiFallbackMode;\n /** Open map keyed by task id (see `AI_TASKS`). An absent task uses `default`. */\n tasks: Record<string, AiRoute>;\n}\n\n/** One feature's saved state. Absent fields fall back to the feature registry. */\nexport interface AiFeatureSetting {\n enabled?: boolean;\n /** Task override, e.g. `{ task: 'vision' }`. */\n task?: string;\n}\n\n/** Open map keyed by feature id (see `AI_FEATURES`). */\nexport type AiFeatureSettings = Record<string, AiFeatureSetting>;\n\n/** Everything under `mikedown.ai.*`. Built by `parseAiSettings`, saved by `serializeAiSettings`. */\nexport interface AiSettings {\n /** Master switch. Default false. */\n enabled: boolean;\n /** Global gate for cloud engines. Default false; per-workspace consent applies on top. */\n allowCloud: boolean;\n engines: AiEngineConfig[];\n routing: AiRouting;\n features: AiFeatureSettings;\n /** CIDR ranges (and `.suffix` host names) that count as local. Default RFC 1918. */\n localNetworks: string[];\n /** The AI button on the toolbar (hosts with AI support only). Default true. */\n showToolbarButton: boolean;\n}\n\n// ── Host group ──────────────────────────────────────────────────────────────\n\nexport type AiMessagePart =\n | { type: 'text'; text: string }\n | { type: 'image'; mime: string; bytes: Uint8Array };\n\nexport interface AiMessage {\n role: 'system' | 'user' | 'assistant';\n content: string | AiMessagePart[];\n}\n\nexport interface AiCompletionRequest {\n requestId: string;\n /** Feature id, for logging and consent copy. */\n feature: string;\n /** Task id, informational for tier-1 hosts. */\n task: string;\n /** Resolved by core; tier-1 hosts may ignore it. */\n route?: AiRoute;\n messages: AiMessage[];\n format?: 'text' | 'json';\n maxTokens?: number;\n temperature?: number;\n /**\n * Per-request body fields (the popover's thinking dial). The host merges\n * them over the model's configured `requestOverrides`, deeply, so the\n * request wins. Tier-1 hosts may ignore them.\n */\n requestOverrides?: Record<string, unknown>;\n}\n\nexport interface AiServedBy {\n engineId?: string;\n /** e.g. \"oMLX (Mac Studio)\". */\n engineName: string;\n modelId: string;\n isLocal: boolean;\n}\n\nexport type AiFinishReason = 'stop' | 'length' | 'cancelled' | 'error';\n\nexport interface AiUsage {\n promptTokens?: number;\n completionTokens?: number;\n}\n\n/**\n * How long a completion took, measured by the host from sending the request.\n * Optional on the wire: an older host sends none.\n */\nexport interface AiTiming {\n /** To the end of the answer. */\n totalMs: number;\n /** To the first output, answer or reasoning. Absent when the server did not stream. */\n firstTokenMs?: number;\n /** Characters of output (answer plus reasoning), to estimate tokens when the engine reports no usage. */\n outputChars?: number;\n}\n\nexport interface AiCompletionResult {\n text: string;\n served: AiServedBy;\n usage?: AiUsage;\n finishReason?: AiFinishReason;\n timing?: AiTiming;\n}\n\n/** One model as the host last saw it. */\nexport interface AiEngineStatusModel {\n id: string;\n label?: string;\n capabilities: AiModelCapabilities;\n source: AiCapabilitiesSource;\n missing?: boolean;\n /** Loaded in memory right now (LM Studio, Ollama report it). */\n loaded?: boolean;\n /** What the server says the model takes, in bytes (oMLX, LM Studio, Ollama report it). */\n sizeBytes?: number;\n /** The server says it is a mixture of experts (LM Studio `arch`, oMLX `config_model_type`). */\n moe?: boolean;\n}\n\n/** Where a host found an engine's key: its secret store, a variable, or a key file. */\nexport type AiKeySource = 'stored' | 'env' | 'file';\n\nexport interface AiEngineStatus {\n engineId: string;\n /** Null = never probed. */\n ok: boolean | null;\n isLocal: boolean;\n hasKey: boolean;\n /**\n * Where the key came from, when `hasKey` and the host knows: its secret store,\n * the engine's `apiKeyEnv` variable, or its `apiKeyFile`. Absent for no key or\n * a host that does not say (the engine form then guesses from the engine's\n * settings).\n */\n keySource?: AiKeySource;\n latencyMs?: number;\n error?: string;\n models: AiEngineStatusModel[];\n}\n\n/**\n * One local model server as `detectLocalServers` found it: whether its preset's\n * address answered, and how many models it listed when it did.\n */\nexport interface AiLocalServerProbe {\n presetId: string;\n baseUrl: string;\n ok: boolean;\n /** Models the server listed (embedding models left out where the server says which they are). */\n models?: number;\n latencyMs?: number;\n}\n\n/**\n * A model API built into the host (VS Code's `vscode.lm`, which GitHub\n * Copilot serves), as `detectHostEngines` found it. Nothing is saved.\n */\nexport interface AiHostEngineProbe {\n /** The preset to add it from (`vscodelm`). */\n presetId: string;\n kind: AiEngineKind;\n /** At least one chat model is available right now. */\n ok: boolean;\n /** Chat models available. */\n models: number;\n /** Why none is available, when the host knows (no Copilot extension, signed out). */\n error?: string;\n}\n\n/**\n * What a host can tell about this machine, for the setup wizard's \"can it\n * run models locally\" hint (`recommendSetupPath`, `modelSizeHint`). A rough\n * call, not a benchmark: VS Code reads no GPU at all.\n */\nexport interface AiHardwareInfo {\n /** Node's `process.arch` names: `arm64`, `x64`. */\n arch: string;\n /** Node's `process.platform` names: `darwin`, `win32`, `linux`. */\n platform: string;\n totalMemoryBytes: number;\n /** macOS on arm64: the GPU shares `totalMemoryBytes` (unified memory). */\n appleSilicon: boolean;\n /** CPU brand string, when the host read one (\"Apple M2 Max\"). */\n cpu?: string;\n /** A discrete GPU, when the host could read one (Desktop: NVIDIA through `nvidia-smi`; Apple Silicon reports `apple`). */\n gpu?: { vendor: 'nvidia' | 'amd' | 'apple' | 'other'; vramBytes?: number; name?: string };\n}\n\nexport type AiErrorCode =\n | 'unreachable'\n | 'auth'\n | 'modelNotFound'\n | 'modelLoadFailed'\n | 'contextOverflow'\n | 'rateLimited'\n | 'cloudBlocked'\n | 'consentDenied'\n | 'cancelled'\n | 'badOutput'\n | 'unknown';\n\nexport const AI_ERROR_CODES: readonly AiErrorCode[] = [\n 'unreachable',\n 'auth',\n 'modelNotFound',\n 'modelLoadFailed',\n 'contextOverflow',\n 'rateLimited',\n 'cloudBlocked',\n 'consentDenied',\n 'cancelled',\n 'badOutput',\n 'unknown',\n];\n\n/** What `MikeDownAiHost` methods reject with. A plain object, so it crosses postMessage and Tauri events. */\nexport interface AiError {\n code: AiErrorCode;\n message: string;\n /** The engine the failed request went to (or the router turned down), when known. */\n engineId?: string;\n}\n\n/**\n * The `ai` group on `MikeDownHost`. Absent turns every AI surface off.\n *\n * Tier 1 (`complete` only): core shows the master switch, feature toggles and\n * the indicator from whatever `served` the host reports. Tier 2 (plus the\n * registry methods): core shows the full AI tab and routes.\n */\nexport interface MikeDownAiHost {\n // Tier 1: enough for every feature.\n complete(req: AiCompletionRequest, onToken: (delta: string) => void, signal: AbortSignal): Promise<AiCompletionResult>;\n embed?(texts: string[], route?: AiRoute, signal?: AbortSignal): Promise<number[][]>;\n transcribe?(audio: { mime: string; bytes: Uint8Array }, signal?: AbortSignal): Promise<string>;\n\n // Tier 2: turns on the engine registry UI and core routing.\n listEngines?(): Promise<AiEngineStatus[]>;\n /**\n * Look for model servers on this machine at the local presets' default\n * addresses (`localDetectCandidates()`), one GET each, capped at\n * `AI_LOCAL_DETECT_TIMEOUT_MS`. Only on request (the first-run panel, the\n * quick-start cards), never in the background. The host looks each preset\n * up by id itself and sends a key (oMLX's key file) only to the preset's\n * own address. One result per candidate, in order. Nothing is saved.\n */\n detectLocalServers?(candidates: { presetId: string; baseUrl: string }[], signal: AbortSignal): Promise<AiLocalServerProbe[]>;\n /**\n * Model APIs built into the host that need no address or key (VS Code:\n * `vscode.lm`, GitHub Copilot's models), one result per `AI_HOST_ENGINE_KINDS`\n * kind the host runs. Lists models only; sends no text. Absent: the host\n * has none, and the `host` preset family stays hidden.\n */\n detectHostEngines?(): Promise<AiHostEngineProbe[]>;\n /**\n * This machine's memory, CPU and (where the host can read it) GPU, for the\n * setup wizard's local hint. Static, so hosts may answer from a cache; null\n * when the host cannot tell.\n */\n hardwareInfo?(): Promise<AiHardwareInfo | null>;\n testEngine?(engineId: string): Promise<AiEngineStatus>;\n discoverModels?(engineId: string): Promise<AiEngineStatus>;\n /** Store (or with null, delete) an engine's key. Never part of `saveSettings`. */\n setSecret?(engineId: string, apiKey: string | null): Promise<void>;\n /**\n * False when the host process may not see variables set in a shell profile\n * (MikeDown Desktop opened from the Dock or Start menu), so `apiKeyEnv` is\n * unreliable there and the engine form points at the key file or a pasted\n * key instead. Absent: the host reads them (VS Code resolves the shell\n * environment) or does not say.\n */\n readonly shellEnv?: boolean;\n /**\n * Host UI asking to use a cloud engine in this workspace. Absent means the\n * host decides on its own. `about` names the request that asked, so the\n * dialog can say what is sent (`aiConsentText`).\n */\n requestCloudConsent?(engineId: string, about?: AiConsentAbout): Promise<AiConsentReply>;\n /**\n * The cloud consents for this workspace or folder, for the Cloud consents\n * list on the AI tab: the stored answers, plus this session's \"Don't\n * allow\" (`deny`), so it can be forgotten without a restart. \"Allow once\"\n * leaves nothing to list.\n */\n listConsents?(): Promise<AiConsentRecord[]>;\n /** Forget the answer for an engine (stored or this session's), so the next request asks again. */\n forgetConsent?(engineId: string): Promise<void>;\n}\n\n/**\n * What `requestCloudConsent` resolves to: `true` allowed for the workspace\n * (remembered), `'once'` allowed for this request only (the next one asks\n * again), `false` declined for this request (a host may also stop asking for\n * the session; it then answers `false` without a dialog), `'never'` a stored\n * refusal from an older version (the host answers it without asking). A host\n * that only knows allow and decline keeps returning booleans.\n */\nexport type AiConsentReply = boolean | 'once' | 'never';\n\n/** One stored cloud consent answer (`MikeDownAiHost.listConsents`). */\nexport interface AiConsentRecord {\n engineId: string;\n /** `allow` and `never` (older versions) are stored; `deny` is this session's \"Don't allow\". */\n answer: 'allow' | 'never' | 'deny';\n /** Unix milliseconds. */\n at: number;\n /** What the answer covers: VS Code's workspace, Desktop's folder. */\n scope: 'workspace' | 'folder';\n}\n\n/**\n * What a request sends to the engine, for the privacy copy (`whatIsSent`):\n * the selection with its neighbouring paragraphs, the whole document, or the\n * document with its frontmatter and the workspace's tag names.\n */\nexport type AiSendsScope = 'selection' | 'document' | 'documentAndTags';\n\n/** The request behind a consent prompt. `sends` overrides the feature's own scope (whole-document Translate). */\nexport interface AiConsentAbout {\n feature?: string;\n sends?: AiSendsScope;\n}\n\n// ── Provider adapters (host side) ───────────────────────────────────────────\n\n/** A model as one discovery call reported it, before merging into settings. */\nexport interface AiDiscoveredModel {\n id: string;\n label?: string;\n /** Endpoint values; anything absent is filled from the id heuristics on merge. */\n capabilities: Partial<AiModelCapabilities>;\n /** `endpoint` when the server described the model, `heuristic` when only its id was listed. */\n source: 'endpoint' | 'heuristic';\n loaded?: boolean;\n /** Bytes the model takes, as the server reports it (oMLX `estimated_size`, LM Studio `size_bytes`, Ollama `size`). */\n sizeBytes?: number;\n /** The server says it is a mixture of experts: an architecture name with `moe` in it (LM Studio `arch`, oMLX `config_model_type`). */\n moe?: boolean;\n}\n\n/**\n * Host-side adapter shape. TypeScript in the extension (`src/ai/providers/`),\n * a Rust trait with the same shape on Desktop. Declared here so both hosts\n * share one contract; core never implements it.\n */\nexport interface ProviderAdapter {\n readonly kind: AiEngineKind;\n /** Discovery plus capability detection. */\n listModels(engine: AiEngineConfig, apiKey: string | null, signal: AbortSignal): Promise<AiDiscoveredModel[]>;\n /** Test connection: reachability, auth, latency. */\n probe(engine: AiEngineConfig, apiKey: string | null, signal: AbortSignal): Promise<{ ok: boolean; latencyMs: number; error?: AiError }>;\n /** Streaming completion against one model. */\n complete(\n engine: AiEngineConfig,\n apiKey: string | null,\n model: AiModelConfig,\n req: AiCompletionRequest,\n onToken: (delta: string) => void,\n signal: AbortSignal,\n ): Promise<AiCompletionResult>;\n embed?(engine: AiEngineConfig, apiKey: string | null, model: AiModelConfig, texts: string[], signal: AbortSignal): Promise<number[][]>;\n}\n","/**\n * Task taxonomy. A feature names a task; the router picks the engine and\n * model routed to that task and checks the model has what the task needs.\n * Task ids are the keys of `routing.tasks`; unknown ids saved by a newer\n * version are kept and route like any other (with no requirements).\n */\n\nimport { DEFAULT_CONTEXT_TOKENS } from './types';\nimport type { AiCapabilityFlag, AiModelCapabilities } from './types';\n\n/** A task's needs. Booleans must be true on the model; `contextTokens` is a minimum. */\nexport type AiCapabilityRequirements = Partial<AiModelCapabilities>;\n\nexport interface AiTaskDefinition {\n id: string;\n label: string;\n description: string;\n requires: AiCapabilityRequirements;\n /**\n * Nice to have, never a gate: the router tries candidates that meet these\n * first and still uses one that does not (a model without JSON mode, a\n * small context window).\n */\n prefers?: AiCapabilityRequirements;\n /** At least one of these flags must be true (ghost text: a FIM model, or a chat model with a continue prompt). */\n requiresAny?: AiCapabilityFlag[];\n /** Served by a host method rather than a model completion. */\n hostMethod?: 'embed' | 'transcribe';\n}\n\n/** The \"comfortable\" context for `summarize`: preferred by the router, and the point below which the UI may warn. Not a gate. */\nexport const SUMMARIZE_MIN_CONTEXT_TOKENS = 32768;\n\nexport const AI_TASKS: readonly AiTaskDefinition[] = [\n {\n id: 'rewrite',\n label: 'Rewrite',\n description: 'Selection rewrite, explain or expand, reading level, translation.',\n requires: { chat: true, stream: true },\n },\n {\n id: 'continue',\n label: 'Continue',\n description: 'Inline ghost text. Prefers a small fill-in-the-middle model.',\n requires: { stream: true },\n requiresAny: ['fim', 'chat'],\n },\n {\n id: 'chat',\n label: 'Chat',\n description: 'Chat with the document, prompt blocks.',\n requires: { chat: true, stream: true },\n },\n {\n id: 'summarize',\n label: 'Summarize',\n description: 'Summaries and \"what changed\". Works best with a large context window; long documents are cut and labelled.',\n requires: { chat: true },\n prefers: { contextTokens: SUMMARIZE_MIN_CONTEXT_TOKENS },\n },\n {\n id: 'structure',\n label: 'Structure',\n description: 'Frontmatter and tags, Mermaid, tables, code, titles, paste cleanup. Prefers a model with JSON output.',\n requires: { chat: true },\n prefers: { json: true },\n },\n {\n id: 'notes',\n label: 'Notes',\n description: 'Meeting notes to action items.',\n requires: { chat: true, json: true },\n },\n {\n id: 'review',\n label: 'Review',\n description: 'Grammar pass and consistency checks.',\n requires: { chat: true, json: true },\n },\n {\n id: 'vision',\n label: 'Vision',\n description: 'Alt text for images.',\n requires: { vision: true },\n },\n {\n id: 'embed',\n label: 'Embeddings',\n description: 'Workspace search and link suggestions.',\n requires: { embed: true },\n hostMethod: 'embed',\n },\n {\n id: 'transcribe',\n label: 'Transcribe',\n description: 'Dictation, through the host.',\n requires: {},\n hostMethod: 'transcribe',\n },\n];\n\nexport function getAiTask(id: string): AiTaskDefinition | undefined {\n return AI_TASKS.find((t) => t.id === id);\n}\n\n/** Merge two requirement sets: flags OR together, `contextTokens` takes the larger minimum. */\nexport function combineRequirements(a: AiCapabilityRequirements, b: AiCapabilityRequirements): AiCapabilityRequirements {\n const out: AiCapabilityRequirements = { ...a };\n for (const [key, value] of Object.entries(b) as Array<[keyof AiModelCapabilities, unknown]>) {\n if (key === 'contextTokens') {\n if (typeof value === 'number') {out.contextTokens = Math.max(out.contextTokens ?? 0, value);}\n } else if (value === true) {\n out[key] = true;\n }\n }\n return out;\n}\n\n/**\n * The capabilities a model lacks for these requirements, as flag names (and\n * `contextTokens` when the window is too small). Empty means it qualifies.\n * An unknown context window counts as `DEFAULT_CONTEXT_TOKENS`.\n */\nexport function missingCapabilities(\n caps: AiModelCapabilities,\n requires: AiCapabilityRequirements,\n requiresAny?: readonly AiCapabilityFlag[],\n defaultContextTokens = DEFAULT_CONTEXT_TOKENS,\n): Array<keyof AiModelCapabilities> {\n const missing: Array<keyof AiModelCapabilities> = [];\n for (const [key, value] of Object.entries(requires) as Array<[keyof AiModelCapabilities, unknown]>) {\n if (key === 'contextTokens') {\n if (typeof value === 'number' && (caps.contextTokens ?? defaultContextTokens) < value) {missing.push('contextTokens');}\n } else if (value === true && !caps[key]) {\n missing.push(key);\n }\n }\n if (requiresAny && requiresAny.length > 0 && !requiresAny.some((flag) => caps[flag])) {\n missing.push(requiresAny[0]);\n }\n return missing;\n}\n","/**\n * Feature registry. A feature's defaults (on or off, which task it uses,\n * what else it needs from a model) live here, so adding a feature is one\n * entry plus its UI and never a settings migration. `features.<id>` in\n * settings overrides `enabled` and `task`.\n */\n\nimport type { AiCapabilityRequirements } from './tasks';\nimport type { AiFeatureSettings, AiSendsScope } from './types';\n\nexport interface AiFeatureDefinition {\n id: string;\n label: string;\n description: string;\n defaultEnabled: boolean;\n defaultTask: string;\n /** Needs on top of the task's own. */\n requires: AiCapabilityRequirements;\n /** `planned` features are listed greyed (\"coming later\") and never surface. */\n status: 'available' | 'planned';\n /** What a request sends, for the consent dialogs and tooltips (`whatIsSent`). Absent: a generic sentence. */\n sends?: AiSendsScope;\n}\n\nexport const AI_FEATURES: readonly AiFeatureDefinition[] = [\n {\n id: 'rewrite',\n label: 'Rewrite selection',\n description: 'Shorter, longer, fix flow or a custom prompt, previewed before it replaces the selection.',\n defaultEnabled: true,\n defaultTask: 'rewrite',\n requires: { chat: true, stream: true },\n status: 'available',\n sends: 'selection',\n },\n {\n id: 'ghostText',\n label: 'Inline suggestions',\n description: 'Dim continuation text while you write. Sends the text around the cursor as you type.',\n // Separate opt-in even with the master switch on (brainstorm idea 1).\n defaultEnabled: false,\n defaultTask: 'continue',\n requires: { stream: true },\n status: 'planned',\n },\n {\n id: 'chat',\n label: 'Chat with this document',\n description: 'A sidebar chat with the document as context.',\n defaultEnabled: true,\n defaultTask: 'chat',\n requires: { chat: true, stream: true },\n status: 'planned',\n sends: 'document',\n },\n {\n id: 'summary',\n label: 'Summary',\n description: 'Insert a summary or outline of the document.',\n defaultEnabled: true,\n defaultTask: 'summarize',\n requires: { chat: true },\n status: 'available',\n sends: 'document',\n },\n {\n id: 'frontmatter',\n label: 'Frontmatter and tags',\n description: 'Suggest a title, tags and other properties.',\n defaultEnabled: true,\n defaultTask: 'structure',\n requires: { chat: true },\n status: 'available',\n sends: 'documentAndTags',\n },\n {\n id: 'title',\n label: 'Suggest titles',\n description: 'Offer a few titles for the document.',\n defaultEnabled: true,\n defaultTask: 'structure',\n requires: { chat: true },\n status: 'available',\n sends: 'document',\n },\n {\n id: 'altText',\n label: 'Image alt text',\n description: 'Describe pasted images for their alt text.',\n defaultEnabled: true,\n defaultTask: 'vision',\n requires: { vision: true },\n status: 'planned',\n },\n];\n\nexport function getAiFeature(id: string): AiFeatureDefinition | undefined {\n return AI_FEATURES.find((f) => f.id === id);\n}\n\nexport interface ResolvedAiFeature {\n id: string;\n /** Saved value, else the registry default. Unknown features default to off. */\n enabled: boolean;\n /** Saved override, else the registry default task, else the feature id. */\n task: string;\n requires: AiCapabilityRequirements;\n definition?: AiFeatureDefinition;\n}\n\n/** A feature's effective state from saved settings plus the registry. Ignores the master switch. */\nexport function resolveAiFeature(features: AiFeatureSettings, id: string): ResolvedAiFeature {\n const definition = getAiFeature(id);\n const saved = features[id] ?? {};\n return {\n id,\n enabled: saved.enabled ?? definition?.defaultEnabled ?? false,\n task: saved.task ?? definition?.defaultTask ?? id,\n requires: definition?.requires ?? {},\n definition,\n };\n}\n","/**\n * Capability detection for models the endpoint does not describe (oMLX and\n * most OpenAI-compatible servers list bare ids). Order of trust: endpoint\n * metadata, then these heuristics, then user edits, which always win and\n * survive re-discovery (`capabilitiesSource: 'user'`).\n *\n * Local models: an ordered regex table of families; every matching rule\n * applies in order, so later, more specific rules override earlier ones.\n * Cloud models (Anthropic, OpenAI): static tables matched by the longest id\n * prefix, so a new model in a known line inherits its line's values.\n */\n\nimport { DEFAULT_CONTEXT_TOKENS } from './types';\nimport type { AiDiscoveredModel, AiEngineKind, AiModelCapabilities, AiModelConfig } from './types';\n\ninterface FamilyRule {\n family: string;\n pattern: RegExp;\n caps: Partial<AiModelCapabilities>;\n}\n\nconst BASE_CAPABILITIES: AiModelCapabilities = {\n chat: true,\n stream: true,\n vision: false,\n json: false,\n tools: false,\n fim: false,\n embed: false,\n contextTokens: null,\n};\n\nconst NOT_A_CHAT_MODEL: Partial<AiModelCapabilities> = { chat: false, stream: false, vision: false, json: false, tools: false, fim: false };\n\n// Matched against the lower-cased id. Order matters: later rules override.\nconst FAMILY_RULES: FamilyRule[] = [\n { family: 'qwen', pattern: /qwen/, caps: { json: true, tools: true, contextTokens: 32768 } },\n { family: 'qwen3', pattern: /qwen-?3(?![.\\d])/, caps: { contextTokens: 40960 } },\n // Qwen 3.5 and later (and the qwen4_exp preview) carry vision in the same weights.\n { family: 'qwen3.5+', pattern: /qwen-?3\\.[5-9]|qwen-?4/, caps: { vision: true, contextTokens: 262144 } },\n { family: 'qwen-coder', pattern: /qwen.*coder/, caps: { vision: false, fim: true, contextTokens: 262144 } },\n { family: 'qwen2.5-coder', pattern: /qwen-?2\\.5.*coder/, caps: { contextTokens: 32768 } },\n { family: 'gemma', pattern: /gemma/, caps: { json: true, contextTokens: 8192 } },\n { family: 'gemma3', pattern: /gemma-?3/, caps: { vision: true, contextTokens: 131072 } },\n { family: 'gemma4', pattern: /gemma-?4/, caps: { vision: true, tools: true, contextTokens: 131072 } },\n { family: 'codegemma', pattern: /codegemma/, caps: { vision: false, fim: true } },\n { family: 'llama3.1+', pattern: /llama-?3\\.[1-3]/, caps: { json: true, tools: true, contextTokens: 131072 } },\n { family: 'llama4', pattern: /llama-?4/, caps: { json: true, tools: true, vision: true, contextTokens: 131072 } },\n { family: 'mistral', pattern: /mistral|mixtral|ministral|magistral/, caps: { json: true, tools: true, contextTokens: 32768 } },\n { family: 'mistral-vision', pattern: /mistral-small-3\\.[1-9]|mistral-medium|magistral|pixtral/, caps: { vision: true, contextTokens: 131072 } },\n { family: 'codestral', pattern: /codestral/, caps: { fim: true, json: true, contextTokens: 32768 } },\n { family: 'deepseek', pattern: /deepseek/, caps: { json: true, tools: true, contextTokens: 131072 } },\n { family: 'deepseek-v4', pattern: /deepseek-?v4/, caps: { contextTokens: 1048576 } },\n { family: 'deepseek-coder', pattern: /deepseek-coder/, caps: { fim: true } },\n { family: 'minimax', pattern: /minimax/, caps: { json: true, tools: true, contextTokens: 196608 } },\n { family: 'glm', pattern: /glm/, caps: { json: true, tools: true, contextTokens: 131072 } },\n { family: 'glm5', pattern: /glm-?5/, caps: { contextTokens: 1048576 } },\n { family: 'gpt-oss', pattern: /gpt-oss/, caps: { json: true, tools: true, contextTokens: 131072 } },\n { family: 'phi4', pattern: /phi-?4/, caps: { json: true, contextTokens: 16384 } },\n { family: 'hermes', pattern: /hermes/, caps: { json: true, tools: true } },\n { family: 'nemotron', pattern: /nemotron/, caps: { json: true, tools: true, contextTokens: 131072 } },\n { family: 'starcoder', pattern: /starcoder|stable-?code/, caps: { fim: true } },\n // Name markers for multimodal builds, whatever the family.\n { family: 'vision', pattern: /(^|[^a-z])vl([^a-z]|$)|vision|llava|minicpm-v|moondream|omni|multimodal/, caps: { vision: true } },\n // Embedding models: not chat models at all.\n { family: 'embedding', pattern: /embed|(^|[/_-])(bge|gte|e5)-|minilm|mxbai|arctic-embed|snowflake/, caps: { ...NOT_A_CHAT_MODEL, embed: true, contextTokens: 8192 } },\n { family: 'bge', pattern: /(^|[/_-])bge-(small|base|large)/, caps: { contextTokens: 512 } },\n // Speech and rerankers.\n { family: 'speech', pattern: /whisper|parakeet|kokoro|(^|[/_-])tts([/_-]|$)|rerank/, caps: { ...NOT_A_CHAT_MODEL, embed: false } },\n];\n\ntype CloudTable = Array<{ prefix: string; caps: Partial<AiModelCapabilities> }>;\n\nconst CLAUDE_BASE: Partial<AiModelCapabilities> = { chat: true, stream: true, vision: true, json: true, tools: true, fim: false, embed: false };\n\n/** Anthropic models, matched by the longest prefix. Unknown `claude-*` ids get the generic row. */\nexport const ANTHROPIC_MODEL_TABLE: CloudTable = [\n { prefix: 'claude-', caps: { ...CLAUDE_BASE, contextTokens: 200000 } },\n { prefix: 'claude-3', caps: { contextTokens: 200000 } },\n { prefix: 'claude-haiku-4-5', caps: { contextTokens: 200000 } },\n { prefix: 'claude-opus-4-6', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-opus-4-7', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-opus-4-8', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-sonnet-4-6', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-opus-5', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-sonnet-5', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-haiku-5', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-fable-5', caps: { contextTokens: 1000000 } },\n { prefix: 'claude-mythos-5', caps: { contextTokens: 1000000 } },\n];\n\n/** OpenAI models, matched by the longest prefix. */\nexport const OPENAI_MODEL_TABLE: CloudTable = [\n { prefix: 'gpt-', caps: { chat: true, stream: true, json: true, tools: true, vision: false, fim: false, embed: false, contextTokens: 128000 } },\n { prefix: 'gpt-3.5', caps: { contextTokens: 16385 } },\n { prefix: 'gpt-4o', caps: { vision: true, contextTokens: 128000 } },\n { prefix: 'gpt-4.1', caps: { vision: true, contextTokens: 1047576 } },\n { prefix: 'gpt-5', caps: { vision: true, contextTokens: 400000 } },\n { prefix: 'gpt-oss', caps: { contextTokens: 131072 } },\n { prefix: 'o1', caps: { chat: true, stream: true, json: true, tools: true, vision: true, contextTokens: 200000 } },\n { prefix: 'o3', caps: { chat: true, stream: true, json: true, tools: true, vision: true, contextTokens: 200000 } },\n { prefix: 'o4', caps: { chat: true, stream: true, json: true, tools: true, vision: true, contextTokens: 200000 } },\n { prefix: 'text-embedding-', caps: { ...NOT_A_CHAT_MODEL, embed: true, contextTokens: 8191 } },\n { prefix: 'whisper', caps: NOT_A_CHAT_MODEL },\n { prefix: 'tts-', caps: NOT_A_CHAT_MODEL },\n { prefix: 'gpt-4o-mini-tts', caps: NOT_A_CHAT_MODEL },\n { prefix: 'gpt-4o-transcribe', caps: NOT_A_CHAT_MODEL },\n { prefix: 'gpt-image', caps: NOT_A_CHAT_MODEL },\n { prefix: 'dall-e', caps: NOT_A_CHAT_MODEL },\n { prefix: 'omni-moderation', caps: NOT_A_CHAT_MODEL },\n];\n\n/** The o-series and GPT-5 onwards (`o3`, `o4-mini`, `gpt-5`, `gpt-5.5`, `gpt-6-mini`), not `gpt-4o` or `gpt-oss`. */\nconst OPENAI_REASONING_ID = /^(o\\d+|gpt-([5-9]|\\d{2,})(\\.\\d+)?)(-|$)/;\n\n/**\n * OpenAI's reasoning models, which take only the default sampling: Chat\n * Completions rejects a `temperature` or `top_p` other than 1 for them. The\n * native `openai` kind matches on the id; any other kind only on an\n * `openai/` id (OpenRouter), so a local model that happens to be called\n * `o3-something` keeps its sampling. `-chat-latest` snapshots match too:\n * the default sampling costs nothing there.\n */\nexport function isOpenAiReasoningModel(modelId: string, kind?: AiEngineKind): boolean {\n const id = bareOpenAiId(modelId, kind);\n return id !== null && OPENAI_REASONING_ID.test(id);\n}\n\n/** The id without an `openai/` vendor prefix, or null for an id that is not OpenAI's on this kind. */\nfunction bareOpenAiId(modelId: string, kind?: AiEngineKind): string | null {\n const id = modelId.trim().toLowerCase();\n if (kind === 'openai') {return id.slice(id.lastIndexOf('/') + 1);}\n return id.startsWith('openai/') ? id.slice('openai/'.length) : null;\n}\n\n/** `gpt-5.1` to `gpt-5.9`, the line that added `none` (documented for 5.1, 5.2 and 5.5). */\nconst OPENAI_NONE_EFFORT_ID = /^gpt-5\\.[1-9]\\d*(-|$)/;\n/** Plain `gpt-5`, `gpt-5-mini`, `gpt-5-nano`: `minimal` is their lowest. */\nconst OPENAI_MINIMAL_EFFORT_ID = /^gpt-5(-|$)/;\n\n/**\n * The `reasoning_effort` that means \"Off\" for an OpenAI reasoning model, or\n * null when `modelId` is not one (the caller leaves the request alone). The\n * Thinking dial stores Off as `none`, which only some families accept:\n *\n * - `gpt-5.1` onwards in the 5.x line (5.5 included): `none`.\n * - `gpt-5`, `gpt-5-mini`, `gpt-5-nano`: `minimal`.\n * - everything else (o-series, `gpt-6`+, codex and pro variants, ids not\n * documented yet): `low`, the one value every reasoning family accepts.\n * Newer models reject `none` (and some `minimal`), so unknown means `low`.\n */\nexport function openAiOffReasoningEffort(modelId: string, kind?: AiEngineKind): 'none' | 'minimal' | 'low' | null {\n const id = bareOpenAiId(modelId, kind);\n if (id === null || !OPENAI_REASONING_ID.test(id)) {return null;}\n if (/codex|-pro(-|$)/.test(id)) {return 'low';}\n if (OPENAI_NONE_EFFORT_ID.test(id)) {return 'none';}\n if (OPENAI_MINIMAL_EFFORT_ID.test(id)) {return 'minimal';}\n return 'low';\n}\n\n/** Every row whose prefix matches, shortest first, so longer prefixes override. */\nfunction matchCloudTable(table: CloudTable, id: string): { caps: Partial<AiModelCapabilities>; prefix: string } | null {\n const rows = table.filter((r) => id.startsWith(r.prefix)).sort((a, b) => a.prefix.length - b.prefix.length);\n if (rows.length === 0) {return null;}\n return { caps: Object.assign({}, ...rows.map((r) => r.caps)) as Partial<AiModelCapabilities>, prefix: rows[rows.length - 1].prefix };\n}\n\nexport interface DetectedCapabilities {\n capabilities: AiModelCapabilities;\n /** The family rule or table prefix that matched last, or null for the generic default. */\n family: string | null;\n}\n\n/**\n * Capabilities guessed from a model id. A `vscode-lm` model is a plain chat\n * model (the endpoint gives its context window). Anthropic and OpenAI engines use\n * their static tables; any engine whose id (after a `vendor/` prefix, as on\n * OpenRouter) starts with `claude-` or a known OpenAI line does too. An id\n * nothing matches is a plain chat model with `DEFAULT_CONTEXT_TOKENS`.\n */\nexport function detectCapabilities(modelId: string, kind?: AiEngineKind): DetectedCapabilities {\n // VS Code's language models (`vendor/family`): text chat, streamed. No JSON mode, images or tools through `vscode.lm` here.\n if (kind === 'vscode-lm') {return { capabilities: withDefaultContext({ ...BASE_CAPABILITIES }), family: 'vscode-lm' };}\n const id = modelId.trim().toLowerCase();\n const bare = id.slice(id.lastIndexOf('/') + 1);\n\n const cloudTable =\n kind === 'anthropic' || bare.startsWith('claude-')\n ? ANTHROPIC_MODEL_TABLE\n : kind === 'openai' || /^(gpt-[\\d.]|o[134](-|$)|text-embedding-|whisper|tts-|dall-e|gpt-image|omni-moderation)/.test(bare)\n ? OPENAI_MODEL_TABLE\n : null;\n if (cloudTable) {\n const hit = matchCloudTable(cloudTable, bare);\n if (hit) {\n return { capabilities: withDefaultContext({ ...BASE_CAPABILITIES, ...hit.caps }), family: hit.prefix };\n }\n }\n\n let caps: AiModelCapabilities = { ...BASE_CAPABILITIES };\n let family: string | null = null;\n for (const rule of FAMILY_RULES) {\n if (rule.pattern.test(id)) {\n caps = { ...caps, ...rule.caps };\n family = rule.family;\n }\n }\n return { capabilities: withDefaultContext(caps), family };\n}\n\nfunction withDefaultContext(caps: AiModelCapabilities): AiModelCapabilities {\n return caps.contextTokens === null ? { ...caps, contextTokens: DEFAULT_CONTEXT_TOKENS } : caps;\n}\n\n/** Endpoint values over heuristics: fields the endpoint left out (or null) come from the id. */\nexport function completeCapabilities(partial: Partial<AiModelCapabilities>, modelId: string, kind?: AiEngineKind): AiModelCapabilities {\n const out = { ...detectCapabilities(modelId, kind).capabilities };\n for (const [key, value] of Object.entries(partial) as Array<[keyof AiModelCapabilities, unknown]>) {\n if (key === 'contextTokens') {\n if (typeof value === 'number' && value > 0) {out.contextTokens = value;}\n } else if (typeof value === 'boolean' && key in out) {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Merge one discovery into an engine's saved models, by id:\n *\n * - listed and saved: `endpoint`/`heuristic` rows get the fresh values, `user`\n * rows keep theirs; `enabled`, `label` and `requestOverrides` are kept, and\n * `missing` is cleared;\n * - saved but not listed: marked `missing: true` (greyed, not deleted), except\n * manual entries, which are left alone;\n * - listed but new: appended in discovery order, enabled unless\n * `opts.addEnabled` is false. Cloud engines pass false (OPEN-12): a\n * 300-model list must not flood the routing pickers, so the user picks.\n */\nexport function mergeDiscoveredModels(\n existing: readonly AiModelConfig[],\n discovered: readonly AiDiscoveredModel[],\n kind?: AiEngineKind,\n opts: { addEnabled?: boolean } = {},\n): AiModelConfig[] {\n const addEnabled = opts.addEnabled ?? true;\n const found = new Map<string, AiDiscoveredModel>();\n for (const d of discovered) {\n if (!found.has(d.id)) {found.set(d.id, d);}\n }\n\n const merged = existing.map((model): AiModelConfig => {\n const d = found.get(model.id);\n if (!d) {\n return model.manual ? model : { ...model, missing: true };\n }\n const next: AiModelConfig = { ...model, label: model.label ?? d.label };\n delete next.missing;\n if (next.label === undefined) {delete next.label;}\n if (model.capabilitiesSource !== 'user') {\n next.capabilities = completeCapabilities(d.capabilities, d.id, kind);\n next.capabilitiesSource = d.source;\n }\n return next;\n });\n\n const known = new Set(existing.map((m) => m.id));\n for (const d of found.values()) {\n if (known.has(d.id)) {continue;}\n const added: AiModelConfig = {\n id: d.id,\n enabled: addEnabled,\n capabilities: completeCapabilities(d.capabilities, d.id, kind),\n capabilitiesSource: d.source,\n };\n if (d.label !== undefined) {added.label = d.label;}\n merged.push(added);\n }\n return merged;\n}\n","/**\n * \"Add engine\" presets. A preset only fills the new engine's name, kind, base\n * URL, key variable and key file; everything else is the user's. Presets also\n * drive the Express engine form: which family a card sits in, the caption\n * under it, where to get a key, and which fetched models count as Popular.\n *\n * No model ids here: they go stale, and the endpoint is the source of truth.\n * Popular rules are patterns matched against whatever the endpoint lists.\n */\n\nimport type { AiApiKeyFile, AiEngineKind } from './types';\n\n/**\n * Where a preset runs: your own server, a cloud API you paste a key for, or\n * a model API built into the host (`host`: VS Code's GitHub Copilot models,\n * no address and no key; shown only where the host reports it).\n */\nexport type AiPresetFamily = 'local' | 'cloud' | 'host';\n\n/**\n * One Popular slot: the newest fetched id that matches `match` (and not\n * `exclude`). An id `prefer` matches (an alias such as `-latest`) wins;\n * otherwise \"newest\" compares the version numbers in the id, then a date\n * stamp.\n */\nexport interface AiPopularRule {\n /** Short family name, for the picker's tooltip (\"Sonnet\"). */\n label: string;\n match: RegExp;\n exclude?: RegExp;\n prefer?: RegExp;\n}\n\nexport interface AiEnginePreset {\n id: string;\n name: string;\n kind: AiEngineKind;\n baseUrl: string;\n apiKeyEnv?: string;\n apiKeyFile?: AiApiKeyFile;\n /** Whether the endpoint expects a key at all (LM Studio accepts any). */\n needsKey: boolean;\n family: AiPresetFamily;\n /** The quick-start card's one-line caption. */\n caption: string;\n /** Under the API key field: where to get one, or whether one is needed. A link to `keyUrl` when it has one. */\n keyHint: string;\n /** Cloud presets: the provider's page that creates API keys, opened through the host's `openLink`. */\n keyUrl?: string;\n /** Cloud presets: the Popular group, in order. The first rule that matches is the auto-picked default. */\n popular?: readonly AiPopularRule[];\n /**\n * Local presets: the path (from the server root, without `/v1`) that local\n * server detection GETs when it is not `{baseUrl}/models`. The native lists\n * answer without a key.\n */\n detectPath?: string;\n /** Cloud and host presets: the provider's own page on how API data is used (see `privacy.ts`). */\n policy?: AiProviderPolicy;\n /**\n * Cloud presets: the provider's official API pricing page, linked as\n * \"Pricing\" beside the key link. Only the link: prices change, so no\n * figure is stated in code. URLs checked 2026-10-29.\n */\n pricingUrl?: string;\n /**\n * Local presets: the official download or install page, for a server that\n * is not running yet (\"Install\", then Look again). URLs checked 2026-10-29.\n */\n installUrl?: string;\n}\n\n/**\n * A provider's official privacy or API data-use page. Only the link: what a\n * provider does with the text is set by its terms, which change, so no claim\n * about training or retention is made in code. URLs checked 2026-10-29.\n */\nexport interface AiProviderPolicy {\n /** Link text (\"Anthropic privacy and data use\"). */\n label: string;\n url: string;\n /** One extra neutral sentence, for a provider that passes requests on (OpenRouter). */\n note?: string;\n}\n\nexport interface AiPresetFamilyInfo {\n family: AiPresetFamily;\n /** Group heading over the quick-start cards. */\n label: string;\n /** The \"two ways to connect\" line for this family. */\n explainer: string;\n}\n\n/**\n * The two quick-start groups, in display order, with the \"two ways to\n * connect\" copy. The `host` family is not in this list: it shows only on a\n * host whose `detectHostEngines` reports a model (`AI_HOST_PRESET_FAMILY`).\n */\nexport const AI_PRESET_FAMILIES: readonly AiPresetFamilyInfo[] = [\n {\n family: 'local',\n label: 'Run on this machine',\n explainer: 'Your own model server, on this computer or your network. Text never leaves it.',\n },\n {\n family: 'cloud',\n label: 'Cloud API key',\n explainer: 'Paste a key from the provider. It bills your account for what you use.',\n },\n];\n\n/** The `host` group (VS Code's built-in models), for the hosts that report one through `detectHostEngines`. */\nexport const AI_HOST_PRESET_FAMILY: AiPresetFamilyInfo = {\n family: 'host',\n label: 'Built into VS Code',\n explainer: 'A model VS Code already offers through your GitHub Copilot plan. No key to paste; text goes to GitHub’s Copilot service.',\n};\n\n/** Version numbers come before a dated suffix, so `-20250514` never outranks `-4-5`. */\nconst anthropicFamily = (word: string, label: string): AiPopularRule => ({ label, match: new RegExp(`^claude-${word}-\\\\d`) });\n\nexport const AI_ENGINE_PRESETS: readonly AiEnginePreset[] = [\n {\n id: 'omlx',\n name: 'oMLX',\n kind: 'openai-compatible',\n baseUrl: 'http://127.0.0.1:8000/v1',\n apiKeyEnv: 'OMLX_API_KEY',\n apiKeyFile: { path: '~/.omlx/settings.json', jsonPath: 'auth.api_key' },\n needsKey: true,\n family: 'local',\n caption: 'MLX models on Apple silicon',\n keyHint: 'oMLX keeps its key in ~/.omlx/settings.json, which is read automatically.',\n installUrl: 'https://omlx.ai',\n },\n {\n id: 'lmstudio',\n name: 'LM Studio',\n kind: 'lmstudio',\n baseUrl: 'http://127.0.0.1:1234/v1',\n needsKey: false,\n family: 'local',\n caption: 'The LM Studio app’s local server',\n keyHint: 'Only needed if you turned on authentication in LM Studio.',\n installUrl: 'https://lmstudio.ai/download',\n detectPath: '/api/v0/models',\n },\n {\n id: 'ollama',\n name: 'Ollama',\n kind: 'ollama',\n baseUrl: 'http://127.0.0.1:11434/v1',\n needsKey: false,\n family: 'local',\n caption: 'Models pulled with ollama',\n keyHint: 'Ollama needs no key.',\n installUrl: 'https://ollama.com/download',\n detectPath: '/api/tags',\n },\n {\n id: 'llamacpp',\n name: 'llama.cpp server',\n kind: 'openai-compatible',\n baseUrl: 'http://127.0.0.1:8080/v1',\n needsKey: false,\n family: 'local',\n caption: 'llama-server with a GGUF model',\n keyHint: 'Only needed if you started llama-server with --api-key.',\n installUrl: 'https://github.com/ggml-org/llama.cpp/blob/master/docs/install.md',\n },\n {\n id: 'vllm',\n name: 'vLLM',\n kind: 'openai-compatible',\n baseUrl: 'http://127.0.0.1:8000/v1',\n apiKeyEnv: 'VLLM_API_KEY',\n needsKey: false,\n family: 'local',\n caption: 'vllm serve on a GPU box',\n keyHint: 'Only needed if you started vLLM with --api-key.',\n installUrl: 'https://docs.vllm.ai/en/latest/getting_started/installation/',\n },\n {\n id: 'custom',\n name: 'Custom OpenAI-compatible',\n kind: 'openai-compatible',\n baseUrl: 'http://127.0.0.1:8000/v1',\n needsKey: false,\n family: 'local',\n caption: 'Any server that speaks the OpenAI API',\n keyHint: 'Leave blank if the server has no auth.',\n },\n {\n id: 'anthropic',\n name: 'Anthropic',\n kind: 'anthropic',\n baseUrl: 'https://api.anthropic.com',\n apiKeyEnv: 'ANTHROPIC_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'Claude models, API key from Anthropic',\n keyHint: 'Get an API key in the Claude Console',\n keyUrl: 'https://platform.claude.com/settings/keys',\n pricingUrl: 'https://platform.claude.com/docs/en/about-claude/pricing',\n policy: { label: 'Anthropic privacy and data use', url: 'https://privacy.claude.com/en/collections/10663361-commercial-customers' },\n popular: [anthropicFamily('sonnet', 'Sonnet'), anthropicFamily('opus', 'Opus'), anthropicFamily('haiku', 'Haiku')],\n },\n {\n id: 'openai',\n name: 'OpenAI',\n kind: 'openai',\n baseUrl: 'https://api.openai.com/v1',\n apiKeyEnv: 'OPENAI_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'GPT models, API key from OpenAI',\n keyHint: 'Get an API key on the OpenAI platform',\n keyUrl: 'https://platform.openai.com/api-keys',\n pricingUrl: 'https://developers.openai.com/api/docs/pricing',\n policy: { label: 'OpenAI API data controls', url: 'https://developers.openai.com/api/docs/guides/your-data' },\n popular: [\n { label: 'Flagship', match: /^gpt-\\d+(\\.\\d+)?$/ },\n { label: 'Mini', match: /^gpt-\\d+(\\.\\d+)?-mini$/ },\n ],\n },\n {\n id: 'openrouter',\n name: 'OpenRouter',\n kind: 'openai-compatible',\n baseUrl: 'https://openrouter.ai/api/v1',\n apiKeyEnv: 'OPENROUTER_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'Models from many providers with one key',\n keyHint: 'Get an API key on OpenRouter',\n keyUrl: 'https://openrouter.ai/keys',\n pricingUrl: 'https://openrouter.ai/pricing',\n policy: {\n label: 'OpenRouter privacy policy',\n url: 'https://openrouter.ai/privacy',\n note: 'OpenRouter passes each request on to the provider that serves the model, whose own terms apply as well.',\n },\n popular: [\n { label: 'Claude Sonnet', match: /^anthropic\\/claude-sonnet-\\d/ },\n { label: 'GPT', match: /^openai\\/gpt-\\d+(\\.\\d+)?$/ },\n { label: 'Gemini Pro', match: /^google\\/gemini-\\d+(\\.\\d+)?-pro$/ },\n { label: 'Gemini Flash', match: /^google\\/gemini-\\d+(\\.\\d+)?-flash$/ },\n ],\n },\n {\n id: 'gemini',\n name: 'Google Gemini',\n kind: 'openai-compatible',\n baseUrl: 'https://generativelanguage.googleapis.com/v1beta/openai',\n apiKeyEnv: 'GEMINI_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'Gemini models, API key from Google AI Studio',\n keyHint: 'Get an API key in Google AI Studio',\n keyUrl: 'https://aistudio.google.com/apikey',\n pricingUrl: 'https://ai.google.dev/gemini-api/docs/pricing',\n policy: { label: 'Gemini API terms and data use', url: 'https://ai.google.dev/gemini-api/terms' },\n popular: [\n { label: 'Flash', match: /(^|\\/)gemini-\\d+(\\.\\d+)?-flash$/ },\n { label: 'Pro', match: /(^|\\/)gemini-\\d+(\\.\\d+)?-pro$/ },\n { label: 'Flash-Lite', match: /(^|\\/)gemini-\\d+(\\.\\d+)?-flash-lite$/ },\n ],\n },\n {\n id: 'groq',\n name: 'Groq',\n kind: 'openai-compatible',\n baseUrl: 'https://api.groq.com/openai/v1',\n apiKeyEnv: 'GROQ_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'Fast open models such as Llama and Qwen',\n keyHint: 'Get an API key in the Groq console',\n keyUrl: 'https://console.groq.com/keys',\n pricingUrl: 'https://console.groq.com/docs/models',\n policy: { label: 'Groq: your data in GroqCloud', url: 'https://console.groq.com/docs/your-data' },\n popular: [\n { label: 'GPT-OSS', match: /^openai\\/gpt-oss-\\d+b$/ },\n { label: 'Llama', match: /^(meta-llama\\/)?llama-\\d/, exclude: /guard|prompt/ },\n { label: 'Qwen', match: /^qwen\\/qwen\\d/ },\n ],\n },\n {\n id: 'mistral',\n name: 'Mistral',\n kind: 'openai-compatible',\n baseUrl: 'https://api.mistral.ai/v1',\n apiKeyEnv: 'MISTRAL_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'Mistral models, API key from Mistral',\n keyHint: 'Get an API key in the Mistral console',\n keyUrl: 'https://console.mistral.ai/api-keys',\n pricingUrl: 'https://mistral.ai/pricing',\n policy: { label: 'Mistral AI privacy policy', url: 'https://legal.mistral.ai/terms/privacy-policy/' },\n popular: [\n { label: 'Medium', match: /^mistral-medium-/, prefer: /-latest$/ },\n { label: 'Large', match: /^mistral-large-/, prefer: /-latest$/ },\n { label: 'Small', match: /^mistral-small-/, prefer: /-latest$/ },\n ],\n },\n {\n id: 'deepseek',\n name: 'DeepSeek',\n kind: 'openai-compatible',\n baseUrl: 'https://api.deepseek.com/v1',\n apiKeyEnv: 'DEEPSEEK_API_KEY',\n needsKey: true,\n family: 'cloud',\n caption: 'DeepSeek chat and reasoning models',\n keyHint: 'Get an API key on the DeepSeek platform',\n keyUrl: 'https://platform.deepseek.com/api_keys',\n pricingUrl: 'https://api-docs.deepseek.com/quick_start/pricing',\n policy: { label: 'DeepSeek privacy policy', url: 'https://cdn.deepseek.com/policies/en-US/deepseek-privacy-policy.html' },\n popular: [\n { label: 'Chat', match: /^deepseek-chat/ },\n { label: 'Reasoner', match: /^deepseek-reasoner/ },\n ],\n },\n {\n id: 'vscodelm',\n name: 'GitHub Copilot',\n kind: 'vscode-lm',\n baseUrl: '',\n needsKey: false,\n family: 'host',\n caption: 'Copilot’s models in VS Code, no key needed',\n keyHint: 'No key: VS Code signs in to GitHub Copilot for you. Needs a Copilot plan and the GitHub Copilot Chat extension.',\n pricingUrl: 'https://github.com/features/copilot/plans',\n policy: {\n label: 'GitHub Copilot Trust Center',\n url: 'https://copilot.github.trust.page/',\n note: 'Text goes to GitHub’s Copilot service under your Copilot plan; individual and business plans handle data differently.',\n },\n },\n];\n\nfunction urlKey(url: string): string {\n return url.trim().toLowerCase().replace(/\\/+$/, '');\n}\n\n/**\n * The preset a saved engine most likely came from (engines do not store\n * one): same kind and address, then the same key variable as a tiebreak\n * (oMLX and vLLM share port 8000); else, for a cloud address, the preset on\n * the same host; else the one preset of a dedicated kind. Undefined when\n * nothing fits (a hand-made OpenAI-compatible engine).\n */\nexport function presetForEngine(\n engine: { kind: string; baseUrl: string; apiKeyEnv?: string },\n presets: readonly AiEnginePreset[] = AI_ENGINE_PRESETS,\n): AiEnginePreset | undefined {\n const sameKind = presets.filter((p) => p.kind === engine.kind);\n const exact = sameKind.filter((p) => urlKey(p.baseUrl) === urlKey(engine.baseUrl));\n if (exact.length > 0) {\n return exact.find((p) => p.apiKeyEnv && p.apiKeyEnv === engine.apiKeyEnv)\n ?? exact.find((p) => !p.apiKeyEnv && !engine.apiKeyEnv)\n ?? exact[0];\n }\n let host = '';\n try { host = new URL(engine.baseUrl.trim()).hostname.toLowerCase(); } catch { /* not a URL yet */ }\n if (host) {\n const sameHost = sameKind.find((p) => p.family === 'cloud' && new URL(p.baseUrl).hostname.toLowerCase() === host);\n if (sameHost) {return sameHost;}\n }\n return engine.kind !== 'openai-compatible' && sameKind.length === 1 ? sameKind[0] : undefined;\n}\n\n/** Local server detection waits this long for each address. */\nexport const AI_LOCAL_DETECT_TIMEOUT_MS = 1500;\n\n/** What `detectLocalServers` is asked about: every local preset but Custom, at its default address. */\nexport function localDetectCandidates(presets: readonly AiEnginePreset[] = AI_ENGINE_PRESETS): { presetId: string; baseUrl: string }[] {\n return presets.filter((p) => p.family === 'local' && p.id !== 'custom').map((p) => ({ presetId: p.id, baseUrl: p.baseUrl }));\n}\n","/**\n * Pure parsers for discovery responses, one per endpoint shape. Hosts do the\n * HTTP and hand the parsed JSON here, so the VS Code adapters and the Rust\n * port read the same fixtures (`test/ai-fixtures/discovery/`). Every parser\n * tolerates junk: entries without a string id are skipped, never thrown on.\n */\n\nimport { AI_ENGINE_PRESETS, type AiEnginePreset } from './presets';\nimport type { AiApiKeyFile, AiDiscoveredModel, AiEngineKind, AiModelCapabilities } from './types';\n\ntype Json = Record<string, unknown>;\n\nfunction isObject(v: unknown): v is Json {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction dataArray(body: unknown, key = 'data'): Json[] {\n if (!isObject(body) || !Array.isArray(body[key])) {return [];}\n return (body[key] as unknown[]).filter(isObject);\n}\n\n/** An architecture name that says a speculative-decoding drafter (`qwen3_5_mtp`): weights for a target model, not a chat model. */\nfunction drafterArch(v: unknown): boolean {\n return typeof v === 'string' && /_mtp$/i.test(v);\n}\n\n/** An architecture name that says mixture of experts (`qwen3_5_moe`, `qwen35moe`, `nemotron_h_moe`). */\nfunction moeArch(v: unknown): boolean {\n return typeof v === 'string' && /moe/i.test(v);\n}\n\nfunction positiveInt(v: unknown): number | undefined {\n return typeof v === 'number' && Number.isFinite(v) && v > 0 ? Math.floor(v) : undefined;\n}\n\n/** `GET {base}/models` on any OpenAI-compatible server: ids only, capabilities from heuristics. */\nexport function parseOpenAiModelList(body: unknown): AiDiscoveredModel[] {\n return dataArray(body)\n .filter((m) => typeof m.id === 'string' && m.id)\n .map((m) => ({ id: m.id as string, capabilities: {}, source: 'heuristic' as const }));\n}\n\n/**\n * LM Studio `GET /api/v0/models`: `type` (`llm`, `vlm`, `embeddings`),\n * `max_context_length`, `state` (`loaded` / `not-loaded`), `arch` (a `moe`\n * in it marks a mixture of experts; one ending `_mtp` is a speculative-decoding\n * drafter, not a chat model), and on newer builds `capabilities`\n * (`tool_use`).\n */\nexport function parseLmStudioModels(body: unknown): AiDiscoveredModel[] {\n const out: AiDiscoveredModel[] = [];\n for (const m of dataArray(body)) {\n if (typeof m.id !== 'string' || !m.id) {continue;}\n const caps: Partial<AiModelCapabilities> = {};\n if (m.type === 'embeddings') {\n Object.assign(caps, { chat: false, stream: false, vision: false, tools: false, json: false, embed: true });\n } else if (m.type === 'vlm') {\n Object.assign(caps, { chat: true, vision: true, embed: false });\n } else if (m.type === 'llm') {\n Object.assign(caps, { chat: true, vision: false, embed: false });\n }\n if (Array.isArray(m.capabilities) && m.capabilities.includes('tool_use')) {caps.tools = true;}\n // An MTP drafter (`arch` ends `_mtp`) is only the draft head of a bigger model; its few\n // hundred MB are the true size, but it cannot chat, so it is never offered as one.\n if (drafterArch(m.arch)) {\n Object.assign(caps, { chat: false, stream: false, vision: false, tools: false, json: false, embed: false });\n }\n const ctx = positiveInt(m.max_context_length) ?? positiveInt(m.loaded_context_length);\n if (ctx) {caps.contextTokens = ctx;}\n const model: AiDiscoveredModel = { id: m.id, capabilities: caps, source: 'endpoint' };\n if (typeof m.state === 'string') {model.loaded = m.state === 'loaded';}\n if (moeArch(m.arch)) {model.moe = true;}\n out.push(model);\n }\n return out;\n}\n\n/**\n * oMLX `GET {base}/models/status` (beside its OpenAI `/models`): per model\n * `loaded` and `model_type` (`llm`, `vlm`, `embedding`, or a non-text type\n * such as `audio_stt` or `markitdown`, which can neither chat nor embed),\n * `model_context_length`, `estimated_size` (bytes) and `config_model_type`\n * (a `moe` in it marks a mixture of experts; one ending `_mtp` is a\n * speculative-decoding drafter, not a chat model, though it says `llm`).\n */\nexport function parseOmlxModelStatus(body: unknown): AiDiscoveredModel[] {\n const out: AiDiscoveredModel[] = [];\n for (const m of dataArray(body, 'models')) {\n if (typeof m.id !== 'string' || !m.id) {continue;}\n const caps: Partial<AiModelCapabilities> = {};\n if (m.model_type === 'llm') {\n Object.assign(caps, { chat: true, vision: false, embed: false });\n } else if (m.model_type === 'vlm') {\n Object.assign(caps, { chat: true, vision: true, embed: false });\n } else if (m.model_type === 'embedding' || m.model_type === 'embeddings') {\n Object.assign(caps, { chat: false, stream: false, vision: false, tools: false, json: false, embed: true });\n } else if (typeof m.model_type === 'string') {\n Object.assign(caps, { chat: false, stream: false, vision: false, tools: false, json: false, embed: false });\n }\n if (drafterArch(m.config_model_type)) {\n Object.assign(caps, { chat: false, stream: false, vision: false, tools: false, json: false, embed: false });\n }\n const ctx = positiveInt(m.model_context_length);\n if (ctx) {caps.contextTokens = ctx;}\n const model: AiDiscoveredModel = { id: m.id, capabilities: caps, source: Object.keys(caps).length > 0 ? 'endpoint' : 'heuristic' };\n if (typeof m.loaded === 'boolean') {model.loaded = m.loaded;}\n const size = positiveInt(m.estimated_size);\n if (size) {model.sizeBytes = size;}\n if (moeArch(m.config_model_type)) {model.moe = true;}\n out.push(model);\n }\n return out;\n}\n\n/**\n * Fold a status listing (`parseOmlxModelStatus`) into the models `/models`\n * listed: the listing decides which models exist, the status adds what it\n * knows about each (capabilities, loaded state).\n */\nexport function withModelStatus(listed: readonly AiDiscoveredModel[], status: readonly AiDiscoveredModel[]): AiDiscoveredModel[] {\n const byId = new Map(status.map((s) => [s.id, s]));\n return listed.map((m) => {\n const s = byId.get(m.id);\n if (!s) {return m;}\n const next: AiDiscoveredModel = { ...m, capabilities: { ...m.capabilities, ...s.capabilities } };\n if (s.source === 'endpoint') {next.source = 'endpoint';}\n if (s.loaded !== undefined) {next.loaded = s.loaded;}\n if (s.sizeBytes !== undefined) {next.sizeBytes = s.sizeBytes;}\n if (s.moe) {next.moe = true;}\n return next;\n });\n}\n\n/**\n * Sizes in bytes by model id from a listing that has them: LM Studio\n * `GET /api/v1/models` (`models[].key`, `size_bytes`; its `/api/v0` has\n * none) and Ollama `GET /api/tags` (`models[].model` or `name`, `size`).\n */\nexport function parseModelSizes(body: unknown): Map<string, number> {\n const out = new Map<string, number>();\n for (const m of dataArray(body, 'models')) {\n const id = [m.key, m.model, m.name].find((v): v is string => typeof v === 'string' && v.length > 0);\n const size = positiveInt(m.size_bytes) ?? positiveInt(m.size);\n if (id && size) {out.set(id, size);}\n }\n return out;\n}\n\n/** `models` with the sizes `parseModelSizes` found added; the rest as they were. */\nexport function withModelSizes(models: readonly AiDiscoveredModel[], sizes: ReadonlyMap<string, number>): AiDiscoveredModel[] {\n return models.map((m) => {\n const size = sizes.get(m.id);\n return size ? { ...m, sizeBytes: size } : m;\n });\n}\n\n/** Ollama `GET /api/tags`: the model names to ask `/api/show` about. */\nexport function parseOllamaTags(body: unknown): string[] {\n return dataArray(body, 'models')\n .map((m) => (typeof m.model === 'string' && m.model ? m.model : m.name))\n .filter((n): n is string => typeof n === 'string' && n.length > 0);\n}\n\n/**\n * Ollama `POST /api/show` for one model: `capabilities` (`completion`,\n * `vision`, `tools`, `embedding`, `insert`) and the context length under\n * `model_info[\"<arch>.context_length\"]`.\n */\nexport function parseOllamaShow(name: string, body: unknown): AiDiscoveredModel {\n const caps: Partial<AiModelCapabilities> = {};\n if (!isObject(body)) {return { id: name, capabilities: caps, source: 'heuristic' };}\n if (Array.isArray(body.capabilities)) {\n const list = body.capabilities.filter((c): c is string => typeof c === 'string');\n caps.chat = list.includes('completion');\n caps.stream = caps.chat;\n caps.vision = list.includes('vision');\n caps.tools = list.includes('tools');\n caps.embed = list.includes('embedding');\n caps.fim = list.includes('insert');\n }\n if (isObject(body.model_info)) {\n const info = body.model_info;\n const arch = typeof info['general.architecture'] === 'string' ? info['general.architecture'] : null;\n const ctx =\n (arch ? positiveInt(info[`${arch}.context_length`]) : undefined) ??\n positiveInt(Object.entries(info).find(([k]) => k.endsWith('.context_length'))?.[1]);\n if (ctx) {caps.contextTokens = ctx;}\n }\n return { id: name, capabilities: caps, source: Object.keys(caps).length > 0 ? 'endpoint' : 'heuristic' };\n}\n\n/** Which Ollama models are loaded right now, from `GET /api/ps` (optional). */\nexport function parseOllamaRunning(body: unknown): Set<string> {\n return new Set(parseOllamaTags(body));\n}\n\n/**\n * Anthropic `GET /v1/models`: `display_name`, `max_input_tokens` (the context\n * window) and the `capabilities` tree (`image_input.supported`,\n * `structured_outputs.supported`). Tools are assumed on.\n */\nexport function parseAnthropicModels(body: unknown): AiDiscoveredModel[] {\n const out: AiDiscoveredModel[] = [];\n for (const m of dataArray(body)) {\n if (typeof m.id !== 'string' || !m.id) {continue;}\n const caps: Partial<AiModelCapabilities> = { chat: true, stream: true, tools: true, fim: false, embed: false };\n const ctx = positiveInt(m.max_input_tokens);\n if (ctx) {caps.contextTokens = ctx;}\n const tree = isObject(m.capabilities) ? m.capabilities : null;\n const supported = (key: string): boolean | undefined => {\n const node = tree?.[key];\n return isObject(node) && typeof node.supported === 'boolean' ? node.supported : undefined;\n };\n const vision = supported('image_input');\n if (vision !== undefined) {caps.vision = vision;}\n const json = supported('structured_outputs');\n if (json !== undefined) {caps.json = json;}\n const model: AiDiscoveredModel = { id: m.id, capabilities: caps, source: 'endpoint' };\n if (typeof m.display_name === 'string' && m.display_name) {model.label = m.display_name;}\n out.push(model);\n }\n return out;\n}\n\n/** Anthropic pages its list; the next `after_id`, or null on the last page. */\nexport function anthropicNextPage(body: unknown): string | null {\n return isObject(body) && body.has_more === true && typeof body.last_id === 'string' ? body.last_id : null;\n}\n\n// ── Local server detection ──────────────────────────────────────────────────\n\n/** The URL detection GETs: `detectPath` under the server root (no `/v1`), else `{baseUrl}/models`. */\nexport function localDetectUrl(baseUrl: string, detectPath?: string): string {\n const base = baseUrl.trim().replace(/\\/+$/, '');\n return detectPath ? `${base.replace(/\\/v1$/, '')}${detectPath}` : `${base}/models`;\n}\n\n/**\n * How many models a detection answer lists: `data` (OpenAI-style, LM Studio)\n * or `models` (Ollama). LM Studio's embedding models are left out. Undefined\n * for a body of neither shape (the server answered, the count is unknown).\n */\nexport function countDetectedModels(body: unknown): number | undefined {\n if (!isObject(body)) {return undefined;}\n if (Array.isArray(body.data)) {return dataArray(body).filter((m) => m.type !== 'embeddings').length;}\n if (Array.isArray(body.models)) {return dataArray(body, 'models').length;}\n return undefined;\n}\n\n/** What a host needs to probe one detection candidate. */\nexport interface AiLocalDetectTarget {\n presetId: string;\n baseUrl: string;\n /** The GET. */\n url: string;\n /** For the auth header shape. */\n kind: AiEngineKind;\n /** Where to read a key, only when `baseUrl` is the preset's own address. */\n apiKeyEnv?: string;\n apiKeyFile?: AiApiKeyFile;\n}\n\n/**\n * A detection candidate checked against the presets. Null for an unknown or\n * cloud preset, Custom, or an address that is not http(s). A key source\n * (oMLX's key file) comes along only when the address is the preset's own,\n * so a key never goes anywhere the user did not set up.\n */\nexport function localDetectTarget(\n candidate: { presetId: string; baseUrl: string },\n presets: readonly AiEnginePreset[] = AI_ENGINE_PRESETS,\n): AiLocalDetectTarget | null {\n const preset = presets.find((p) => p.id === candidate.presetId);\n if (!preset || preset.family !== 'local' || preset.id === 'custom') {return null;}\n const baseUrl = typeof candidate.baseUrl === 'string' ? candidate.baseUrl.trim() : '';\n if (!/^https?:\\/\\/[^/]/i.test(baseUrl)) {return null;}\n const target: AiLocalDetectTarget = { presetId: preset.id, baseUrl, url: localDetectUrl(baseUrl, preset.detectPath), kind: preset.kind };\n const own = baseUrl.replace(/\\/+$/, '').toLowerCase() === preset.baseUrl.replace(/\\/+$/, '').toLowerCase();\n if (own && preset.apiKeyEnv) {target.apiKeyEnv = preset.apiKeyEnv;}\n if (own && preset.apiKeyFile) {target.apiKeyFile = { ...preset.apiKeyFile };}\n return target;\n}\n","/**\n * `mikedown.ai.*` settings: parse (validate, fill defaults, keep unknown\n * keys) and serialize back to a settings patch.\n *\n * `parseAiSettings` never throws. Malformed entries (an engine that is not\n * an object or has no base URL, a model with no id, a route without both\n * ids) are dropped with a warning; everything it does not recognize is kept\n * as is, on every object, so a newer version's settings survive a save.\n * Engines without an id get one, once: the id is written back by\n * `serializeAiSettings`, so it is stable from then on.\n */\n\nimport { completeCapabilities, detectCapabilities } from './capabilityHeuristics';\nimport { defaultAiSettings } from './defaults';\nimport { AI_ENGINE_KINDS, AI_FALLBACK_MODES, AI_HOST_ENGINE_KINDS } from './types';\nimport type {\n AiApiKeyFile,\n AiCapabilitiesSource,\n AiEngineConfig,\n AiEngineKind,\n AiFeatureSetting,\n AiFeatureSettings,\n AiLocalitySetting,\n AiModelCapabilities,\n AiModelConfig,\n AiRoute,\n AiRouting,\n AiSettings,\n} from './types';\n\ntype Json = Record<string, unknown>;\n\nexport interface ParseAiSettingsOptions {\n /** Id for an engine that has none. Default: a slug of its name plus random characters. */\n generateId?: (name: string) => string;\n /** Called once per dropped or repaired entry. Default `console.warn`. */\n onWarning?: (message: string) => void;\n}\n\n/** The top-level keys, without the `ai.` prefix. */\nexport const AI_SETTINGS_KEYS = ['enabled', 'allowCloud', 'engines', 'routing', 'features', 'localNetworks', 'showToolbarButton'] as const;\n\nexport { defaultAiSettings };\n\nexport function isKnownEngineKind(kind: string): kind is AiEngineKind {\n return (AI_ENGINE_KINDS as readonly string[]).includes(kind);\n}\n\n/** A kind only one host runs, with no address or key (`vscode-lm`); see `AI_HOST_ENGINE_KINDS`. */\nexport function isHostEngineKind(kind: string): boolean {\n return (AI_HOST_ENGINE_KINDS as readonly string[]).includes(kind);\n}\n\nfunction isObject(v: unknown): v is Json {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** Unknown keys of `raw`, copied, so known keys can be written over them. */\nfunction extras(raw: Json, known: readonly string[]): Json {\n const out: Json = {};\n for (const [k, v] of Object.entries(raw)) {\n if (!known.includes(k)) {out[k] = v;}\n }\n return out;\n}\n\nfunction randomSuffix(length: number): string {\n const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789';\n const bytes = new Uint8Array(length);\n const c = (globalThis as { crypto?: { getRandomValues?: (a: Uint8Array) => Uint8Array } }).crypto;\n if (c?.getRandomValues) {\n c.getRandomValues(bytes);\n } else {\n for (let i = 0; i < length; i++) {bytes[i] = Math.floor(Math.random() * 256);}\n }\n return Array.from(bytes, (b) => alphabet[b % alphabet.length]).join('');\n}\n\n/** A new engine id: `omlx-mac-studio-k3f9a2`. Never shown, only has to be unique and stable. */\nexport function generateAiId(name = ''): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 24)\n .replace(/-+$/, '');\n return slug ? `${slug}-${randomSuffix(6)}` : `engine-${randomSuffix(8)}`;\n}\n\n/**\n * Read `raw` as AI settings. Accepts the nested object VS Code returns for\n * `getConfiguration('mikedown.ai')` (`{ enabled, engines, ... }`) or a flat\n * store with `ai.`-prefixed keys (Desktop, a settings patch). Anything else,\n * including undefined, gives the defaults.\n */\nexport function parseAiSettings(raw: unknown, opts: ParseAiSettingsOptions = {}): AiSettings {\n const warn = opts.onWarning ?? ((m: string) => console.warn(`[mikedown ai] ${m}`));\n const genId = opts.generateId ?? generateAiId;\n const defaults = defaultAiSettings();\n if (!isObject(raw)) {\n if (raw !== undefined && raw !== null) {warn('AI settings are not an object; using defaults.');}\n return defaults;\n }\n\n const source = unflatten(raw);\n const out = { ...extras(source, AI_SETTINGS_KEYS), ...defaults } as AiSettings;\n\n if (typeof source.enabled === 'boolean') {out.enabled = source.enabled;}\n if (typeof source.allowCloud === 'boolean') {out.allowCloud = source.allowCloud;}\n if (typeof source.showToolbarButton === 'boolean') {out.showToolbarButton = source.showToolbarButton;}\n if (Array.isArray(source.localNetworks)) {\n out.localNetworks = source.localNetworks.filter((n): n is string => typeof n === 'string' && n.trim().length > 0);\n } else if (source.localNetworks !== undefined) {\n warn('localNetworks is not an array; using the default.');\n }\n\n out.engines = parseEngines(source.engines, genId, warn);\n out.routing = parseRouting(source.routing, warn);\n out.features = parseFeatures(source.features, warn);\n return out;\n}\n\n/** `{ 'ai.enabled': true, ... }` to `{ enabled: true, ... }`; a nested object passes through. */\nfunction unflatten(raw: Json): Json {\n const prefixed = Object.keys(raw).filter((k) => k.startsWith('ai.'));\n if (prefixed.length === 0) {return isObject(raw.ai) ? raw.ai : raw;}\n const out: Json = {};\n for (const k of prefixed) {out[k.slice(3)] = raw[k];}\n return out;\n}\n\nconst ENGINE_KEYS = ['id', 'name', 'kind', 'baseUrl', 'enabled', 'locality', 'apiKeyEnv', 'apiKeyFile', 'discovery', 'models'] as const;\n\n/** `{ path, jsonPath }` with both non-empty strings; unknown keys survive, like on engines. */\nfunction parseApiKeyFile(raw: unknown): AiApiKeyFile | undefined {\n if (!isObject(raw) || typeof raw.path !== 'string' || typeof raw.jsonPath !== 'string') {return undefined;}\n const path = raw.path.trim();\n const jsonPath = raw.jsonPath.trim();\n return path && jsonPath ? { ...extras(raw, ['path', 'jsonPath']), path, jsonPath } : undefined;\n}\n\nfunction parseEngines(raw: unknown, genId: (name: string) => string, warn: (m: string) => void): AiEngineConfig[] {\n if (raw === undefined) {return [];}\n if (!Array.isArray(raw)) {\n warn('engines is not an array; ignoring it.');\n return [];\n }\n const engines: AiEngineConfig[] = [];\n const ids = new Set<string>();\n raw.forEach((entry, index) => {\n if (!isObject(entry) || typeof entry.baseUrl !== 'string') {\n warn(`engines[${index}] has no baseUrl; dropped.`);\n return;\n }\n const name = typeof entry.name === 'string' && entry.name.trim() ? entry.name : entry.baseUrl;\n let kind = typeof entry.kind === 'string' ? entry.kind : 'openai-compatible';\n if (!kind.trim()) {kind = 'openai-compatible';}\n let id = typeof entry.id === 'string' && entry.id.trim() ? entry.id : '';\n if (!id || ids.has(id)) {\n if (id) {warn(`engines[${index}] repeats id \"${id}\"; gave it a new one.`);}\n do {\n id = genId(name);\n } while (ids.has(id));\n }\n ids.add(id);\n\n const engine = { ...extras(entry, ENGINE_KEYS) } as unknown as AiEngineConfig;\n engine.id = id;\n engine.name = name;\n // An unknown kind (a newer version's) is kept so the entry survives a save; the router skips it.\n engine.kind = kind as AiEngineKind;\n engine.baseUrl = entry.baseUrl.trim();\n engine.enabled = typeof entry.enabled === 'boolean' ? entry.enabled : true;\n engine.locality = entry.locality === 'local' || entry.locality === 'cloud' ? entry.locality : ('auto' as AiLocalitySetting);\n if (typeof entry.apiKeyEnv === 'string' && entry.apiKeyEnv.trim()) {engine.apiKeyEnv = entry.apiKeyEnv.trim();}\n if (entry.apiKeyFile !== undefined) {\n const file = parseApiKeyFile(entry.apiKeyFile);\n if (file) {engine.apiKeyFile = file;} else {warn(`engines[${index}].apiKeyFile needs a path and a jsonPath (both strings); dropped.`);}\n }\n engine.discovery = entry.discovery === 'manual' ? 'manual' : 'auto';\n engine.models = parseModels(entry.models, engine.kind, `engines[${index}]`, warn);\n engines.push(engine);\n });\n return engines;\n}\n\nconst MODEL_KEYS = ['id', 'label', 'enabled', 'capabilities', 'capabilitiesSource', 'requestOverrides', 'missing', 'manual'] as const;\nconst CAPABILITY_SOURCES: readonly AiCapabilitiesSource[] = ['endpoint', 'heuristic', 'user'];\n\nfunction parseModels(raw: unknown, kind: AiEngineKind, where: string, warn: (m: string) => void): AiModelConfig[] {\n if (raw === undefined) {return [];}\n if (!Array.isArray(raw)) {\n warn(`${where}.models is not an array; ignoring it.`);\n return [];\n }\n const models: AiModelConfig[] = [];\n const ids = new Set<string>();\n raw.forEach((entry, index) => {\n if (!isObject(entry) || typeof entry.id !== 'string' || !entry.id.trim()) {\n warn(`${where}.models[${index}] has no id; dropped.`);\n return;\n }\n if (ids.has(entry.id)) {\n warn(`${where}.models[${index}] repeats id \"${entry.id}\"; dropped.`);\n return;\n }\n ids.add(entry.id);\n\n const model = { ...extras(entry, MODEL_KEYS) } as unknown as AiModelConfig;\n model.id = entry.id;\n if (typeof entry.label === 'string' && entry.label.trim()) {model.label = entry.label;}\n model.enabled = typeof entry.enabled === 'boolean' ? entry.enabled : true;\n const known = isKnownEngineKind(kind) ? kind : undefined;\n if (isObject(entry.capabilities)) {\n model.capabilities = completeCapabilities(entry.capabilities as Partial<AiModelCapabilities>, entry.id, known);\n // An explicit null context window means \"unknown\"; keep it rather than the heuristic guess.\n if (entry.capabilities.contextTokens === null) {model.capabilities.contextTokens = null;}\n model.capabilitiesSource = CAPABILITY_SOURCES.includes(entry.capabilitiesSource as AiCapabilitiesSource)\n ? (entry.capabilitiesSource as AiCapabilitiesSource)\n : 'heuristic';\n } else {\n model.capabilities = detectCapabilities(entry.id, known).capabilities;\n model.capabilitiesSource = 'heuristic';\n }\n if (isObject(entry.requestOverrides)) {\n model.requestOverrides = entry.requestOverrides;\n } else if (entry.requestOverrides !== undefined) {\n warn(`${where}.models[${index}].requestOverrides is not an object; ignoring it.`);\n }\n if (entry.missing === true) {model.missing = true;}\n if (entry.manual === true) {model.manual = true;}\n models.push(model);\n });\n return models;\n}\n\nfunction parseRoute(raw: unknown): AiRoute | null {\n if (!isObject(raw) || typeof raw.engineId !== 'string' || typeof raw.modelId !== 'string' || !raw.engineId || !raw.modelId) {\n return null;\n }\n return { ...raw, engineId: raw.engineId, modelId: raw.modelId };\n}\n\nfunction parseRouting(raw: unknown, warn: (m: string) => void): AiRouting {\n const routing: AiRouting = { default: null, fallback: 'anyLocal', tasks: {} };\n if (raw === undefined) {return routing;}\n if (!isObject(raw)) {\n warn('routing is not an object; using the defaults.');\n return routing;\n }\n const out = { ...extras(raw, ['default', 'fallback', 'tasks']), ...routing } as AiRouting;\n if (raw.default !== undefined && raw.default !== null) {\n out.default = parseRoute(raw.default);\n if (!out.default) {warn('routing.default needs engineId and modelId; cleared.');}\n }\n if (typeof raw.fallback === 'string' && (AI_FALLBACK_MODES as readonly string[]).includes(raw.fallback)) {\n out.fallback = raw.fallback as AiRouting['fallback'];\n } else if (raw.fallback !== undefined) {\n warn(`routing.fallback \"${String(raw.fallback)}\" is not a fallback mode; using anyLocal.`);\n }\n if (isObject(raw.tasks)) {\n for (const [task, value] of Object.entries(raw.tasks)) {\n const route = parseRoute(value);\n if (route) {\n out.tasks[task] = route;\n } else if (value !== null) {\n warn(`routing.tasks.${task} needs engineId and modelId; dropped.`);\n }\n }\n } else if (raw.tasks !== undefined) {\n warn('routing.tasks is not an object; ignoring it.');\n }\n return out;\n}\n\nfunction parseFeatures(raw: unknown, warn: (m: string) => void): AiFeatureSettings {\n const features: AiFeatureSettings = {};\n if (raw === undefined) {return features;}\n if (!isObject(raw)) {\n warn('features is not an object; ignoring it.');\n return features;\n }\n for (const [id, value] of Object.entries(raw)) {\n if (!isObject(value)) {\n warn(`features.${id} is not an object; dropped.`);\n continue;\n }\n const feature = { ...extras(value, ['enabled', 'task']) } as AiFeatureSetting;\n if (typeof value.enabled === 'boolean') {feature.enabled = value.enabled;}\n if (typeof value.task === 'string' && value.task.trim()) {feature.task = value.task;}\n features[id] = feature;\n }\n return features;\n}\n\n/** The settings patch keys, `ai.`-prefixed: what the editor sends to `host.saveSettings`. */\nexport type AiSettingsPatch = Record<`ai.${string}`, unknown>;\n\n/**\n * Settings to a `saveSettings` patch: one `ai.<key>` per top-level key,\n * including unknown ones, as plain JSON (deep copies). VS Code writes each as\n * `mikedown.ai.<key>`; Desktop stores the flat keys. Secrets are never here.\n */\nexport function serializeAiSettings(settings: AiSettings): AiSettingsPatch {\n const patch: AiSettingsPatch = {};\n for (const [key, value] of Object.entries(settings)) {\n if (value === undefined) {continue;}\n patch[`ai.${key}`] = JSON.parse(JSON.stringify(value)) as unknown;\n }\n return patch;\n}\n","/**\n * Task routing: which engine and model serve a feature's request. Pure and\n * cheap, so the editor recomputes it on every request from the settings, the\n * status cache of the last `listEngines`, and the consent answers so far.\n *\n * 1. The master switch and the feature must be on.\n * 2. Route = `routing.tasks[task]`, else `routing.default`.\n * 3. A candidate is usable when the engine exists, is enabled and of a known\n * kind (a host kind such as `vscode-lm` only when the host lists a status\n * for it); the model exists, is enabled and not `missing`; the model has every\n * capability the task and feature need; the engine is local, or cloud with\n * `allowCloud` on and consent not refused; and its last probe did not fail.\n * 4. Otherwise `routing.fallback`: `none` stops, `default` tries the default\n * route, `anyLocal` then every local engine/model in registry order, `any`\n * then cloud engines through the same gate. Fallback never moves a local\n * route to a cloud engine unless the mode is `any`.\n * 5. The result names the pair and whether a fallback served it.\n *\n * A task may also `prefers` capabilities (JSON output for `structure`, a big\n * context for `summarize`). That is never a rejection reason. The chosen\n * route is honoured as is; when a fallback has several usable candidates in\n * the same tier (the default route and the local engines, then the cloud\n * ones), those meeting the preference come first, in registry order.\n *\n * Consent: a cloud candidate nobody has asked about yet is usable with\n * `needsConsent: true`; the caller asks (`host.ai.requestCloudConsent`),\n * records the answer and resolves again. A refusal (`false`) makes the\n * engine unusable, which lets the fallback run.\n */\n\nimport { resolveAiFeature } from './features';\nimport { engineLocality } from './locality';\nimport { isHostEngineKind, isKnownEngineKind } from './schema';\nimport { combineRequirements, getAiTask, missingCapabilities } from './tasks';\nimport type { AiCapabilityRequirements } from './tasks';\nimport type { AiCapabilityFlag, AiEngineConfig, AiEngineStatus, AiModelConfig, AiRoute, AiSettings } from './types';\n\n/** Consent answers by engine id: true granted, false refused (\"Not now\"), absent never asked. */\nexport type AiConsentState = Readonly<Record<string, boolean | undefined>>;\n\nexport type AiUnusableCode =\n | 'aiDisabled'\n | 'featureDisabled'\n | 'noRoute'\n | 'engineMissing'\n | 'engineDisabled'\n | 'engineUnsupported'\n | 'modelMissing'\n | 'modelDisabled'\n | 'modelNotListed'\n | 'capability'\n | 'cloudBlocked'\n | 'consentDenied'\n | 'probeFailed';\n\nexport interface AiRouteOk {\n engineId: string;\n modelId: string;\n viaFallback: boolean;\n /** Why the preferred route was passed over, when `viaFallback`. */\n reason?: string;\n /** The task that was routed. */\n task: string;\n isLocal: boolean;\n /** Cloud engine with no consent answer yet: ask before sending. */\n needsConsent: boolean;\n}\n\nexport interface AiRouteUnusable {\n unusable: AiUnusableCode;\n /** Tooltip copy, e.g. \"oMLX (Mac Studio) failed its last connection test\". */\n reason: string;\n task?: string;\n /** The engine the rejection is about (the routed one), when it is about one engine. */\n engineId?: string;\n}\n\nexport type AiRouteResolution = AiRouteOk | AiRouteUnusable;\n\nexport function isRouteUsable(r: AiRouteResolution): r is AiRouteOk {\n return !('unusable' in r);\n}\n\nexport interface ResolveRouteOptions {\n feature: string;\n /** Route this task instead of the feature's. */\n task?: string;\n}\n\ninterface Rejection {\n code: AiUnusableCode;\n reason: string;\n engineId?: string;\n}\n\ninterface Candidate {\n engine: AiEngineConfig;\n model: AiModelConfig;\n isLocal: boolean;\n needsConsent: boolean;\n}\n\nconst CAPABILITY_LABELS: Record<string, string> = {\n chat: 'chat',\n stream: 'streaming',\n vision: 'vision',\n json: 'JSON output',\n tools: 'tools',\n fim: 'fill-in-the-middle',\n embed: 'embeddings',\n contextTokens: 'a large enough context window',\n};\n\nfunction engineLabel(engine: AiEngineConfig): string {\n return engine.name || engine.id;\n}\n\nfunction modelLabel(model: AiModelConfig): string {\n return model.label || model.id;\n}\n\nexport function resolveRoute(\n settings: AiSettings,\n statuses: readonly AiEngineStatus[],\n consent: AiConsentState,\n opts: ResolveRouteOptions,\n): AiRouteResolution {\n // Step 1.\n if (!settings.enabled) {return { unusable: 'aiDisabled', reason: 'AI is turned off.' };}\n const feature = resolveAiFeature(settings.features, opts.feature);\n const task = opts.task ?? feature.task;\n if (!feature.enabled) {return { unusable: 'featureDisabled', reason: `${feature.definition?.label ?? opts.feature} is turned off.`, task };}\n\n const taskDef = getAiTask(task);\n const requires: AiCapabilityRequirements = combineRequirements(taskDef?.requires ?? {}, feature.requires);\n const requiresAny = taskDef?.requiresAny;\n const prefers = taskDef?.prefers;\n const statusById = new Map(statuses.map((s) => [s.engineId, s]));\n\n const check = (route: AiRoute): Candidate | Rejection => checkCandidate(settings, statusById, consent, route, requires, requiresAny);\n\n // Step 2.\n const preferred = settings.routing.tasks[task] ?? settings.routing.default;\n if (!preferred) {\n return fallbackFrom(null, { code: 'noRoute', reason: 'No engine and model is chosen for this task.' });\n }\n const first = check(preferred);\n if (!('code' in first)) {return ok(first, false);}\n return fallbackFrom(preferred, first);\n\n function ok(c: Candidate, viaFallback: boolean, reason?: string): AiRouteOk {\n const result: AiRouteOk = { engineId: c.engine.id, modelId: c.model.id, viaFallback, task, isLocal: c.isLocal, needsConsent: c.needsConsent };\n if (reason) {result.reason = reason;}\n return result;\n }\n\n // Step 4.\n function fallbackFrom(primary: AiRoute | null, rejection: Rejection): AiRouteResolution {\n const mode = settings.routing.fallback;\n if (mode === 'none') {return unusableFrom(rejection);}\n\n // A primary that is not a configured engine counts as local: no silent hop to cloud.\n const primaryEngine = primary ? settings.engines.find((e) => e.id === primary.engineId) : undefined;\n const primaryIsLocal = primaryEngine ? engineLocality(primaryEngine, settings.localNetworks) === 'local' : true;\n const allowsCloud = (c: Candidate): boolean => c.isLocal || !primaryIsLocal || mode === 'any';\n const tried = new Set<string>();\n if (primary) {tried.add(routeKey(primary));}\n\n // Tier one is the default route plus the local engines; the cloud ones come after.\n const tier: Candidate[] = [];\n const def = settings.routing.default;\n if (def && !tried.has(routeKey(def))) {\n tried.add(routeKey(def));\n const c = check(def);\n if (!('code' in c) && allowsCloud(c)) {tier.push(c);}\n }\n if (mode === 'default') {\n const only = preferFirst(tier, prefers);\n return only ? ok(only, true, rejection.reason) : unusableFrom(rejection);\n }\n\n const scan = (wantLocal: boolean): Candidate[] => {\n const found: Candidate[] = [];\n for (const engine of settings.engines) {\n for (const model of engine.models) {\n const route = { engineId: engine.id, modelId: model.id };\n if (tried.has(routeKey(route))) {continue;}\n const c = check(route);\n if ('code' in c || c.isLocal !== wantLocal) {continue;}\n found.push(c);\n }\n }\n return found;\n };\n\n const pick = preferFirst([...tier, ...scan(true)], prefers);\n if (pick) {return ok(pick, true, rejection.reason);}\n if (mode === 'any') {\n const cloud = preferFirst(scan(false), prefers);\n if (cloud) {return ok(cloud, true, rejection.reason);}\n }\n return unusableFrom(rejection);\n }\n\n function unusableFrom(rejection: Rejection): AiRouteUnusable {\n return { unusable: rejection.code, reason: rejection.reason, task, ...(rejection.engineId ? { engineId: rejection.engineId } : {}) };\n }\n}\n\n/** The first candidate that meets `prefers`, else the first one; null when there are none. */\nfunction preferFirst(candidates: readonly Candidate[], prefers: AiCapabilityRequirements | undefined): Candidate | null {\n if (candidates.length === 0) {return null;}\n if (prefers) {\n const match = candidates.find((c) => missingCapabilities(c.model.capabilities, prefers).length === 0);\n if (match) {return match;}\n }\n return candidates[0];\n}\n\nfunction routeKey(r: AiRoute): string {\n return `${r.engineId}\\u0000${r.modelId}`;\n}\n\nfunction checkCandidate(\n settings: AiSettings,\n statusById: ReadonlyMap<string, AiEngineStatus>,\n consent: AiConsentState,\n route: AiRoute,\n requires: AiCapabilityRequirements,\n requiresAny: readonly AiCapabilityFlag[] | undefined,\n): Candidate | Rejection {\n const engine = settings.engines.find((e) => e.id === route.engineId);\n if (!engine) {return { code: 'engineMissing', reason: 'The routed engine no longer exists.' };}\n const name = engineLabel(engine);\n const engineId = engine.id;\n if (!engine.enabled) {return { code: 'engineDisabled', reason: `${name} is turned off.`, engineId };}\n if (!isKnownEngineKind(engine.kind)) {return { code: 'engineUnsupported', reason: `${name} uses an engine kind this version does not support.` };}\n // A host kind (`vscode-lm`) runs only where the host reports the engine; elsewhere it is unsupported.\n const status = statusById.get(engine.id);\n if (isHostEngineKind(engine.kind) && !status) {return { code: 'engineUnsupported', reason: `${name} uses an engine kind this app does not support.` };}\n\n const model = engine.models.find((m) => m.id === route.modelId);\n if (!model) {return { code: 'modelMissing', reason: `${name} has no model \"${route.modelId}\".`, engineId };}\n if (!model.enabled) {return { code: 'modelDisabled', reason: `${modelLabel(model)} is turned off.` };}\n if (model.missing || status?.models.find((m) => m.id === model.id)?.missing) {\n return { code: 'modelNotListed', reason: `${name} no longer lists ${modelLabel(model)}.`, engineId };\n }\n\n const lacking = missingCapabilities(model.capabilities, requires, requiresAny);\n if (lacking.length > 0) {\n return { code: 'capability', reason: `${modelLabel(model)} lacks ${lacking.map((c) => CAPABILITY_LABELS[c] ?? c).join(', ')}.` };\n }\n\n const isLocal = engineLocality(engine, settings.localNetworks) === 'local';\n let needsConsent = false;\n if (!isLocal) {\n if (!settings.allowCloud) {return { code: 'cloudBlocked', reason: `${name} is a cloud engine and Allow cloud is off.` };}\n const answer = consent[engine.id];\n if (answer === false) {return { code: 'consentDenied', reason: `${name} was not allowed in this workspace.` };}\n needsConsent = answer !== true;\n }\n\n if (status?.ok === false) {\n return { code: 'probeFailed', reason: status.error ? `${name} failed its last connection test: ${status.error}` : `${name} failed its last connection test.`, engineId };\n }\n return { engine, model, isLocal, needsConsent };\n}\n","/**\n * Server-sent events, reassembled from arbitrary network chunks (a line, or\n * a multi-byte character, may be split across chunks; the host decodes bytes\n * with a streaming `TextDecoder` before pushing text here), plus readers for\n * the two completion stream shapes:\n *\n * - OpenAI Chat Completions (OpenAI, oMLX, LM Studio, Ollama `/v1`, vLLM,\n * llama.cpp): `data: {choices:[{delta:{content}}]}` chunks ending in\n * `data: [DONE]`. `reasoning_content` deltas are thinking, not text.\n * - Anthropic Messages: `message_start`, `content_block_delta` with\n * `text_delta`, `message_delta` with `stop_reason` and usage, `message_stop`,\n * and an `error` event.\n *\n * A stream that ends without its terminator (`[DONE]` or `message_stop`) is\n * truncated: `finishReason: 'error'`.\n */\n\nimport type { AiEngineKind, AiErrorCode, AiFinishReason, AiUsage } from './types';\n\nexport interface SseEvent {\n /** `event:` field; absent means the default `message`. */\n event?: string;\n data: string;\n id?: string;\n}\n\n/** Incremental SSE parser (WHATWG event-stream rules: CRLF/CR/LF, `:` comments, multi-line `data`). */\nexport class SseParser {\n private buffer = '';\n private started = false;\n private dataLines: string[] = [];\n private eventName: string | undefined;\n private lastId: string | undefined;\n\n /** Feed decoded text; returns the events it completed. */\n push(chunk: string): SseEvent[] {\n let text = chunk;\n if (!this.started && text.length > 0) {\n this.started = true;\n if (text.charCodeAt(0) === 0xfeff) {text = text.slice(1);}\n }\n this.buffer += text;\n const events: SseEvent[] = [];\n for (;;) {\n const match = /\\r\\n|\\r|\\n/.exec(this.buffer);\n if (!match) {break;}\n // A lone CR at the very end may be the first half of a CRLF split across chunks.\n if (match[0] === '\\r' && match.index === this.buffer.length - 1) {break;}\n const line = this.buffer.slice(0, match.index);\n this.buffer = this.buffer.slice(match.index + match[0].length);\n const event = this.line(line);\n if (event) {events.push(event);}\n }\n return events;\n }\n\n /** The stream closed: flush a final event that had no trailing blank line. */\n end(): SseEvent[] {\n const events: SseEvent[] = [];\n if (this.buffer.length > 0) {\n const rest = this.buffer.replace(/\\r$/, '');\n this.buffer = '';\n const event = this.line(rest);\n if (event) {events.push(event);}\n }\n const last = this.line('');\n if (last) {events.push(last);}\n return events;\n }\n\n private line(line: string): SseEvent | null {\n if (line === '') {\n if (this.dataLines.length === 0) {\n this.eventName = undefined;\n return null;\n }\n const event: SseEvent = { data: this.dataLines.join('\\n') };\n if (this.eventName) {event.event = this.eventName;}\n if (this.lastId !== undefined) {event.id = this.lastId;}\n this.dataLines = [];\n this.eventName = undefined;\n return event;\n }\n if (line.startsWith(':')) {return null;}\n const colon = line.indexOf(':');\n const field = colon === -1 ? line : line.slice(0, colon);\n let value = colon === -1 ? '' : line.slice(colon + 1);\n if (value.startsWith(' ')) {value = value.slice(1);}\n if (field === 'data') {\n this.dataLines.push(value);\n } else if (field === 'event') {\n this.eventName = value;\n } else if (field === 'id' && !value.includes('\\u0000')) {\n this.lastId = value;\n }\n return null;\n }\n}\n\n/** Where a completion stream ended up. */\nexport interface StreamOutcome {\n text: string;\n finishReason: AiFinishReason;\n usage?: AiUsage;\n /** The provider's own stop reason (`end_turn`, `length`, `refusal`...). */\n stopReason?: string;\n /** A stream-level error event, mapped to a code. */\n error?: { code: AiErrorCode; message: string };\n}\n\n/** One completion stream: push decoded chunks, get text deltas back, then `finish`. */\nexport interface CompletionStreamReader {\n push(chunk: string): string[];\n finish(): StreamOutcome;\n /**\n * Output seen so far, answer plus reasoning, in UTF-16 units: above zero\n * once the model starts generating (the host's first-token time), and the\n * basis of a token estimate when the engine reports no usage.\n */\n outputChars(): number;\n}\n\nfunction parseJson(data: string): Record<string, unknown> | null {\n try {\n const v: unknown = JSON.parse(data);\n return typeof v === 'object' && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\nfunction num(v: unknown): number | undefined {\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n}\n\nfunction errorCodeFromType(type: unknown): AiErrorCode {\n switch (type) {\n case 'authentication_error':\n case 'permission_error':\n case 'invalid_api_key':\n return 'auth';\n case 'not_found_error':\n case 'model_not_found':\n return 'modelNotFound';\n case 'rate_limit_error':\n case 'overloaded_error':\n case 'rate_limit_exceeded':\n return 'rateLimited';\n case 'context_length_exceeded':\n case 'request_too_large':\n return 'contextOverflow';\n default:\n return 'unknown';\n }\n}\n\nfunction streamError(raw: unknown): { code: AiErrorCode; message: string } {\n const e = typeof raw === 'object' && raw !== null ? (raw as Record<string, unknown>) : {};\n const message = typeof e.message === 'string' ? e.message : typeof raw === 'string' ? raw : 'The stream reported an error.';\n return { code: errorCodeFromType(e.type ?? e.code), message };\n}\n\nfunction mapOpenAiFinish(reason: string): AiFinishReason {\n if (reason === 'length') {return 'length';}\n if (reason === 'stop' || reason === 'tool_calls' || reason === 'function_call' || reason === 'eos') {return 'stop';}\n return 'error';\n}\n\nfunction mapAnthropicStop(reason: string): AiFinishReason {\n if (reason === 'max_tokens' || reason === 'model_context_window_exceeded') {return 'length';}\n if (reason === 'end_turn' || reason === 'stop_sequence' || reason === 'tool_use' || reason === 'pause_turn') {return 'stop';}\n return 'error';\n}\n\n/** Chat Completions stream (`openai-compatible`, `lmstudio`, `ollama`, `openai`). */\nexport function createOpenAiStreamReader(): CompletionStreamReader {\n const parser = new SseParser();\n let text = '';\n let reasoningChars = 0;\n let done = false;\n let stopReason: string | undefined;\n let usage: AiUsage | undefined;\n let error: StreamOutcome['error'];\n\n const handle = (events: SseEvent[]): string[] => {\n const deltas: string[] = [];\n for (const ev of events) {\n const data = ev.data.trim();\n if (data === '[DONE]') {\n done = true;\n continue;\n }\n const json = parseJson(data);\n if (!json) {continue;}\n if (json.error !== undefined) {\n error = streamError(json.error);\n continue;\n }\n const u = json.usage as Record<string, unknown> | null | undefined;\n if (u && typeof u === 'object') {\n usage = { promptTokens: num(u.prompt_tokens), completionTokens: num(u.completion_tokens) };\n }\n const choices = Array.isArray(json.choices) ? (json.choices as Array<Record<string, unknown>>) : [];\n for (const choice of choices) {\n const delta = choice.delta as Record<string, unknown> | undefined;\n const content = delta?.content;\n if (typeof content === 'string' && content) {\n text += content;\n deltas.push(content);\n }\n // oMLX, LM Studio and vLLM send `reasoning_content`; Ollama and OpenRouter `reasoning`.\n const reasoning = delta?.reasoning_content ?? delta?.reasoning;\n if (typeof reasoning === 'string') {reasoningChars += reasoning.length;}\n if (typeof choice.finish_reason === 'string' && choice.finish_reason) {stopReason = choice.finish_reason;}\n }\n }\n return deltas;\n };\n\n return {\n push: (chunk) => handle(parser.push(chunk)),\n outputChars: () => text.length + reasoningChars,\n finish() {\n handle(parser.end());\n let finishReason: AiFinishReason;\n if (error) {\n finishReason = 'error';\n } else if (!done) {\n finishReason = 'error';\n } else {\n finishReason = stopReason ? mapOpenAiFinish(stopReason) : 'stop';\n }\n const outcome: StreamOutcome = { text, finishReason };\n if (usage) {outcome.usage = usage;}\n if (stopReason) {outcome.stopReason = stopReason;}\n if (error) {outcome.error = error;}\n return outcome;\n },\n };\n}\n\n/** Anthropic Messages stream. */\nexport function createAnthropicStreamReader(): CompletionStreamReader {\n const parser = new SseParser();\n let text = '';\n let thinkingChars = 0;\n let done = false;\n let stopReason: string | undefined;\n let promptTokens: number | undefined;\n let completionTokens: number | undefined;\n let error: StreamOutcome['error'];\n\n const handle = (events: SseEvent[]): string[] => {\n const deltas: string[] = [];\n for (const ev of events) {\n const json = parseJson(ev.data);\n if (!json) {continue;}\n const type = typeof json.type === 'string' ? json.type : ev.event;\n switch (type) {\n case 'message_start': {\n const message = json.message as Record<string, unknown> | undefined;\n const u = message?.usage as Record<string, unknown> | undefined;\n promptTokens = num(u?.input_tokens) ?? promptTokens;\n completionTokens = num(u?.output_tokens) ?? completionTokens;\n break;\n }\n case 'content_block_delta': {\n const delta = json.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string' && delta.text) {\n text += delta.text;\n deltas.push(delta.text);\n } else if (delta?.type === 'thinking_delta' && typeof delta.thinking === 'string') {\n thinkingChars += delta.thinking.length;\n }\n break;\n }\n case 'message_delta': {\n const delta = json.delta as Record<string, unknown> | undefined;\n if (typeof delta?.stop_reason === 'string') {stopReason = delta.stop_reason;}\n const u = json.usage as Record<string, unknown> | undefined;\n promptTokens = num(u?.input_tokens) ?? promptTokens;\n completionTokens = num(u?.output_tokens) ?? completionTokens;\n break;\n }\n case 'message_stop':\n done = true;\n break;\n case 'error':\n error = streamError(json.error);\n break;\n default:\n break;\n }\n }\n return deltas;\n };\n\n return {\n push: (chunk) => handle(parser.push(chunk)),\n outputChars: () => text.length + thinkingChars,\n finish() {\n handle(parser.end());\n let finishReason: AiFinishReason;\n if (error || !done) {\n finishReason = 'error';\n } else {\n finishReason = stopReason ? mapAnthropicStop(stopReason) : 'stop';\n }\n const outcome: StreamOutcome = { text, finishReason };\n if (promptTokens !== undefined || completionTokens !== undefined) {outcome.usage = { promptTokens, completionTokens };}\n if (stopReason) {outcome.stopReason = stopReason;}\n if (error) {outcome.error = error;}\n return outcome;\n },\n };\n}\n\n/** The reader for an engine kind. */\nexport function createCompletionStreamReader(kind: AiEngineKind): CompletionStreamReader {\n return kind === 'anthropic' ? createAnthropicStreamReader() : createOpenAiStreamReader();\n}\n","/**\n * How long a result took and how fast it came: the popover footer's\n * \"2.4 s · 38 tok/s\", its tooltip, and the hosts' request log. Hosts measure\n * (`AiCompletionResult.timing`); this only does the arithmetic and the words.\n *\n * Output tokens are the engine's own count when it reports one (OpenAI-style\n * `usage` with `stream_options.include_usage`, Anthropic `message_delta`),\n * else about one token per four characters of output, marked approximate\n * (\"~38 tok/s\"). Speed is output tokens over the time from the first token to\n * the end; without a first-token time, over the whole request.\n *\n * Desktop's `request_log.rs` ports `aiTimingLogParts`; keep them in step.\n */\n\nimport type { AiCompletionResult } from './types';\n\n/** Characters per token for the estimate when the engine reports no usage. */\nexport const AI_CHARS_PER_TOKEN = 4;\n\nexport interface AiResultStats {\n totalMs: number;\n firstTokenMs?: number;\n outputTokens?: number;\n inputTokens?: number;\n /** `outputTokens` (and so the speed) is estimated from the characters. */\n approximate: boolean;\n tokensPerSecond?: number;\n}\n\nfunction positive(n: number | undefined): number | undefined {\n return typeof n === 'number' && Number.isFinite(n) && n > 0 ? n : undefined;\n}\n\n/** The numbers behind the footer, or null when the host measured nothing (an older host). */\nexport function aiResultStats(result: Pick<AiCompletionResult, 'text' | 'usage' | 'timing'>): AiResultStats | null {\n const t = result.timing;\n if (!t || typeof t.totalMs !== 'number' || !Number.isFinite(t.totalMs) || t.totalMs < 0) {return null;}\n const stats: AiResultStats = { totalMs: t.totalMs, approximate: false };\n if (typeof t.firstTokenMs === 'number' && Number.isFinite(t.firstTokenMs) && t.firstTokenMs >= 0) {stats.firstTokenMs = t.firstTokenMs;}\n // A zero count next to real text is a server that fills usage with zeros: estimate instead.\n const reported = positive(result.usage?.completionTokens);\n if (reported !== undefined) {\n stats.outputTokens = reported;\n } else {\n const chars = t.outputChars ?? result.text.length;\n if (chars > 0) {\n stats.outputTokens = Math.max(1, Math.round(chars / AI_CHARS_PER_TOKEN));\n stats.approximate = true;\n }\n }\n const input = positive(result.usage?.promptTokens);\n if (input !== undefined) {stats.inputTokens = input;}\n const first = stats.firstTokenMs;\n const generationMs = first !== undefined && t.totalMs > first ? t.totalMs - first : t.totalMs;\n if (stats.outputTokens !== undefined && generationMs > 0) {stats.tokensPerSecond = stats.outputTokens / (generationMs / 1000);}\n return stats;\n}\n\n/** \"0.8 s\", \"2.4 s\", \"12 s\". */\nexport function formatAiSeconds(ms: number): string {\n const s = ms / 1000;\n return s < 10 ? `${s.toFixed(1)} s` : `${Math.round(s)} s`;\n}\n\n/** \"38 tok/s\", \"~38 tok/s\", \"4.5 tok/s\". */\nexport function formatAiTokensPerSecond(tps: number, approximate: boolean): string {\n const n = tps < 10 ? tps.toFixed(1) : String(Math.round(tps));\n return `${approximate ? '~' : ''}${n} tok/s`;\n}\n\n/** The footer text: \"2.4 s · 38 tok/s\", or just \"2.4 s\" with no speed. */\nexport function aiResultStatsText(stats: AiResultStats): string {\n const time = formatAiSeconds(stats.totalMs);\n return stats.tokensPerSecond === undefined ? time : `${time} · ${formatAiTokensPerSecond(stats.tokensPerSecond, stats.approximate)}`;\n}\n\n/** The footer tooltip, one fact per line. */\nexport function aiResultStatsTitle(stats: AiResultStats): string {\n const lines: string[] = [];\n if (stats.firstTokenMs !== undefined) {lines.push(`First token after ${formatAiSeconds(stats.firstTokenMs)}`);}\n lines.push(`Total ${formatAiSeconds(stats.totalMs)}`);\n if (stats.outputTokens !== undefined) {\n lines.push(stats.approximate ? `About ${stats.outputTokens.toLocaleString('en-US')} output tokens (estimated from the text)` : `${stats.outputTokens.toLocaleString('en-US')} output tokens`);\n }\n if (stats.inputTokens !== undefined) {lines.push(`${stats.inputTokens.toLocaleString('en-US')} input tokens`);}\n if (stats.tokensPerSecond !== undefined) {lines.push(formatAiTokensPerSecond(stats.tokensPerSecond, stats.approximate));}\n return lines.join('\\n');\n}\n\n/** The request log's additions: `first token N ms` and the speed, when measured. */\nexport function aiTimingLogParts(result: Pick<AiCompletionResult, 'text' | 'usage' | 'timing'>): string[] {\n const stats = aiResultStats(result);\n if (!stats) {return [];}\n const parts: string[] = [];\n if (stats.firstTokenMs !== undefined) {parts.push(`first token ${Math.round(stats.firstTokenMs)} ms`);}\n if (stats.tokensPerSecond !== undefined) {parts.push(formatAiTokensPerSecond(stats.tokensPerSecond, stats.approximate));}\n return parts;\n}\n","/**\n * Request building per engine kind: endpoint URLs, auth headers and the\n * completion and embedding bodies. Pure, so the VS Code adapters and the Rust\n * port produce identical requests (checked against the same fixtures).\n *\n * Bodies:\n * - Chat Completions kinds: `{ model, messages, stream, max_tokens,\n * temperature, response_format }`; `openai` uses `max_completion_tokens`\n * and asks for usage in the stream. OpenAI reasoning models (o-series,\n * GPT-5 onwards) get no `temperature`: they accept only the default.\n * - `anthropic`: Messages API, system prompts lifted into `system`,\n * `max_tokens` always set (required). No sampling parameters: API version\n * 2026-10-21 rejects `temperature`.\n *\n * `model.requestOverrides` merge last, deeply (objects merge, everything else\n * replaces), so they win (OPEN-6); then the request's own `requestOverrides`\n * (a per-request thinking level) merge over those the same way.\n *\n * A 400 that names a parameter the model does not take\n * (`unsupportedRequestParam`) is retried once without it\n * (`withoutRequestParam`); the hosts remember it per engine and model for\n * the session.\n */\n\nimport { isOpenAiReasoningModel, openAiOffReasoningEffort } from './capabilityHeuristics';\nimport { detectLocality, hostOfUrl } from './locality';\nimport type { AiCompletionRequest, AiEngineConfig, AiEngineKind, AiMessage, AiMessagePart, AiModelConfig } from './types';\n\n/** `anthropic-version` sent by the Anthropic adapter. */\nexport const ANTHROPIC_API_VERSION = '2026-10-21';\n\n/** `max_tokens` for Anthropic when the request sets none (the API requires it). */\nexport const ANTHROPIC_DEFAULT_MAX_TOKENS = 4096;\n\n/** Appended to the system prompt for `format: 'json'` on kinds with no schema-free JSON mode. */\nexport const JSON_ONLY_INSTRUCTION = 'Respond with a single valid JSON value and nothing else.';\n\ntype Json = Record<string, unknown>;\n\n// ── Endpoints ───────────────────────────────────────────────────────────────\n\nexport interface AiEngineEndpoints {\n /** Streaming completion. */\n completions: string;\n /** Model list used for Test connection and bare discovery. */\n models: string;\n /** Embeddings, absent for kinds without them. */\n embeddings?: string;\n /** Native discovery endpoint (LM Studio `/api/v0/models`, Ollama `/api/tags`). */\n nativeModels?: string;\n /** Ollama `POST /api/show` (body `{ model }`). */\n nativeShow?: string;\n}\n\nfunction trimSlash(url: string): string {\n return url.trim().replace(/\\/+$/, '');\n}\n\n/**\n * URLs for an engine. OpenAI-style base URLs include `/v1`\n * (`http://127.0.0.1:8000/v1`); for LM Studio and Ollama a base without it\n * gets `/v1` added for completions, and their native endpoints hang off the\n * root. Anthropic's base is the host (`https://api.anthropic.com`), with or\n * without `/v1`.\n */\nexport function engineEndpoints(engine: Pick<AiEngineConfig, 'kind' | 'baseUrl'>): AiEngineEndpoints {\n const base = trimSlash(engine.baseUrl);\n const root = base.replace(/\\/v1$/, '');\n const v1 = `${root}/v1`;\n switch (engine.kind) {\n case 'anthropic':\n return { completions: `${v1}/messages`, models: `${v1}/models` };\n case 'lmstudio':\n return { completions: `${v1}/chat/completions`, models: `${v1}/models`, embeddings: `${v1}/embeddings`, nativeModels: `${root}/api/v0/models` };\n case 'ollama':\n return {\n completions: `${v1}/chat/completions`,\n models: `${v1}/models`,\n embeddings: `${v1}/embeddings`,\n nativeModels: `${root}/api/tags`,\n nativeShow: `${root}/api/show`,\n };\n default:\n return { completions: `${base}/chat/completions`, models: `${base}/models`, embeddings: `${base}/embeddings` };\n }\n}\n\n/** Headers for every request to the engine (JSON content type included). */\nexport function buildAuthHeaders(kind: AiEngineKind, apiKey: string | null): Record<string, string> {\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n if (kind === 'anthropic') {\n headers['anthropic-version'] = ANTHROPIC_API_VERSION;\n if (apiKey) {headers['x-api-key'] = apiKey;}\n } else if (apiKey) {\n headers.authorization = `Bearer ${apiKey}`;\n }\n return headers;\n}\n\n// ── Bodies ──────────────────────────────────────────────────────────────────\n\nfunction isPlainObject(v: unknown): v is Json {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** Deep merge: nested plain objects merge, anything else in `overrides` replaces. Inputs are not mutated. */\nexport function mergeRequestOverrides(body: Json, overrides: Json | undefined): Json {\n if (!overrides) {return body;}\n const out: Json = { ...body };\n for (const [key, value] of Object.entries(overrides)) {\n const current = out[key];\n out[key] = isPlainObject(current) && isPlainObject(value) ? mergeRequestOverrides(current, value) : value;\n }\n return out;\n}\n\nconst BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/** Standard base64 with padding, no line breaks. No `Buffer` or `btoa`, so it runs in every host. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n let out = '';\n let i = 0;\n for (; i + 2 < bytes.length; i += 3) {\n const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];\n out += BASE64_ALPHABET[(n >> 18) & 63] + BASE64_ALPHABET[(n >> 12) & 63] + BASE64_ALPHABET[(n >> 6) & 63] + BASE64_ALPHABET[n & 63];\n }\n const rest = bytes.length - i;\n if (rest === 1) {\n const n = bytes[i] << 16;\n out += `${BASE64_ALPHABET[(n >> 18) & 63]}${BASE64_ALPHABET[(n >> 12) & 63]}==`;\n } else if (rest === 2) {\n const n = (bytes[i] << 16) | (bytes[i + 1] << 8);\n out += `${BASE64_ALPHABET[(n >> 18) & 63]}${BASE64_ALPHABET[(n >> 12) & 63]}${BASE64_ALPHABET[(n >> 6) & 63]}=`;\n }\n return out;\n}\n\n/** Inverse of `bytesToBase64`; ignores whitespace and accepts URL-safe characters. */\nexport function base64ToBytes(b64: string): Uint8Array {\n const clean = b64.replace(/[\\s=]/g, '').replace(/-/g, '+').replace(/_/g, '/');\n const out = new Uint8Array(Math.floor((clean.length * 3) / 4));\n let bits = 0;\n let value = 0;\n let o = 0;\n for (const ch of clean) {\n const idx = BASE64_ALPHABET.indexOf(ch);\n if (idx === -1) {continue;}\n value = (value << 6) | idx;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[o++] = (value >> bits) & 0xff;\n }\n }\n return out.slice(0, o);\n}\n\nfunction textOf(content: AiMessage['content']): string {\n if (typeof content === 'string') {return content;}\n return content\n .filter((p): p is Extract<AiMessagePart, { type: 'text' }> => p.type === 'text')\n .map((p) => p.text)\n .join('\\n\\n');\n}\n\nfunction openAiContent(content: AiMessage['content']): string | Json[] {\n if (typeof content === 'string') {return content;}\n return content.map((part): Json =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : { type: 'image_url', image_url: { url: `data:${part.mime};base64,${bytesToBase64(part.bytes)}` } },\n );\n}\n\nfunction anthropicContent(content: AiMessage['content']): string | Json[] {\n if (typeof content === 'string') {return content;}\n return content.map((part): Json =>\n part.type === 'text'\n ? { type: 'text', text: part.text }\n : { type: 'image', source: { type: 'base64', media_type: part.mime, data: bytesToBase64(part.bytes) } },\n );\n}\n\nexport interface BuildCompletionOptions {\n /** Default true. */\n stream?: boolean;\n}\n\n/** The JSON body for one completion against `model` on an engine of `kind`. */\nexport function buildCompletionBody(kind: AiEngineKind, model: Pick<AiModelConfig, 'id' | 'requestOverrides'>, req: AiCompletionRequest, opts: BuildCompletionOptions = {}): Json {\n const stream = opts.stream ?? true;\n const body = kind === 'anthropic' ? anthropicBody(model.id, req, stream) : chatCompletionsBody(kind, model.id, req, stream);\n const merged = mergeRequestOverrides(body, model.requestOverrides);\n const final = isPlainObject(req.requestOverrides) ? mergeRequestOverrides(merged, req.requestOverrides) : merged;\n return kind === 'anthropic' ? final : acceptedOffEffort(kind, model.id, final);\n}\n\n/**\n * The Thinking dial's Off is stored as `reasoning_effort: \"none\"`, which\n * OpenAI's gpt-5 and o-series reject. For those models send the lowest value\n * the family accepts instead, so the first request does not fail (the\n * unsupported-parameter retry stays as the fallback).\n */\nfunction acceptedOffEffort(kind: AiEngineKind, modelId: string, body: Json): Json {\n if (body.reasoning_effort !== 'none') {return body;}\n const effort = openAiOffReasoningEffort(modelId, kind);\n return effort === null || effort === 'none' ? body : { ...body, reasoning_effort: effort };\n}\n\nfunction chatCompletionsBody(kind: AiEngineKind, modelId: string, req: AiCompletionRequest, stream: boolean): Json {\n const body: Json = {\n model: modelId,\n messages: req.messages.map((m) => ({ role: m.role, content: openAiContent(m.content) })),\n stream,\n };\n if (req.maxTokens !== undefined) {body[kind === 'openai' ? 'max_completion_tokens' : 'max_tokens'] = req.maxTokens;}\n if (req.temperature !== undefined && !isOpenAiReasoningModel(modelId, kind)) {body.temperature = req.temperature;}\n if (req.format === 'json') {\n // LM Studio accepts only `json_schema`; a permissive object schema is the schema-free equivalent.\n body.response_format =\n kind === 'lmstudio'\n ? { type: 'json_schema', json_schema: { name: 'response', schema: { type: 'object' } } }\n : { type: 'json_object' };\n }\n if (kind === 'openai' && stream) {body.stream_options = { include_usage: true };}\n return body;\n}\n\nfunction anthropicBody(modelId: string, req: AiCompletionRequest, stream: boolean): Json {\n const system = req.messages.filter((m) => m.role === 'system').map((m) => textOf(m.content));\n if (req.format === 'json') {system.push(JSON_ONLY_INSTRUCTION);}\n const body: Json = {\n model: modelId,\n max_tokens: req.maxTokens ?? ANTHROPIC_DEFAULT_MAX_TOKENS,\n messages: req.messages.filter((m) => m.role !== 'system').map((m) => ({ role: m.role, content: anthropicContent(m.content) })),\n stream,\n };\n if (system.length > 0) {body.system = system.join('\\n\\n');}\n return body;\n}\n\n// ── Usage in the stream ─────────────────────────────────────────────────────\n\n/**\n * Cloud hosts of the `openai-compatible` kind that take\n * `stream_options.include_usage` (OpenRouter, Gemini, Groq, DeepSeek).\n * Mistral and unknown cloud hosts are left out: a strict server may reject\n * a field it does not know.\n */\nexport const STREAM_USAGE_HOSTS: readonly string[] = ['openrouter.ai', 'generativelanguage.googleapis.com', 'api.groq.com', 'api.deepseek.com'];\n\n/**\n * Whether a streamed completion may ask for usage in the stream. `openai`,\n * LM Studio and Ollama take it; on `openai-compatible`, a server on this\n * machine or network (oMLX, vLLM, llama.cpp all take it) or a host in\n * `STREAM_USAGE_HOSTS`. Anthropic reports usage without being asked.\n */\nexport function streamUsageSupported(engine: Pick<AiEngineConfig, 'kind' | 'baseUrl'>): boolean {\n switch (engine.kind) {\n case 'openai':\n case 'lmstudio':\n case 'ollama':\n return true;\n case 'openai-compatible': {\n if (detectLocality(engine.baseUrl) === 'local') {return true;}\n const host = hostOfUrl(engine.baseUrl);\n return host !== null && STREAM_USAGE_HOSTS.includes(host);\n }\n default:\n return false;\n }\n}\n\n/**\n * `body` asking for usage in the stream (`stream_options.include_usage`)\n * when the engine takes it, the body streams and the overrides did not set\n * `stream_options` themselves. Hosts apply it after `buildCompletionBody`.\n */\nexport function withStreamUsage(body: Json, engine: Pick<AiEngineConfig, 'kind' | 'baseUrl'>): Json {\n if (body.stream !== true || 'stream_options' in body || !streamUsageSupported(engine)) {return body;}\n return { ...body, stream_options: { include_usage: true } };\n}\n\n// ── Unsupported parameters ──────────────────────────────────────────────────\n\n/**\n * Body fields a server may reject for one model while taking them for\n * others. Only these are retried: anything else in a 400 is a real error.\n */\nexport const RETRYABLE_REQUEST_PARAMS: readonly string[] = [\n 'temperature',\n 'top_p',\n 'presence_penalty',\n 'frequency_penalty',\n 'max_tokens',\n 'max_completion_tokens',\n 'reasoning_effort',\n 'stream_options',\n];\n\nconst UNSUPPORTED_WORDING = /unsupported|not supported|does not support|not compatible/i;\n\n/**\n * The parameter a failed completion was rejected for, or null. Only a 400\n * whose error says the parameter (or its value) is unsupported counts:\n * OpenAI's `{ error: { param, code: \"unsupported_value\" | \"unsupported_parameter\", message } }`,\n * or any body whose message quotes one of `RETRYABLE_REQUEST_PARAMS` next to\n * that wording (the first one quoted, so \"Use 'max_completion_tokens'\n * instead\" does not count).\n */\nexport function unsupportedRequestParam(status: number, bodyText: string): string | null {\n if (status !== 400) {return null;}\n let message = bodyText;\n let param: unknown;\n let code: unknown;\n try {\n const json: unknown = JSON.parse(bodyText);\n const error = isPlainObject(json) ? json.error : undefined;\n if (isPlainObject(error)) {\n message = typeof error.message === 'string' ? error.message : '';\n param = error.param;\n code = error.code;\n } else if (typeof error === 'string') {\n message = error;\n } else if (isPlainObject(json) && typeof json.message === 'string') {\n message = json.message;\n }\n } catch {\n // Not JSON: the text is the message.\n }\n if (!UNSUPPORTED_WORDING.test(`${typeof code === 'string' ? code : ''} ${message}`)) {return null;}\n if (typeof param === 'string' && param !== '') {return RETRYABLE_REQUEST_PARAMS.includes(param) ? param : null;}\n for (const match of message.matchAll(/['\"`]([a-z_]+)['\"`]/g)) {\n if (RETRYABLE_REQUEST_PARAMS.includes(match[1])) {return match[1];}\n }\n return null;\n}\n\n/**\n * `body` without a parameter the server rejected: `max_tokens` and\n * `max_completion_tokens` become each other (the documented replacement),\n * anything else is dropped. Null when the body does not carry it, so there\n * is nothing to retry.\n */\nexport function withoutRequestParam(body: Json, param: string): Json | null {\n if (!(param in body)) {return null;}\n const out: Json = { ...body };\n const value = out[param];\n delete out[param];\n const replacement = param === 'max_tokens' ? 'max_completion_tokens' : param === 'max_completion_tokens' ? 'max_tokens' : null;\n if (replacement && !(replacement in out)) {out[replacement] = value;}\n return out;\n}\n\n/** Embeddings body (`POST .../embeddings`), or null for kinds without an embeddings endpoint. */\nexport function buildEmbeddingsBody(kind: AiEngineKind, model: Pick<AiModelConfig, 'id' | 'requestOverrides'>, texts: string[]): Json | null {\n if (kind === 'anthropic') {return null;}\n return mergeRequestOverrides({ model: model.id, input: texts }, model.requestOverrides);\n}\n\n/** Vectors from an OpenAI-style embeddings response, in input order. */\nexport function parseEmbeddingsResponse(body: unknown): number[][] {\n if (!isPlainObject(body) || !Array.isArray(body.data)) {return [];}\n return (body.data as unknown[])\n .filter(isPlainObject)\n .map((d, i) => ({ index: typeof d.index === 'number' ? d.index : i, embedding: d.embedding }))\n .sort((a, b) => a.index - b.index)\n .map((d) => (Array.isArray(d.embedding) ? d.embedding.filter((x): x is number => typeof x === 'number') : []));\n}\n","/**\n * `AiCompletionRequest` over a JSON channel (VS Code `postMessage`, Tauri\n * `invoke`): image bytes travel as base64, the same way `savePastedImage`\n * sends pasted images.\n */\n\nimport { base64ToBytes, bytesToBase64 } from './requests';\nimport type { AiCompletionRequest, AiMessage } from './types';\n\nexport type AiWireMessagePart = { type: 'text'; text: string } | { type: 'image'; mime: string; dataBase64: string };\n\nexport interface AiWireMessage {\n role: AiMessage['role'];\n content: string | AiWireMessagePart[];\n}\n\nexport type AiWireCompletionRequest = Omit<AiCompletionRequest, 'messages'> & { messages: AiWireMessage[] };\n\nexport function toWireCompletionRequest(req: AiCompletionRequest): AiWireCompletionRequest {\n return {\n ...req,\n messages: req.messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content.map((p): AiWireMessagePart => (p.type === 'text' ? p : { type: 'image', mime: p.mime, dataBase64: bytesToBase64(p.bytes) })),\n })),\n };\n}\n\nexport function fromWireCompletionRequest(wire: AiWireCompletionRequest): AiCompletionRequest {\n return {\n ...wire,\n messages: wire.messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content.map((p) => (p.type === 'text' ? p : { type: 'image' as const, mime: p.mime, bytes: base64ToBytes(p.dataBase64) })),\n })),\n };\n}\n","/**\n * `AiError` helpers and the user-facing copy for each `AiErrorCode`, kept in\n * one place so the rewrite popover, the AI tab and the indicator say the same\n * thing. Hosts reject with `{ code, message }`; `message` is the technical\n * detail, the copy here is what the user reads first.\n */\n\nimport { AI_ERROR_CODES } from './types';\nimport type { AiError, AiErrorCode } from './types';\n\nexport interface AiErrorCopy {\n title: string;\n message: string;\n /**\n * Suggested next step, when there is one. `openEngine`: the failing\n * engine's own page. `chooseModel`: the same page, to pick another model.\n */\n action?: 'openSettings' | 'openEngine' | 'chooseModel' | 'retry' | 'shortenSelection';\n}\n\nexport const AI_ERROR_COPY: Readonly<Record<AiErrorCode, AiErrorCopy>> = {\n unreachable: {\n title: 'Engine not reachable',\n message: 'The AI engine did not answer. Check that the server is running and the base URL is right.',\n action: 'openSettings',\n },\n auth: {\n title: 'Key rejected',\n message: 'The engine refused the API key. Store a new key in AI settings, or check the environment variable.',\n action: 'openSettings',\n },\n modelNotFound: {\n title: 'Model not found',\n message: 'The engine does not serve this model. Refresh models or pick another one in AI settings.',\n action: 'openSettings',\n },\n modelLoadFailed: {\n title: \"Model didn't load\",\n message: \"The engine couldn't load the model. Load it in the engine's app, or choose another model.\",\n action: 'chooseModel',\n },\n contextOverflow: {\n title: 'Too much text',\n message: \"The text is longer than the model's context window. Try a shorter selection.\",\n action: 'shortenSelection',\n },\n rateLimited: {\n title: 'Rate limited',\n message: 'The engine is busy or the rate limit was reached. Wait a moment and try again.',\n action: 'retry',\n },\n cloudBlocked: {\n title: 'Cloud engines are off',\n message: 'This task is routed to a cloud engine, and Allow cloud is off in AI settings.',\n action: 'openSettings',\n },\n consentDenied: {\n title: 'Not allowed in this workspace',\n message: 'Sending text to this cloud engine was not allowed for this workspace.',\n action: 'openSettings',\n },\n cancelled: {\n title: 'Cancelled',\n message: 'The request was cancelled.',\n },\n badOutput: {\n title: 'No usable answer',\n message: 'The model did not return a usable answer. Try again or pick another model.',\n action: 'retry',\n },\n unknown: {\n title: 'Something went wrong',\n message: 'The AI request failed.',\n action: 'retry',\n },\n};\n\n/**\n * A stream that stopped at its token limit (`finishReason: 'length'`). A\n * thinking model counts its reasoning against that limit, so with thinking\n * on the advice is to lower Thinking. `empty`: no answer text came back at\n * all (shown as the error); otherwise the answer is shown with `message` as\n * a note.\n */\nexport function aiLengthLimitCopy(opts: { thinking: boolean; empty: boolean }): AiErrorCopy {\n if (opts.empty) {\n return opts.thinking\n ? { title: 'No room left to answer', message: 'The model used its whole length budget thinking and sent no answer. Lower Thinking, or Retry.', action: 'retry' }\n : { title: 'No room left to answer', message: 'The model reached its length limit before it sent any text. Retry.', action: 'retry' };\n }\n return opts.thinking\n ? { title: 'Answer cut off', message: 'The answer was cut off at the length limit. Lower Thinking, or Retry, for a full answer.', action: 'retry' }\n : { title: 'Answer cut off', message: 'The answer was cut off at the length limit.', action: 'retry' };\n}\n\nexport function isAiErrorCode(v: unknown): v is AiErrorCode {\n return typeof v === 'string' && (AI_ERROR_CODES as readonly string[]).includes(v);\n}\n\nexport function isAiError(v: unknown): v is AiError {\n return typeof v === 'object' && v !== null && isAiErrorCode((v as AiError).code) && typeof (v as AiError).message === 'string';\n}\n\nexport function makeAiError(code: AiErrorCode, message?: string): AiError {\n return { code, message: message ?? AI_ERROR_COPY[code].message };\n}\n\n/** Any rejection value as an `AiError`: an `AiError` as is, an `AbortError` as `cancelled`, the rest `unknown`. */\nexport function toAiError(err: unknown): AiError {\n if (isAiError(err)) {return { code: err.code, message: err.message, ...(typeof err.engineId === 'string' ? { engineId: err.engineId } : {}) };}\n if (typeof err === 'object' && err !== null && (err as { name?: unknown }).name === 'AbortError') {return makeAiError('cancelled');}\n const message = err instanceof Error ? err.message : typeof err === 'string' ? err : AI_ERROR_COPY.unknown.message;\n return { code: 'unknown', message };\n}\n\n/** Copy for the codes whose fix is on one engine's page, naming the engine (\"Anthropic refused the API key.\"). */\nconst ENGINE_COPY: Partial<Record<AiErrorCode, (engine: string, model: string) => string>> = {\n unreachable: (engine) => `${engine} did not answer. Check that the server is running and the base URL is right.`,\n auth: (engine) => `${engine} refused the API key. Store a new key, or check the environment variable.`,\n modelNotFound: (engine) => `${engine} does not serve this model. Refresh its models or pick another one.`,\n modelLoadFailed: (engine, model) => `${engine} couldn't load ${model}. Load it in ${engine}, or choose another model.`,\n};\n\n/**\n * The copy for `code`. With `engine` (the display name of the engine the\n * request went to), an engine problem names it and its action is\n * `openEngine` (`chooseModel` for a model that did not load): the popover's\n * button opens that engine instead of the AI tab. `model` names the model\n * where the copy has room for it.\n */\nexport function aiErrorCopy(code: AiErrorCode, opts: { engine?: string; model?: string } = {}): AiErrorCopy {\n const copy = AI_ERROR_COPY[code] ?? AI_ERROR_COPY.unknown;\n const named = opts.engine ? ENGINE_COPY[code] : undefined;\n if (!named || !opts.engine) {return copy;}\n return { ...copy, message: named(opts.engine, opts.model || 'the model'), action: copy.action === 'chooseModel' ? 'chooseModel' : 'openEngine' };\n}\n\n/**\n * A local server that could not load the model: LM Studio's `Failed to load\n * model \"x\"`, llama.cpp's `error loading model`, Ollama's `llama runner\n * process has terminated` and its out-of-memory refusal.\n */\nexport const MODEL_LOAD_FAILED_PHRASES: readonly string[] = [\n 'failed to load model',\n 'error loading model',\n 'unable to load model',\n 'could not load model',\n \"couldn't load model\",\n 'model failed to load',\n 'llama runner process has terminated',\n 'requires more system memory',\n];\n\nfunction isModelLoadFailure(bodyText: string): boolean {\n const lower = bodyText.toLowerCase();\n return MODEL_LOAD_FAILED_PHRASES.some((p) => lower.includes(p));\n}\n\n/**\n * Map an HTTP failure to a code (shared by both hosts): 401/403 auth, 404\n * modelNotFound, a body that says the model failed to load modelLoadFailed,\n * 413 or a 400 that mentions the context contextOverflow, 429 and 529\n * rateLimited, 502/503/504 unreachable, everything else unknown.\n */\nexport function aiErrorCodeFromHttp(status: number, bodyText = ''): AiErrorCode {\n if (status === 401 || status === 403) {return 'auth';}\n if (status === 404) {return 'modelNotFound';}\n if (isModelLoadFailure(bodyText)) {return 'modelLoadFailed';}\n if (status === 413) {return 'contextOverflow';}\n if (status === 429 || status === 529) {return 'rateLimited';}\n if (status === 400 && /context|too long|maximum.*tokens|token limit/i.test(bodyText)) {return 'contextOverflow';}\n if (status === 400 && /model.*(not found|does not exist|unknown)|no such model/i.test(bodyText)) {return 'modelNotFound';}\n if (status === 502 || status === 503 || status === 504) {return 'unreachable';}\n return 'unknown';\n}\n","/**\n * Per-model thinking level (Off / Low / Medium / High) mapped onto each\n * engine kind's request fields, stored in the model's `requestOverrides`\n * (OPEN-6) so the adapters need no change: they merge the overrides into\n * every request body last.\n *\n * Mappings:\n * - `openai` and cloud `openai-compatible` (Gemini, Groq, OpenRouter...):\n * `reasoning_effort` `none` / `low` / `medium` / `high`. Off is stored as\n * `none`; `buildCompletionBody` swaps it for the lowest value an OpenAI\n * reasoning model accepts (`openAiOffReasoningEffort`).\n * - local `openai-compatible` (oMLX, vLLM, llama.cpp): tiers through\n * `reasoning_effort`; Off through `chat_template_kwargs.enable_thinking:\n * false`, which the chat template honours (oMLX ignores\n * `reasoning_effort: \"none\"`).\n * - `lmstudio`: on/off only, `reasoning_effort` `none` or `medium`.\n * - `ollama`: `reasoning_effort` `none` / `low` / `medium` / `high` (its\n * OpenAI-compatible endpoint maps it onto `think`).\n * - `vscode-lm`: none (VS Code's language model API has no such field).\n * - `anthropic`: adaptive thinking with `output_config.effort` `low` /\n * `medium` / `high`. No Off: current models reject `thinking: disabled`\n * (Opus 5.5, Sonnet 5.5, Fable), so Low is the lightest setting.\n *\n * \"Model default\" (no level) removes the kind's thinking fields and leaves\n * everything else in `requestOverrides` alone.\n *\n * Per-task default (Q6): with the dial on Default and no level set on the\n * model, MikeDown picks the level for the job (`taskThinkingDefault`): Off\n * for the short edits (every rewrite variant, translate, suggest title,\n * suggest properties), none for the rest (explain, summarize), so the model\n * decides there. An explicit dial choice, or a level set on the model, wins\n * (`resolveThinkingLevel`).\n */\n\nimport { isOpenAiReasoningModel } from './capabilityHeuristics';\n\nexport type AiThinkingLevel = 'off' | 'low' | 'medium' | 'high';\n\nexport interface AiThinkingChoice {\n level: AiThinkingLevel;\n label: string;\n}\n\nexport interface AiThinkingOptions {\n /** The engine's locality is local (changes the Off mapping for `openai-compatible`). */\n local?: boolean;\n}\n\ntype Json = Record<string, unknown>;\n\nconst TIERS: readonly AiThinkingLevel[] = ['low', 'medium', 'high'];\nconst LABELS: Record<AiThinkingLevel, string> = { off: 'Off', low: 'Low', medium: 'Medium', high: 'High' };\n\nfunction isObject(v: unknown): v is Json {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/** The levels a kind can express, in order. Empty means no known mapping: hide the control. */\nexport function thinkingChoices(kind: string): AiThinkingChoice[] {\n switch (kind) {\n case 'openai':\n case 'openai-compatible':\n case 'ollama':\n return (['off', ...TIERS] as AiThinkingLevel[]).map((level) => ({ level, label: LABELS[level] }));\n case 'lmstudio':\n return [{ level: 'off', label: 'Off' }, { level: 'medium', label: 'On' }];\n case 'anthropic':\n return TIERS.map((level) => ({ level, label: LABELS[level] }));\n // `vscode.lm` takes no thinking fields, so the dial hides.\n case 'vscode-lm':\n default:\n return [];\n }\n}\n\n/**\n * The level a kind uses for `level`: the level itself when the kind has it,\n * else the nearest one (Off on Anthropic is Low, any tier on LM Studio is\n * On). Null for no level or a kind with no mapping.\n */\nexport function nearestThinkingLevel(kind: string, level: AiThinkingLevel | null): AiThinkingLevel | null {\n const choices = thinkingChoices(kind);\n if (!level || choices.length === 0) {return null;}\n if (choices.some((c) => c.level === level)) {return level;}\n return level === 'off' ? choices[0].level : (choices.find((c) => c.level !== 'off') ?? choices[0]).level;\n}\n\n/**\n * What a request is for, as far as its default thinking level goes: a\n * feature id (`rewrite`, `title`, `frontmatter`, `summary`) or a job that\n * shares one (`explain` and `translate` run under `rewrite`).\n */\nexport type AiThinkingJob = string;\n\n/** Short edits: thinking spends the budget and adds little, so \"Default\" sends Off. */\nconst SHORT_EDIT_JOBS: ReadonlySet<string> = new Set(['rewrite', 'translate', 'title', 'frontmatter']);\n\n/**\n * The level MikeDown picks for a job when the dial is on Default (Q6): Off\n * for the short edits, null (the model's own) for everything else.\n */\nexport function taskThinkingDefault(job: string | undefined): AiThinkingLevel | null {\n return job && SHORT_EDIT_JOBS.has(job) ? 'off' : null;\n}\n\nexport interface ResolvedThinkingLevel {\n /** The level to send (already the kind's nearest), or null to send none. */\n level: AiThinkingLevel | null;\n /** True when `level` is the job's default rather than the user's choice. */\n auto: boolean;\n}\n\n/**\n * The level a request sends: the user's explicit choice (the dial) first,\n * then nothing when the model has its own level set (`configured`), then the\n * job's default (mapped to the kind's nearest: Off on Anthropic is Low).\n * The explicit level is sent as given (`thinkingLevelFor` already maps a\n * remembered one); one the kind cannot express sends nothing.\n */\nexport function resolveThinkingLevel(\n kind: string,\n explicit: AiThinkingLevel | null,\n configured: AiThinkingLevel | null,\n job: string | undefined,\n opts: { modelId?: string } = {},\n): ResolvedThinkingLevel {\n if (explicit) {return { level: explicit, auto: false };}\n if (configured) {return { level: null, auto: false };}\n // An OpenAI model that does not reason rejects `reasoning_effort`: nothing to turn off.\n if (kind === 'openai' && opts.modelId !== undefined && !isOpenAiReasoningModel(opts.modelId, 'openai')) {return { level: null, auto: false };}\n const level = nearestThinkingLevel(kind, taskThinkingDefault(job));\n return { level, auto: level !== null };\n}\n\n/** The request fields for one level, or `{}` when the kind cannot express it. */\nexport function thinkingOverrides(kind: string, level: AiThinkingLevel, opts: AiThinkingOptions = {}): Json {\n if (!thinkingChoices(kind).some((c) => c.level === level)) {return {};}\n switch (kind) {\n case 'anthropic':\n return { thinking: { type: 'adaptive' }, output_config: { effort: level } };\n case 'openai-compatible':\n if (level === 'off' && opts.local) {return { chat_template_kwargs: { enable_thinking: false } };}\n return { reasoning_effort: level === 'off' ? 'none' : level };\n default:\n return { reasoning_effort: level === 'off' ? 'none' : level };\n }\n}\n\n/** The level `overrides` express for this kind, or null for \"model default\" (or a value the menu has no entry for). */\nexport function thinkingLevelFrom(kind: string, overrides: Json | undefined): AiThinkingLevel | null {\n if (!overrides || thinkingChoices(kind).length === 0) {return null;}\n if (kind === 'anthropic') {\n const effort = isObject(overrides.output_config) ? overrides.output_config.effort : undefined;\n return typeof effort === 'string' && (TIERS as readonly string[]).includes(effort) ? (effort as AiThinkingLevel) : null;\n }\n const kwargs = isObject(overrides.chat_template_kwargs) ? overrides.chat_template_kwargs : undefined;\n if (kwargs?.enable_thinking === false) {return 'off';}\n const effort = overrides.reasoning_effort;\n if (effort === 'none') {return 'off';}\n if (typeof effort !== 'string' || !(TIERS as readonly string[]).includes(effort)) {return null;}\n // LM Studio is on/off: any tier reads as On.\n return kind === 'lmstudio' ? 'medium' : (effort as AiThinkingLevel);\n}\n\n/** `overrides` without this kind's thinking fields; empty containers are dropped. */\nfunction stripThinking(kind: string, overrides: Json): Json {\n const out: Json = { ...overrides };\n const dropNested = (key: string, inner: string) => {\n const current = out[key];\n if (!isObject(current)) {return;}\n const rest = { ...current };\n delete rest[inner];\n if (Object.keys(rest).length === 0) {delete out[key];} else {out[key] = rest;}\n };\n if (kind === 'anthropic') {\n delete out.thinking;\n dropNested('output_config', 'effort');\n } else {\n delete out.reasoning_effort;\n dropNested('chat_template_kwargs', 'enable_thinking');\n }\n return out;\n}\n\nfunction deepMerge(base: Json, patch: Json): Json {\n const out: Json = { ...base };\n for (const [key, value] of Object.entries(patch)) {\n const current = out[key];\n out[key] = isObject(current) && isObject(value) ? deepMerge(current, value) : value;\n }\n return out;\n}\n\n/**\n * `requestOverrides` with the thinking level set (null: model default).\n * Other keys, nested ones included, are kept. Undefined when nothing is left.\n */\nexport function withThinkingLevel(\n kind: string,\n overrides: Json | undefined,\n level: AiThinkingLevel | null,\n opts: AiThinkingOptions = {},\n): Json | undefined {\n const base = stripThinking(kind, overrides ?? {});\n const next = level ? deepMerge(base, thinkingOverrides(kind, level, opts)) : base;\n return Object.keys(next).length > 0 ? next : undefined;\n}\n\n/**\n * Output tokens a thinking model may spend reasoning before it answers, by\n * level. A thinking model counts its reasoning against `max_tokens`, so an\n * answer budget alone can be used up before the first visible word. Sized\n * for local reasoning models (Qwen3 class), which often think 2,000 to\n * 4,000 tokens even on a one-paragraph edit. It is a ceiling, not a spend.\n */\nexport const THINKING_HEADROOM_TOKENS: Readonly<Record<AiThinkingLevel, number>> = { off: 0, low: 2048, medium: 4096, high: 8192 };\n\n/** \"Model default\" (no level set): the model may think, at a level we cannot see. */\nexport const THINKING_DEFAULT_HEADROOM_TOKENS = THINKING_HEADROOM_TOKENS.medium;\n\n/** The most `thinkingMaxTokens` raises a request to (a larger answer budget is kept as is). */\nexport const THINKING_MAX_TOKENS_CAP = 12288;\n\nexport interface ThinkingBudgetOptions {\n /** The model's context window, when known. */\n contextTokens?: number | null;\n /** Estimated prompt tokens; with `contextTokens`, the headroom never pushes past the window. */\n promptTokens?: number;\n}\n\n/**\n * The `max_tokens` to send for an answer budget of `answerTokens` on a model\n * that can think: the answer plus reasoning headroom for the effective level\n * (null: model default, counted as thinking). Off adds nothing. The raise is\n * capped at `THINKING_MAX_TOKENS_CAP` and at what is left of the context\n * window, and never goes below the answer budget.\n */\nexport function thinkingMaxTokens(answerTokens: number, level: AiThinkingLevel | null, opts: ThinkingBudgetOptions = {}): number {\n const headroom = level ? THINKING_HEADROOM_TOKENS[level] : THINKING_DEFAULT_HEADROOM_TOKENS;\n if (headroom <= 0) {return answerTokens;}\n let limit = THINKING_MAX_TOKENS_CAP;\n if (opts.contextTokens && opts.contextTokens > 0) {\n limit = Math.min(limit, opts.contextTokens - Math.ceil(opts.promptTokens ?? 0) - 256);\n }\n return Math.max(answerTokens, Math.min(answerTokens + headroom, limit));\n}\n","/**\n * Selection rewrite prompt (the `rewrite` feature). Pure: builds the chat\n * messages from the selected markdown and a little context, and cleans what\n * the model sends back before it is previewed or parsed into the document.\n *\n * Context budget (brainstorm constraint 8): the selection plus the text of\n * the enclosing block before and after it, and the neighbouring block on each\n * side, capped at `REWRITE_CONTEXT_CHARS` per side. Never the whole document.\n */\n\nimport type { AiMessage } from '../types';\n\nexport type AiRewriteVariant =\n | 'shorter' | 'longer' | 'expand' | 'simpler' | 'fixFlow'\n | 'formal' | 'casual' | 'readingLevel' | 'translate' | 'explain' | 'custom';\n\n/** Menu group a variant belongs to (the popover's choose phase renders one section per group). */\nexport type AiRewriteGroup = 'length' | 'tone' | 'audience' | 'translate' | 'explain' | 'custom';\n\n/** `replace` swaps the selection; `readOnly` only shows text (Explain) and can insert it below. */\nexport type AiRewriteOutput = 'replace' | 'readOnly';\n\nexport type AiAudience = 'general' | 'expert' | 'plain' | 'grade8' | 'esl';\n\nexport interface AiAudienceDefinition {\n id: AiAudience;\n label: string;\n /** Instruction line sent to the model for `readingLevel`. */\n instruction: string;\n}\n\nexport const AI_AUDIENCES: readonly AiAudienceDefinition[] = [\n {\n id: 'general',\n label: 'General audience',\n instruction: 'Rewrite it for a general audience: plain words, short sentences, jargon explained or avoided. Keep the meaning and every fact, name and link.',\n },\n {\n id: 'expert',\n label: 'Expert',\n instruction: 'Rewrite it for expert readers: precise terminology, no basic explanations, dense and direct. Keep the meaning and every fact, name and link.',\n },\n {\n id: 'plain',\n label: 'Plain language',\n instruction: 'Rewrite it in plain language: everyday words, short sentences, active voice, no jargon or idioms. Keep the meaning and every fact, name and link.',\n },\n {\n id: 'grade8',\n label: '8th grade',\n instruction: 'Rewrite it at an 8th grade reading level: common words, short sentences, one idea at a time. Keep the meaning and every fact, name and link.',\n },\n {\n id: 'esl',\n label: 'English learners (ESL)',\n instruction: 'Rewrite it for readers who are learning English: simple vocabulary, short direct sentences, literal wording, no idioms or phrasal verbs. Keep the meaning and every fact, name and link.',\n },\n];\n\nexport function getAiAudience(id: string): AiAudienceDefinition | undefined {\n return AI_AUDIENCES.find((a) => a.id === id);\n}\n\nexport interface AiRewriteVariantDefinition {\n id: AiRewriteVariant;\n /** Menu label. */\n label: string;\n /** Tooltip, where the label alone does not tell two variants apart (Longer, Add detail). */\n title?: string;\n /**\n * The instruction line sent to the model. Empty for `custom` (the user's own\n * words); `{language}` and `{audience}` are filled from `RewritePromptInput.params`.\n */\n instruction: string;\n group: AiRewriteGroup;\n output: AiRewriteOutput;\n /** The parameter this variant needs before it can run (a second-level picker supplies it). */\n param?: 'language' | 'audience';\n}\n\n/** Order here is the menu order. */\nexport const AI_REWRITE_VARIANTS: readonly AiRewriteVariantDefinition[] = [\n {\n id: 'shorter',\n label: 'Shorter',\n instruction: 'Make it shorter and tighter. Keep the meaning and every fact, name and link; cut repetition and filler.',\n group: 'length',\n output: 'replace',\n },\n {\n id: 'longer',\n label: 'Longer',\n title: 'Longer: the same points, said at more length',\n instruction: 'Make it longer: add detail, examples or explanation that fit the surrounding text. Do not invent facts, numbers, names or links.',\n group: 'length',\n output: 'replace',\n },\n {\n id: 'expand',\n label: 'Add detail',\n title: 'Add detail (facts and examples)',\n instruction: 'Expand it: develop each point with the reasoning, detail or an example that the surrounding text supports, so it reads as a fuller passage. Do not invent facts, numbers, names or links.',\n group: 'length',\n output: 'replace',\n },\n {\n id: 'simpler',\n label: 'Simpler',\n instruction: 'Make it simpler: shorter sentences, everyday words, one idea at a time. Keep the meaning and every fact, name and link.',\n group: 'tone',\n output: 'replace',\n },\n {\n id: 'fixFlow',\n label: 'Fix flow',\n instruction: 'Fix the flow: correct grammar, spelling and punctuation, and smooth awkward sentences and transitions. Keep the length, meaning and tone close to the original.',\n group: 'tone',\n output: 'replace',\n },\n {\n id: 'formal',\n label: 'More formal',\n instruction: 'Make the tone more formal and professional: no contractions or slang, precise wording. Keep the meaning, every fact and the length close to the original.',\n group: 'tone',\n output: 'replace',\n },\n {\n id: 'casual',\n label: 'More casual',\n instruction: 'Make the tone more casual and conversational: contractions and everyday phrasing. Keep the meaning, every fact and the length close to the original.',\n group: 'tone',\n output: 'replace',\n },\n {\n id: 'readingLevel',\n label: 'Reading level…',\n instruction: '{audience}',\n group: 'audience',\n output: 'replace',\n param: 'audience',\n },\n {\n id: 'translate',\n label: 'Translate…',\n instruction: 'Translate the passage into {language}.',\n group: 'translate',\n output: 'replace',\n param: 'language',\n },\n {\n id: 'explain',\n label: 'Explain',\n instruction: 'Explain what the passage says and means in plain words, as a short note for the reader. Do not repeat the passage.',\n group: 'explain',\n output: 'readOnly',\n },\n { id: 'custom', label: 'Custom prompt…', instruction: '', group: 'custom', output: 'replace' },\n];\n\nexport function getAiRewriteVariant(id: string): AiRewriteVariantDefinition | undefined {\n return AI_REWRITE_VARIANTS.find((v) => v.id === id);\n}\n\n/** Context kept on each side of the selection, in characters (about 400 tokens). */\nexport const REWRITE_CONTEXT_CHARS = 1600;\n\n/** Longest custom instruction sent as is. */\nexport const REWRITE_INSTRUCTION_MAX_CHARS = 1000;\n\nexport interface RewritePromptInput {\n variant: AiRewriteVariant;\n /** The user's instruction for `custom` (ignored otherwise). */\n instruction?: string;\n /** The selected text as markdown. */\n selection: string;\n /** Markdown just before the selection (enclosing block, then the previous one). */\n before?: string;\n /** Markdown just after the selection. */\n after?: string;\n /** Values for the parameterised variants (`translate` needs `language`, `readingLevel` needs `audience`). */\n params?: AiRewriteParams;\n /**\n * `document` for a whole-document run (`translate`): the passage is the body\n * without its frontmatter and no context is sent. Default `selection`.\n */\n scope?: AiRewriteScope;\n}\n\nexport interface AiRewriteParams {\n language?: string;\n audience?: AiAudience;\n}\n\nexport type AiRewriteScope = 'selection' | 'document';\n\n/** Longest target language name sent (it is free text from \"Other…\"). */\nexport const REWRITE_LANGUAGE_MAX_CHARS = 60;\n\nexport const REWRITE_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. You rewrite the passage the user selected, following one instruction.',\n '',\n 'Rules:',\n '- Reply with the rewritten passage only. No preamble, no explanation, no quotation marks and no code fence around it.',\n '- Keep it Markdown, and keep the structure of the passage: headings stay headings, list items stay list items with the same markers and indentation, tables stay tables, quotes stay quotes. A passage that is part of a sentence stays part of a sentence.',\n '- Keep every link and image with its exact URL, inline code and code blocks character for character, and inline formatting (bold, italic, strikethrough, ==highlight==, footnote references, [[wikilinks]], #tags, math) on the words it belongs to.',\n '- Write in the language and voice of the passage.',\n '- The text before and after the passage is context only. Do not repeat it and do not continue it.',\n '- If the instruction asks for anything other than a version of the passage, still reply with a version of the passage only.',\n].join('\\n');\n\n/**\n * Appended to the system prompt for `translate`. It overrides the \"write in the\n * language of the passage\" rule; the target language is named only in the\n * instruction line.\n */\nexport const TRANSLATE_SYSTEM_ADDENDUM = [\n 'This is a translation. The \"write in the language and voice of the passage\" rule above does not apply: write in the language named in the instruction.',\n '- Translate prose only: paragraph text, list item text, table cell text, heading text, image alt text and link text.',\n '- Keep verbatim, untranslated: the Markdown syntax itself (heading marks, list markers and indentation, table pipes and separator rows, quote marks), link and image URLs, inline code and fenced code blocks (including their language tag), [[wikilinks]], #tags, math, footnote markers such as [^1], HTML tags and frontmatter keys.',\n '- Keep the line structure: the same number of lines, blocks and list items in the same order, with blank lines where the passage has them.',\n '- Keep names of people, products and places as they are unless they have a well established translation.',\n].join('\\n');\n\n/** Appended to the system prompt when the instruction asks for an explanation (the `explain` variant). */\nexport const EXPLAIN_SYSTEM_ADDENDUM =\n 'The instruction asks for an explanation of the passage, not a version of it: the \"version of the passage\" rules above do not apply. Reply with the explanation only, as Markdown prose, and do not repeat the passage.';\n\n/** The whole-document variant of the translation note. */\nexport const TRANSLATE_DOCUMENT_NOTE =\n 'The passage is a whole document body (its frontmatter has been removed and is handled elsewhere). Translate all of it, from the first line to the last.';\n\n/** Neutralise a closing tag inside user text so it cannot end its block early. */\nexport function escapeClosingTag(text: string, tag: string): string {\n return text.split(`</${tag}>`).join(`<\\\\/${tag}>`);\n}\n\n/** Context trimmed to the budget at the far end, so the part next to the selection survives. */\nexport function trimRewriteContext(text: string, side: 'before' | 'after', max = REWRITE_CONTEXT_CHARS): string {\n if (text.length <= max) {return text;}\n if (side === 'before') {\n const cut = text.slice(text.length - max);\n const nl = cut.indexOf('\\n');\n return nl >= 0 && nl < max / 2 ? cut.slice(nl + 1) : cut;\n }\n const cut = text.slice(0, max);\n const nl = cut.lastIndexOf('\\n');\n return nl > max / 2 ? cut.slice(0, nl) : cut;\n}\n\n/** Target language as typed, flattened to one short line (it ends up inside a prompt). */\nexport function normalizeRewriteLanguage(language: string | undefined): string {\n return (language ?? '').replace(/[<>\\r\\n]+/g, ' ').replace(/\\s+/g, ' ').trim().slice(0, REWRITE_LANGUAGE_MAX_CHARS);\n}\n\n/**\n * The instruction line for a variant. A parameterised variant whose parameter\n * is missing or unknown falls back to `fixFlow`, like an empty `custom`.\n */\nexport function rewriteInstruction(input: Pick<RewritePromptInput, 'variant' | 'instruction' | 'params'>): string {\n const fallback = getAiRewriteVariant('fixFlow')?.instruction ?? '';\n if (input.variant === 'custom') {\n const own = (input.instruction ?? '').trim().slice(0, REWRITE_INSTRUCTION_MAX_CHARS);\n return own || fallback;\n }\n const def = getAiRewriteVariant(input.variant);\n if (!def) {return '';}\n if (def.param === 'language') {\n const language = normalizeRewriteLanguage(input.params?.language);\n return language ? def.instruction.replace('{language}', () => language) : fallback;\n }\n if (def.param === 'audience') {\n const audience = input.params?.audience ? getAiAudience(input.params.audience) : undefined;\n return audience ? def.instruction.replace('{audience}', () => audience.instruction) : fallback;\n }\n return def.instruction;\n}\n\n/** The system prompt for a variant: the shared rules plus the translate or explain addendum. */\nexport function rewriteSystemPrompt(variant: AiRewriteVariant, scope: AiRewriteScope = 'selection'): string {\n if (variant === 'translate') {\n return [REWRITE_SYSTEM_PROMPT, TRANSLATE_SYSTEM_ADDENDUM, ...(scope === 'document' ? [TRANSLATE_DOCUMENT_NOTE] : [])].join('\\n\\n');\n }\n if (variant === 'explain') {return `${REWRITE_SYSTEM_PROMPT}\\n\\n${EXPLAIN_SYSTEM_ADDENDUM}`;}\n return REWRITE_SYSTEM_PROMPT;\n}\n\nexport function buildRewriteMessages(input: RewritePromptInput): AiMessage[] {\n const instruction = rewriteInstruction(input);\n const scope: AiRewriteScope = input.scope === 'document' && input.variant === 'translate' ? 'document' : 'selection';\n const before = scope === 'document' ? '' : trimRewriteContext(input.before ?? '', 'before').trimEnd();\n const after = scope === 'document' ? '' : trimRewriteContext(input.after ?? '', 'after').trimStart();\n const parts: string[] = [];\n // A custom prompt is the user's own words; quote it so it reads as the\n // instruction and the rules above still apply.\n parts.push(input.variant === 'custom'\n ? `Instruction (from the user): <instruction>\\n${escapeClosingTag(instruction, 'instruction')}\\n</instruction>`\n : `Instruction: ${instruction}`);\n if (before) {parts.push(`Text before the passage (context only, do not rewrite it):\\n<before>\\n${escapeClosingTag(before, 'before')}\\n</before>`);}\n parts.push(`${input.variant === 'explain' ? 'Passage to explain' : 'Passage to rewrite'}:\\n<passage>\\n${escapeClosingTag(input.selection, 'passage')}\\n</passage>`);\n if (after) {parts.push(`Text after the passage (context only, do not rewrite it):\\n<after>\\n${escapeClosingTag(after, 'after')}\\n</after>`);}\n parts.push(input.variant === 'explain' ? 'Reply with the explanation only.' : 'Reply with the rewritten passage only.');\n return [\n { role: 'system', content: rewriteSystemPrompt(input.variant, scope) },\n { role: 'user', content: parts.join('\\n\\n') },\n ];\n}\n\n/** Output budget for `explain`: a short note, independent of the passage length. */\nexport const EXPLAIN_MAX_TOKENS = 600;\n\nconst REWRITE_TOKEN_FACTOR: Partial<Record<AiRewriteVariant, number>> = {\n longer: 3,\n expand: 3,\n translate: 3,\n custom: 3,\n simpler: 1.8,\n formal: 1.8,\n casual: 1.8,\n};\n\n/** Output budget: room for \"longer\" to double the passage, never below a short paragraph. */\nexport function rewriteMaxTokens(selection: string, variant: AiRewriteVariant): number {\n if (variant === 'explain') {return EXPLAIN_MAX_TOKENS;}\n const approxTokens = Math.ceil(selection.length / 3.5);\n const factor = REWRITE_TOKEN_FACTOR[variant] ?? 1.6;\n return Math.max(256, Math.min(4096, Math.ceil(approxTokens * factor) + 64));\n}\n\n/**\n * The text to show while it streams: reasoning blocks (`<think>…</think>`,\n * which some local models put in the content) are hidden, including one that\n * has not closed yet, and an echoed `<passage>` tag is dropped.\n */\nexport function rewriteVisibleText(raw: string): string {\n let text = raw.replace(/<think>[\\s\\S]*?<\\/think>\\s*/g, '');\n const open = text.indexOf('<think>');\n if (open >= 0) {text = text.slice(0, open);}\n return text.replace(/^\\s*<passage>\\s*\\n?/, '').replace(/\\n?\\s*<\\/passage>\\s*$/, '');\n}\n\n/**\n * The final replacement: the visible text without a wrapping code fence or\n * quotes the model added, trimmed, with the selection's own leading and\n * trailing whitespace put back (a selection of \"word \" stays one word plus a\n * space).\n */\nexport function finalizeRewriteText(raw: string, selection: string): string {\n let text = rewriteVisibleText(raw).trim();\n const fenced = /^```(?:markdown|md|text)?[ \\t]*\\n([\\s\\S]*?)\\n```$/i.exec(text);\n if (fenced && !selection.trimStart().startsWith('```')) {text = fenced[1].trim();}\n const quoted = /^([\"“])([\\s\\S]*)([\"”])$/.exec(text);\n if (quoted && !/^\\s*[\"“]/.test(selection) && !quoted[2].includes(quoted[1])) {text = quoted[2].trim();}\n if (!text) {return '';}\n const lead = /^\\s*/.exec(selection)?.[0] ?? '';\n const trail = /\\s*$/.exec(selection)?.[0] ?? '';\n return lead + text + trail;\n}\n","/**\n * Explain prompt (the `explain` variant of the `rewrite` feature). Pure: builds\n * the chat messages for a read-only note about the selected passage. The reply\n * is shown in the popover and can be inserted below the selection; it never\n * replaces anything.\n */\n\nimport type { AiMessage } from '../types';\nimport { EXPLAIN_MAX_TOKENS, escapeClosingTag, getAiRewriteVariant, trimRewriteContext } from './rewrite';\n\nexport const EXPLAIN_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. The user selected a passage and wants it explained.',\n '',\n 'Rules:',\n '- Reply with the explanation only, as short Markdown prose (a few sentences or a short paragraph, a brief list only when the passage has several distinct points). No preamble, no heading, and no code fence around the whole reply.',\n '- Explain what the passage says and means, and define any term a reader may not know. Do not repeat or quote the passage back and do not rewrite it.',\n '- Do not invent facts. If the passage or its context is not enough to explain something, say so in one sentence.',\n '- Write in the language of the passage.',\n '- The text before and after the passage and the document title are context only. Do not explain them.',\n].join('\\n');\n\nexport interface ExplainPromptInput {\n /** The selected text as markdown. */\n selection: string;\n /** Markdown just before the selection. */\n before?: string;\n /** Markdown just after the selection. */\n after?: string;\n /** The document title (frontmatter `title:` or the first heading), if there is one. */\n title?: string;\n}\n\nexport function buildExplainMessages(input: ExplainPromptInput): AiMessage[] {\n const before = trimRewriteContext(input.before ?? '', 'before').trimEnd();\n const after = trimRewriteContext(input.after ?? '', 'after').trimStart();\n const title = (input.title ?? '').replace(/\\s+/g, ' ').trim().slice(0, 200);\n const parts: string[] = [`Instruction: ${getAiRewriteVariant('explain')?.instruction ?? ''}`];\n if (title) {parts.push(`Document title (context only): ${title}`);}\n if (before) {parts.push(`Text before the passage (context only, do not explain it):\\n<before>\\n${escapeClosingTag(before, 'before')}\\n</before>`);}\n parts.push(`Passage to explain:\\n<passage>\\n${escapeClosingTag(input.selection, 'passage')}\\n</passage>`);\n if (after) {parts.push(`Text after the passage (context only, do not explain it):\\n<after>\\n${escapeClosingTag(after, 'after')}\\n</after>`);}\n parts.push('Reply with the explanation only.');\n return [\n { role: 'system', content: EXPLAIN_SYSTEM_PROMPT },\n { role: 'user', content: parts.join('\\n\\n') },\n ];\n}\n\n/** Output budget: a short note whatever the passage length. */\nexport function explainMaxTokens(): number {\n return EXPLAIN_MAX_TOKENS;\n}\n","/**\n * Markdown structure post-check. Pure: counts the structural pieces of a\n * passage and compares the original with a model's result, so translate and\n * tone rewrites can show a warning line before Accept when something was\n * dropped. A warning only; it never blocks Accept.\n */\n\nexport interface StructureCounts {\n /** Headings by level, index 0 is level 1. */\n headings: [number, number, number, number, number, number];\n fencedCodeBlocks: number;\n inlineCodeSpans: number;\n /** URL of every link (inline, reference definition, autolink), in order. */\n links: string[];\n /** URL of every image, in order. */\n images: string[];\n listItems: number;\n tableRows: number;\n wikilinks: number;\n footnoteReferences: number;\n}\n\nexport interface StructureMismatch {\n kind: 'heading' | 'fencedCodeBlock' | 'inlineCode' | 'link' | 'image' | 'listItem' | 'tableRow' | 'wikilink' | 'footnote';\n original: number;\n result: number;\n /** A human sentence such as \"2 links in the original, 1 in the result\". */\n message: string;\n}\n\nexport interface StructureDiff {\n /** True when nothing differs. */\n ok: boolean;\n mismatches: StructureMismatch[];\n /** `mismatches[].message`, ready to show. */\n messages: string[];\n}\n\nconst FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;\nconst HEADING = /^ {0,3}(#{1,6})(?:[ \\t]|$)/;\nconst LIST_ITEM = /^[ \\t]*(?:[-*+]|\\d{1,9}[.)])(?:[ \\t]|$)/;\nconst TABLE_DELIMITER = /^[ \\t]*\\|?[ \\t]*:?-+:?[ \\t]*(?:\\|[ \\t]*:?-+:?[ \\t]*)*\\|?[ \\t]*$/;\nconst FOOTNOTE_DEFINITION = /^ {0,3}\\[\\^[^\\]\\s]+\\]:/;\nconst URL_PART = String.raw`<?((?:[^()\\s<>]|\\([^()\\s]*\\))+)>?`;\nconst LINK_TEXT = String.raw`\\[(?:[^\\[\\]]|\\[[^\\[\\]]*\\])*\\]`;\nconst INLINE_IMAGE = new RegExp(String.raw`!${LINK_TEXT}\\(\\s*${URL_PART}(?:\\s+(?:\"[^\"]*\"|'[^']*'|\\([^)]*\\)))?\\s*\\)`, 'g');\nconst INLINE_LINK = new RegExp(String.raw`(?<!!)${LINK_TEXT}\\(\\s*${URL_PART}(?:\\s+(?:\"[^\"]*\"|'[^']*'|\\([^)]*\\)))?\\s*\\)`, 'g');\nconst REFERENCE_DEFINITION = /^ {0,3}\\[(?!\\^)[^\\]]+\\]:[ \\t]*<?(\\S+?)>?(?:[ \\t]+[\"'(].*)?$/gm;\nconst AUTOLINK = /<([a-z][a-z0-9+.-]*:[^\\s<>]+)>/gi;\nconst WIKILINK = /\\[\\[[^\\]\\n]+\\]\\]/g;\nconst FOOTNOTE_REFERENCE = /\\[\\^[^\\]\\s]+\\]/g;\n\n/** Split the lines into prose lines and the number of fenced blocks (an unclosed fence runs to the end). */\nfunction splitFences(markdown: string): { prose: string[]; fenced: number } {\n const prose: string[] = [];\n let fenced = 0;\n let open: { char: string; length: number } | null = null;\n for (const line of markdown.split(/\\r?\\n/)) {\n if (open) {\n const close = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/.exec(line);\n if (close && close[1][0] === open.char && close[1].length >= open.length) {open = null;}\n continue;\n }\n const m = FENCE_OPEN.exec(line);\n if (m && !(m[1][0] === '`' && line.slice(m[0].length).includes('`'))) {\n open = { char: m[1][0], length: m[1].length };\n fenced++;\n continue;\n }\n prose.push(line);\n }\n return { prose, fenced };\n}\n\n/** Remove inline code spans (they can hold link-like text) and return how many there were. */\nfunction stripInlineCode(text: string): { text: string; count: number } {\n let count = 0;\n const out = text.replace(/(`+)(?!`)([\\s\\S]*?[^`])\\1(?!`)/g, () => {\n count++;\n return '';\n });\n return { text: out, count };\n}\n\nfunction tableRowCount(lines: string[]): number {\n let rows = 0;\n for (let i = 1; i < lines.length; i++) {\n const header = lines[i - 1];\n if (!lines[i].includes('|') || !lines[i].includes('-') || !TABLE_DELIMITER.test(lines[i]) || !header.includes('|')) {continue;}\n // Header row above, then every following line that still looks like a row.\n rows++;\n let j = i + 1;\n while (j < lines.length && lines[j].trim() !== '' && lines[j].includes('|')) {\n rows++;\n j++;\n }\n i = j - 1;\n }\n return rows;\n}\n\nfunction collect(re: RegExp, text: string): string[] {\n const urls: string[] = [];\n for (const m of text.matchAll(re)) {urls.push(m[1]);}\n return urls;\n}\n\nexport function countMarkdownStructure(markdown: string): StructureCounts {\n const { prose, fenced } = splitFences(markdown);\n const headings: StructureCounts['headings'] = [0, 0, 0, 0, 0, 0];\n let listItems = 0;\n for (const line of prose) {\n const h = HEADING.exec(line);\n if (h) {headings[h[1].length - 1]++;}\n else if (LIST_ITEM.test(line)) {listItems++;}\n }\n const tableRows = tableRowCount(prose);\n const code = stripInlineCode(prose.map((l) => l.replace(FOOTNOTE_DEFINITION, '')).join('\\n'));\n const images = collect(INLINE_IMAGE, code.text);\n const withoutImages = code.text.replace(INLINE_IMAGE, '');\n const links = [\n ...collect(INLINE_LINK, withoutImages),\n ...collect(REFERENCE_DEFINITION, withoutImages),\n ...collect(AUTOLINK, withoutImages),\n ];\n return {\n headings,\n fencedCodeBlocks: fenced,\n inlineCodeSpans: code.count,\n links,\n images,\n listItems,\n tableRows,\n wikilinks: (code.text.match(WIKILINK) ?? []).length,\n footnoteReferences: (code.text.match(FOOTNOTE_REFERENCE) ?? []).length,\n };\n}\n\nfunction noun(n: number, singular: string, plural = `${singular}s`): string {\n return `${n} ${n === 1 ? singular : plural}`;\n}\n\nfunction countMessage(original: number, result: number, singular: string, plural?: string): string {\n return `${noun(original, singular, plural)} in the original, ${result} in the result`;\n}\n\n/** URLs in `from` that `against` does not account for (multiset difference). */\nfunction missingUrls(from: string[], against: string[]): string[] {\n const left = new Map<string, number>();\n for (const u of against) {left.set(u, (left.get(u) ?? 0) + 1);}\n const missing: string[] = [];\n for (const u of from) {\n const n = left.get(u) ?? 0;\n if (n > 0) {left.set(u, n - 1);}\n else {missing.push(u);}\n }\n return missing;\n}\n\nconst URL_LIST_CAP = 3;\n\nfunction urlList(urls: string[]): string {\n const shown = urls.slice(0, URL_LIST_CAP).join(', ');\n return urls.length > URL_LIST_CAP ? `${shown} and ${urls.length - URL_LIST_CAP} more` : shown;\n}\n\nfunction urlMismatch(kind: 'link' | 'image', original: string[], result: string[]): StructureMismatch | null {\n const missing = missingUrls(original, result);\n const extra = missingUrls(result, original);\n if (original.length === result.length && !missing.length) {return null;}\n const plural = `${kind}s`;\n let message: string;\n if (original.length !== result.length) {\n message = countMessage(original.length, result.length, kind, plural);\n if (missing.length) {message += ` (missing: ${urlList(missing)})`;}\n } else {\n message = `${noun(original.length, kind, plural)} point somewhere else in the result (missing: ${urlList(missing)}; unexpected: ${urlList(extra)})`;\n }\n return { kind, original: original.length, result: result.length, message };\n}\n\n/**\n * Compare the structure of a rewritten passage with the original. Zero\n * mismatches means every counted piece survived; a mismatch is a sentence for\n * the warning line, not an error.\n */\nexport function checkMarkdownStructure(original: string, result: string): StructureDiff {\n const a = countMarkdownStructure(original);\n const b = countMarkdownStructure(result);\n const mismatches: StructureMismatch[] = [];\n const count = (kind: StructureMismatch['kind'], x: number, y: number, singular: string, plural?: string): void => {\n if (x !== y) {mismatches.push({ kind, original: x, result: y, message: countMessage(x, y, singular, plural) });}\n };\n\n a.headings.forEach((n, i) => count('heading', n, b.headings[i], `level ${i + 1} heading`));\n count('fencedCodeBlock', a.fencedCodeBlocks, b.fencedCodeBlocks, 'fenced code block');\n count('inlineCode', a.inlineCodeSpans, b.inlineCodeSpans, 'inline code span');\n for (const m of [urlMismatch('link', a.links, b.links), urlMismatch('image', a.images, b.images)]) {\n if (m) {mismatches.push(m);}\n }\n count('listItem', a.listItems, b.listItems, 'list item');\n count('tableRow', a.tableRows, b.tableRows, 'table row');\n count('wikilink', a.wikilinks, b.wikilinks, 'wikilink');\n count('footnote', a.footnoteReferences, b.footnoteReferences, 'footnote reference');\n\n return { ok: mismatches.length === 0, mismatches, messages: mismatches.map((m) => m.message) };\n}\n","/**\n * Lenient JSON for local models (D6). Many local models have no JSON mode, so\n * a reply can arrive as clean JSON, inside a code fence, after a sentence of\n * prose, behind a `<think>` block, with a trailing comma or with single\n * quotes. `parseLenientJson` takes all of those; plain text is `ok: false`\n * with a message the repair turn can quote. Pure, no DOM.\n */\n\nimport type { AiMessage } from './types';\n\nexport type LenientJsonResult<T = unknown> = { ok: true; value: T } | { ok: false; error: string };\n\n/** `<think>…</think>` blocks removed, and an unclosed `<think>` dropped to the end. */\nfunction stripReasoning(raw: string): string {\n let text = raw.replace(/<think>[\\s\\S]*?<\\/think>/gi, '');\n const open = text.search(/<think>/i);\n if (open >= 0) {text = text.slice(0, open);}\n return text.trim();\n}\n\n/** The bodies of fenced code blocks, in order. An unclosed fence runs to the end. */\nfunction fencedBodies(text: string): string[] {\n const out: string[] = [];\n const re = /```[A-Za-z0-9_-]*[ \\t]*\\r?\\n([\\s\\S]*?)(?:\\r?\\n```|$)/g;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n out.push(m[1]);\n if (m[0].length === 0) {re.lastIndex++;}\n }\n return out;\n}\n\n/**\n * The balanced `{…}` or `[…]` starting at `start`, aware of strings (double or\n * single quoted). `end` is the index after the closer; null when it never\n * closes (a truncated reply).\n */\nfunction balancedSpan(text: string, start: number): { end: number } | null {\n const stack: string[] = [];\n let quote: string | null = null;\n for (let i = start; i < text.length; i++) {\n const c = text[i];\n if (quote) {\n if (c === '\\\\') {i++;}\n else if (c === quote) {quote = null;}\n continue;\n }\n if (c === '\"' || c === \"'\") {quote = c;}\n else if (c === '{') {stack.push('}');}\n else if (c === '[') {stack.push(']');}\n else if (c === '}' || c === ']') {\n if (stack.pop() !== c) {return null;}\n if (stack.length === 0) {return { end: i + 1 };}\n }\n }\n return null;\n}\n\n/**\n * The usual slips: single-quoted strings, trailing commas, unquoted keys and\n * raw newlines inside strings. One pass over the text, string aware.\n */\nfunction repairJson(src: string): string {\n let out = '';\n let i = 0;\n // The last significant character written outside a string, for key detection.\n let prev = '';\n while (i < src.length) {\n const c = src[i];\n if (c === '\"' || c === \"'\") {\n const q = c;\n let body = '';\n i++;\n while (i < src.length && src[i] !== q) {\n const ch = src[i];\n if (ch === '\\\\' && i + 1 < src.length) {\n const next = src[i + 1];\n // `\\'` is not a JSON escape.\n body += next === \"'\" ? \"'\" : ch + next;\n i += 2;\n continue;\n }\n if (ch === '\\n') {body += '\\\\n';}\n else if (ch === '\\r') {body += '\\\\r';}\n else if (ch === '\\t') {body += '\\\\t';}\n else if (ch === '\"') {body += '\\\\\"';}\n else {body += ch;}\n i++;\n }\n i++; // closing quote\n out += `\"${body}\"`;\n prev = '\"';\n continue;\n }\n if (c === ',') {\n // Trailing comma: the next significant character closes the container.\n let j = i + 1;\n while (j < src.length && /\\s/.test(src[j])) {j++;}\n if (src[j] === '}' || src[j] === ']') {i++; continue;}\n }\n if ((prev === '{' || prev === ',') && /[A-Za-z_$]/.test(c)) {\n const m = /^([A-Za-z_$][\\w$-]*)(\\s*):/.exec(src.slice(i));\n if (m) {\n out += `\"${m[1]}\"${m[2]}:`;\n i += m[0].length;\n prev = ':';\n continue;\n }\n }\n out += c;\n if (!/\\s/.test(c)) {prev = c;}\n i++;\n }\n return out;\n}\n\nfunction tryParse(text: string): LenientJsonResult {\n try {\n return { ok: true, value: JSON.parse(text) };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\n }\n}\n\nconst MAX_STARTS = 24;\n\n/**\n * Strip `<think>`, look inside code fences, take the first balanced `{…}` or\n * `[…]` that parses (after one repair pass for the common slips) and return\n * it. A reply with no JSON, or one cut off before it closed, is `ok: false`.\n * `T` is only the caller's claim about the shape; validate before trusting it.\n */\nexport function parseLenientJson<T = unknown>(raw: string): LenientJsonResult<T> {\n const text = stripReasoning(typeof raw === 'string' ? raw : '');\n if (!text) {return { ok: false, error: 'The reply was empty.' };}\n let firstError = '';\n let sawStart = false;\n let truncated = false;\n for (const source of [...fencedBodies(text), text]) {\n let from = 0;\n let attempts = 0;\n while (attempts < MAX_STARTS) {\n const rel = source.slice(from).search(/[{[]/);\n if (rel < 0) {break;}\n const start = from + rel;\n sawStart = true;\n attempts++;\n const span = balancedSpan(source, start);\n if (!span) {\n truncated = true;\n break;\n }\n const slice = source.slice(start, span.end);\n const direct = tryParse(slice);\n if (direct.ok) {return direct as LenientJsonResult<T>;}\n const fixed = tryParse(repairJson(slice));\n if (fixed.ok) {return fixed as LenientJsonResult<T>;}\n if (!firstError && !direct.ok) {firstError = direct.error;}\n from = span.end;\n }\n }\n if (!sawStart) {return { ok: false, error: 'No JSON object found in the reply.' };}\n if (firstError) {return { ok: false, error: `Invalid JSON: ${firstError}` };}\n return { ok: false, error: truncated ? 'The JSON was cut off before it closed.' : 'No JSON object found in the reply.' };\n}\n\n/** Longest earlier reply quoted back in the repair turn, in characters. */\nconst REPAIR_ECHO_CHARS = 4000;\n\n/**\n * The one-shot repair turn: append these two messages to the original ones\n * and ask again. The failed reply goes back as the assistant turn (reasoning\n * removed, capped), then the parse error.\n */\nexport function jsonRepairMessages(previousRaw: string, error: string): AiMessage[] {\n const previous = stripReasoning(previousRaw).slice(0, REPAIR_ECHO_CHARS);\n return [\n { role: 'assistant', content: previous || '(empty reply)' },\n { role: 'user', content: `Your previous reply was not valid JSON (${error}). Reply with the JSON object only.` },\n ];\n}\n\n/**\n * The prompt line that fixes the reply shape: a one-line example object.\n * Pass the example as a value (serialised on one line) or as a string.\n */\nexport function JSON_SHAPE_HINT(example: unknown): string {\n const shape = typeof example === 'string' ? example : JSON.stringify(example);\n return `Reply with a JSON object only, in exactly this shape and with no other text, no code fence and no comments: ${shape}`;\n}\n","/**\n * Fit a markdown document to a model's context window by whole sections, and\n * say how much of it fit (never a silent cut). Used by `/summary`, `/title`\n * and Properties suggestions. Pure, no DOM.\n *\n * The kept text is always a prefix of the document: the frontmatter and the\n * opening sections, ending on a heading boundary. If even the first section\n * is too big it is cut on a blank line between blocks. A fenced code block is\n * never split, so a fence that opens inside the kept text also closes in it.\n */\n\n/** Characters per token the budget assumes (a little pessimistic for English). */\nexport const CHARS_PER_TOKEN = 3.5;\n\nexport interface DocumentBudgetOptions {\n /** The model's context window in tokens. */\n contextTokens: number;\n /** Tokens the answer may use. */\n outputTokens: number;\n /** Tokens for the system prompt, instructions and slack. */\n reserveTokens: number;\n}\n\nexport interface BudgetedDocument {\n /** The markdown to send: frontmatter plus the leading sections that fit. */\n text: string;\n /** Share of the document in `text`, 0 to 1 (1 when nothing was cut). */\n coverage: number;\n truncated: boolean;\n /** Sections in `text` (a partly kept first section counts as one). */\n sectionsIncluded: number;\n sectionsTotal: number;\n}\n\nexport interface HeadingOutlineEntry {\n /** 1 to 6. */\n level: number;\n text: string;\n}\n\ninterface Line {\n text: string;\n start: number;\n}\n\nconst FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})/;\nconst HEADING = /^ {0,3}(#{1,6})[ \\t]+(.*?)[ \\t]*$/;\nconst FRONTMATTER = /^---[ \\t]*\\r?\\n[\\s\\S]*?\\r?\\n(?:---|\\.\\.\\.)[ \\t]*(?:\\r?\\n|$)/;\n\nfunction frontmatterEnd(markdown: string): number {\n const m = FRONTMATTER.exec(markdown);\n return m ? m[0].length : 0;\n}\n\nfunction splitLines(markdown: string, from: number): Line[] {\n const lines: Line[] = [];\n let pos = from;\n while (pos < markdown.length) {\n const nl = markdown.indexOf('\\n', pos);\n const end = nl < 0 ? markdown.length : nl;\n lines.push({ text: markdown.slice(pos, end).replace(/\\r$/, ''), start: pos });\n pos = nl < 0 ? markdown.length : nl + 1;\n }\n return lines;\n}\n\n/** Per line: true when the line is inside a fenced code block (fence lines included). */\nfunction fenceMask(lines: readonly Line[]): boolean[] {\n const mask: boolean[] = [];\n let fence: { char: string; len: number } | null = null;\n for (const line of lines) {\n if (!fence) {\n const m = FENCE_OPEN.exec(line.text);\n if (m) {\n fence = { char: m[1][0], len: m[1].length };\n mask.push(true);\n } else {\n mask.push(false);\n }\n continue;\n }\n mask.push(true);\n const close = /^ {0,3}(`{3,}|~{3,})[ \\t]*$/.exec(line.text);\n if (close && close[1][0] === fence.char && close[1].length >= fence.len) {fence = null;}\n }\n return mask;\n}\n\n/** Heading levels and texts, skipping frontmatter and fenced code. */\nexport function headingsOutline(markdown: string): HeadingOutlineEntry[] {\n const lines = splitLines(markdown, frontmatterEnd(markdown));\n const mask = fenceMask(lines);\n const out: HeadingOutlineEntry[] = [];\n lines.forEach((line, i) => {\n if (mask[i]) {return;}\n const m = HEADING.exec(line.text);\n if (!m) {return;}\n const text = m[2].replace(/[ \\t]+#+$/, '').trim();\n if (text) {out.push({ level: m[1].length, text });}\n });\n return out;\n}\n\n/** Offsets where each section starts; the first is always 0 (frontmatter and any preamble belong to it). */\nfunction sectionStarts(markdown: string, lines: readonly Line[], mask: readonly boolean[]): number[] {\n const starts = [0];\n const bodyStart = lines.length > 0 ? lines[0].start : markdown.length;\n lines.forEach((line, i) => {\n if (mask[i] || !HEADING.test(line.text)) {return;}\n // A heading with nothing before it (apart from frontmatter) opens section 0.\n if (markdown.slice(bodyStart, line.start).trim() === '') {return;}\n starts.push(line.start);\n });\n return starts;\n}\n\n/** Offsets where a block starts: a non-blank line after a blank one that is not inside a fence. */\nfunction blockStarts(lines: readonly Line[], mask: readonly boolean[]): number[] {\n const starts: number[] = [];\n for (let i = 1; i < lines.length; i++) {\n if (lines[i].text.trim() === '') {continue;}\n if (lines[i - 1].text.trim() === '' && !mask[i - 1]) {starts.push(lines[i].start);}\n }\n return starts;\n}\n\n/**\n * Keep the frontmatter and as many whole leading sections as fit in\n * `contextTokens - outputTokens - reserveTokens` (at `CHARS_PER_TOKEN`\n * characters a token). A first section bigger than the budget is cut on a\n * block boundary instead, and the first block is kept whatever the budget.\n */\nexport function budgetDocument(markdown: string, opts: DocumentBudgetOptions): BudgetedDocument {\n const total = markdown.length;\n const budgetTokens = opts.contextTokens - opts.outputTokens - opts.reserveTokens;\n const budgetChars = Math.max(0, Math.floor(budgetTokens * CHARS_PER_TOKEN));\n const fmEnd = frontmatterEnd(markdown);\n const lines = splitLines(markdown, fmEnd);\n const mask = fenceMask(lines);\n const starts = sectionStarts(markdown, lines, mask);\n const sectionsTotal = starts.length;\n\n if (total <= budgetChars || total === 0) {\n return { text: markdown, coverage: 1, truncated: false, sectionsIncluded: sectionsTotal, sectionsTotal };\n }\n\n // Cut points are section starts after the first section (and the end).\n let cut = 0;\n let sectionsIncluded = 0;\n for (let i = 1; i < starts.length; i++) {\n if (starts[i] > budgetChars) {break;}\n cut = starts[i];\n sectionsIncluded = i;\n }\n\n if (cut === 0) {\n // The first section alone is over budget: whole blocks of it, at least the first.\n const firstEnd = starts.length > 1 ? starts[1] : total;\n const blocks = blockStarts(lines, mask).filter((s) => s < firstEnd);\n cut = blocks.length > 0 ? blocks[0] : firstEnd;\n for (const s of blocks) {\n if (s > budgetChars) {break;}\n cut = Math.max(cut, s);\n }\n sectionsIncluded = 1;\n }\n\n if (cut >= total) {\n return { text: markdown, coverage: 1, truncated: false, sectionsIncluded: sectionsTotal, sectionsTotal };\n }\n const text = markdown.slice(0, cut).trimEnd();\n return {\n text,\n coverage: Math.min(0.999, Math.max(0.001, cut / total)),\n truncated: true,\n sectionsIncluded,\n sectionsTotal,\n };\n}\n","/**\n * Summary prompts (the `summary` feature: `/summary` and `/outline`). Pure:\n * builds the chat messages from the budgeted document and tidies the reply\n * before it is previewed and inserted.\n *\n * TL;DR is wrapped in a `[!NOTE]` callout whose first line is **Summary**\n * (D11: the GFM five are the only kinds that round-trip on GitHub). Outline\n * stays a plain nested list.\n */\n\nimport type { AiMessage } from '../types';\nimport { rewriteVisibleText } from './rewrite';\n\nexport type AiSummaryKind = 'tldr' | 'outline';\n\nexport interface SummaryPromptInput {\n /** The document as markdown, already through `budgetDocument`. */\n document: string;\n kind: AiSummaryKind;\n /** Share of the document included, 0 to 1 (below 1 means it was cut). */\n coverage: number;\n}\n\n/** A document shorter than this gets a one-paragraph TL;DR instead of bullets. */\nexport const SUMMARY_SHORT_DOCUMENT_CHARS = 1200;\n\nconst COMMON_RULES = [\n 'Reply with Markdown only. No preamble, no closing remarks, no code fence around the answer and no title line.',\n 'Use only what the document says. Do not invent facts, names or numbers.',\n 'Write in the language of the document.',\n];\n\nexport const SUMMARY_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. You write a short TL;DR of the document the user gives you.',\n '',\n 'Rules:',\n '- Write 3 to 6 bullet points, one line each, most important first. Each bullet is a list item starting with \"- \".',\n '- Plain wording that someone who has not read the document can follow. No heading, no \"Summary\" label.',\n ...COMMON_RULES.map((r) => `- ${r}`),\n].join('\\n');\n\nexport const OUTLINE_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. You write an outline of the document the user gives you.',\n '',\n 'Rules:',\n '- Reply with a nested Markdown list: top-level items are the main sections, indented items (two spaces per level, \"- \" markers) are their subsections or key points. At most three levels.',\n '- If the document has headings, follow them in order and keep their wording. If it has none, propose short headings that fit its parts.',\n '- Each item is a few words, not a sentence. No heading syntax (#) and no numbering.',\n ...COMMON_RULES.map((r) => `- ${r}`),\n].join('\\n');\n\n/** Neutralise a closing tag inside user text so it cannot end its block early. */\nfunction fence(text: string, tag: string): string {\n return text.split(`</${tag}>`).join(`<\\\\/${tag}>`);\n}\n\nexport function coveragePercent(coverage: number): number {\n return Math.max(1, Math.min(99, Math.round(coverage * 100)));\n}\n\nexport function buildSummaryMessages(input: SummaryPromptInput): AiMessage[] {\n const parts: string[] = [];\n if (input.kind === 'tldr') {\n parts.push(input.document.length < SUMMARY_SHORT_DOCUMENT_CHARS\n ? 'This document is short: write one paragraph of two to four sentences instead of bullet points.'\n : 'Write the TL;DR as 3 to 6 bullet points.');\n } else {\n parts.push('Write the outline.');\n }\n if (input.coverage < 1) {\n parts.push(`Only the first ${coveragePercent(input.coverage)}% of the document is included below; it was cut to fit. Cover only what is shown and do not guess at the rest.`);\n }\n parts.push(`Document:\\n<document>\\n${fence(input.document, 'document')}\\n</document>`);\n parts.push(input.kind === 'tldr' ? 'Reply with the TL;DR only.' : 'Reply with the outline list only.');\n return [\n { role: 'system', content: input.kind === 'tldr' ? SUMMARY_SYSTEM_PROMPT : OUTLINE_SYSTEM_PROMPT },\n { role: 'user', content: parts.join('\\n\\n') },\n ];\n}\n\n/** Output budget: scales with the input, within a range a small model handles. */\nexport function summaryMaxTokens(kind: AiSummaryKind, inputChars: number): number {\n const approxTokens = Math.ceil(inputChars / 3.5);\n return kind === 'tldr'\n ? Math.max(256, Math.min(700, Math.ceil(approxTokens * 0.2) + 128))\n : Math.max(384, Math.min(1200, Math.ceil(approxTokens * 0.3) + 160));\n}\n\n/** The text to show while it streams (reasoning hidden). */\nexport function summaryVisibleText(raw: string): string {\n return rewriteVisibleText(raw);\n}\n\n/** Drop a wrapping code fence and a \"Here is the summary:\" lead-in. */\nfunction tidy(raw: string, kind: AiSummaryKind): string {\n let text = rewriteVisibleText(raw).trim();\n let lines = text.split('\\n');\n const dropLeadIn = (): void => {\n if (lines.length > 1 && /^(here('s| is| are)|sure|certainly|of course)\\b[^\\n]*:\\s*$/i.test(lines[0].trim())) {\n lines.shift();\n while (lines.length > 0 && lines[0].trim() === '') {lines.shift();}\n }\n };\n // The lead-in goes first so a fence that follows it is the wrapper.\n dropLeadIn();\n text = lines.join('\\n').trim();\n const fenced = /^```(?:markdown|md|text)?[ \\t]*\\n([\\s\\S]*?)\\n```$/i.exec(text);\n if (fenced) {text = fenced[1].trim();}\n lines = text.split('\\n');\n // A lead-in inside the fence.\n dropLeadIn();\n // The callout supplies its own label.\n if (kind === 'tldr' && lines.length > 1 && /^(?:#{1,6}\\s*)?(?:\\*\\*|__)?\\s*(?:summary|tl;?dr)\\s*:?\\s*(?:\\*\\*|__)?\\s*:?\\s*$/i.test(lines[0].trim())) {\n lines.shift();\n }\n return lines.join('\\n').trim();\n}\n\n/**\n * A summary that stopped at the length limit: the last word may be a\n * fragment (a cut `#planning` reads as the tag `#pl`), so it goes unless the\n * text ends on punctuation, with any list marker or quote left bare, and an\n * ellipsis marks the cut.\n */\nfunction dropCutFragment(text: string): string {\n let t = text.trimEnd();\n if (!t) {return '';}\n if (!/[.!?;:)\\]\"'”’`*_]$/.test(t)) {t = t.replace(/\\S+$/, '').trimEnd();}\n // A line left with only a list marker, a quote marker or a heading mark.\n t = t.replace(/(^|\\n)[ \\t]*(?:[-*+]|\\d+[.)]|>|#{1,6})?[ \\t]*$/, '').trimEnd();\n if (!t) {return '';}\n return /[\\p{L}\\p{N}]$/u.test(t) ? `${t}…` : `${t} …`;\n}\n\n/**\n * The preview once the stream ends: the body `finalizeSummary` will insert,\n * without the callout wrapper and the coverage line (the popover shows those\n * apart), so the cut fragment and lead-in are gone before Insert.\n */\nexport function summaryPreviewText(raw: string, kind: AiSummaryKind, cutOff = false): string {\n return cutOff ? dropCutFragment(tidy(raw, kind)) : tidy(raw, kind);\n}\n\n/**\n * The markdown to insert. TL;DR becomes a `[!NOTE]` callout headed\n * **Summary**; Outline stays the list. When the document was cut\n * (`coverage` below 1) a \"Covers the first N% of the document.\" line closes\n * it. An empty reply stays empty so the caller can report a bad output.\n * `cutOff` (the stream stopped at the length limit) drops a trailing\n * fragment and ends the text with an ellipsis.\n */\nexport function finalizeSummary(raw: string, kind: AiSummaryKind, coverage = 1, cutOff = false): string {\n const body = cutOff ? dropCutFragment(tidy(raw, kind)) : tidy(raw, kind);\n if (!body) {return '';}\n const note = coverage < 1 ? `Covers the first ${coveragePercent(coverage)}% of the document.` : '';\n if (kind === 'outline') {\n return note ? `${body}\\n\\n${note}` : body;\n }\n const inner = ['**Summary**', '', body, ...(note ? ['', note] : [])].join('\\n');\n return ['[!NOTE]', inner].join('\\n').split('\\n').map((l) => (l ? `> ${l}` : '>')).join('\\n');\n}\n","/**\n * Title suggestions (`/title` and \"Suggest titles…\", the `title` feature).\n * Pure: the prompt asks for a small JSON object, `parseTitles` validates it.\n * The document is the budgeted prefix plus the full headings list, so a long\n * document is still judged as a whole.\n */\n\nimport { JSON_SHAPE_HINT, parseLenientJson } from '../json';\nimport type { AiMessage } from '../types';\nimport type { HeadingOutlineEntry } from '../documentBudget';\n\nexport const TITLE_MAX_CHARS = 120;\n/** Longest headings list sent, so a huge outline cannot eat the budget. */\nexport const TITLE_OUTLINE_MAX_ITEMS = 60;\n\nexport interface TitlePromptInput {\n /** The document as markdown, already through `budgetDocument`. */\n document: string;\n /** All headings of the full document (not only the budgeted part). */\n outline: readonly HeadingOutlineEntry[];\n /** The current title, so the model does not hand it back. */\n existingTitle?: string;\n count?: number;\n}\n\nexport const TITLE_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. You suggest titles for the document the user gives you.',\n '',\n 'Rules:',\n '- Each title is specific to this document, in plain wording, in Title Case or sentence case to match the document, under 80 characters, with no trailing period and no quotation marks.',\n '- Make the titles different from each other in angle (descriptive, benefit-led, short).',\n '- Write in the language of the document.',\n '- Reply with the JSON object only.',\n].join('\\n');\n\nfunction fence(text: string, tag: string): string {\n return text.split(`</${tag}>`).join(`<\\\\/${tag}>`);\n}\n\n/** The headings as an indented list, capped. Shared by the title and properties prompts. */\nexport function renderHeadingOutline(outline: readonly HeadingOutlineEntry[], max = TITLE_OUTLINE_MAX_ITEMS): string {\n const shown = outline.slice(0, max).map((h) => `${' '.repeat(Math.max(0, h.level - 1))}- ${h.text}`);\n if (outline.length > max) {shown.push(`(${outline.length - max} more headings)`);}\n return shown.join('\\n');\n}\n\nexport function buildTitleMessages(input: TitlePromptInput): AiMessage[] {\n const count = Math.max(1, Math.min(8, input.count ?? 3));\n const placeholders = Array.from({ length: count }, (_, i) => `title ${i + 1}`);\n const parts: string[] = [`Suggest ${count} titles for this document.`];\n const existing = (input.existingTitle ?? '').trim();\n if (existing) {parts.push(`The current title is \"${existing.replace(/\"/g, \"'\")}\". Do not repeat it.`);}\n if (input.outline.length > 0) {\n parts.push(`Headings of the whole document:\\n<headings>\\n${fence(renderHeadingOutline(input.outline), 'headings')}\\n</headings>`);\n }\n parts.push(`Document:\\n<document>\\n${fence(input.document, 'document')}\\n</document>`);\n parts.push(JSON_SHAPE_HINT({ titles: placeholders }));\n return [\n { role: 'system', content: TITLE_SYSTEM_PROMPT },\n { role: 'user', content: parts.join('\\n\\n') },\n ];\n}\n\n/** Output budget: a few short strings. */\nexport function titleMaxTokens(count = 3): number {\n return 128 + 48 * Math.max(1, count);\n}\n\nfunction cleanTitle(value: string): string {\n let t = value.replace(/\\s+/g, ' ').trim();\n t = t.replace(/^[\"“'‘]+|[\"”'’]+$/g, '').trim();\n t = t.replace(/^#{1,6}\\s+/, '').replace(/[.。]+$/, '').trim();\n if (t.length > TITLE_MAX_CHARS) {t = t.slice(0, TITLE_MAX_CHARS).trimEnd();}\n return t;\n}\n\nconst sameTitle = (a: string, b: string): boolean => a.toLowerCase() === b.toLowerCase();\n\nexport type ParsedTitles = { ok: true; titles: string[] } | { ok: false; error: string };\n\n/**\n * The titles in a reply: `{ \"titles\": [...] }` (a bare array or a single\n * `title` string is accepted too), trimmed to 120 characters, de-duplicated\n * case-insensitively, minus the existing title. No usable title is a failure\n * so the caller can repair or report `badOutput`.\n */\nexport function parseTitles(raw: string, existingTitle?: string): ParsedTitles {\n const parsed = parseLenientJson(raw);\n if (!parsed.ok) {return { ok: false, error: parsed.error };}\n const v = parsed.value;\n let list: unknown;\n if (Array.isArray(v)) {list = v;}\n else if (v && typeof v === 'object') {\n const o = v as Record<string, unknown>;\n list = o.titles ?? (typeof o.title === 'string' ? [o.title] : undefined);\n }\n if (!Array.isArray(list)) {return { ok: false, error: 'The JSON has no \"titles\" array.' };}\n const existing = (existingTitle ?? '').trim();\n const titles: string[] = [];\n for (const item of list) {\n if (typeof item !== 'string') {continue;}\n const t = cleanTitle(item);\n if (!t || (existing && sameTitle(t, existing)) || titles.some((x) => sameTitle(x, t))) {continue;}\n titles.push(t);\n }\n if (titles.length === 0) {return { ok: false, error: 'The \"titles\" array has no usable title.' };}\n return { ok: true, titles };\n}\n","/**\n * Properties suggestions (the `frontmatter` feature: \"Suggest\" in the\n * sidebar's Properties header). Pure: the prompt asks for a title, a\n * description and tags as JSON; `parsePropertySuggestions` validates the\n * reply and normalises tags through the shared tag syntax, so what reaches the\n * UI is already safe to apply.\n */\n\nimport type { ParsedEntry } from '../../shared/frontmatterYaml';\nimport { normalizeTag } from '../../shared/tagSyntax';\nimport { JSON_SHAPE_HINT, parseLenientJson } from '../json';\nimport type { HeadingOutlineEntry } from '../documentBudget';\nimport type { AiMessage } from '../types';\nimport { renderHeadingOutline } from './title';\n\n/** A frontmatter property as the sidebar holds it. */\nexport type AiFrontmatterEntry = ParsedEntry;\n\nexport const PROPERTY_MAX_TAGS = 8;\nexport const PROPERTY_DESCRIPTION_MAX_CHARS = 200;\nexport const PROPERTY_TITLE_MAX_CHARS = 120;\n/** Vocabulary tags listed in the prompt at most. */\nexport const PROPERTY_VOCABULARY_MAX = 80;\n\nexport interface PropertiesPromptInput {\n /** The document as markdown, already through `budgetDocument`. */\n document: string;\n /** All headings of the full document. */\n outline: readonly HeadingOutlineEntry[];\n /** The properties the document already has. */\n existing: readonly AiFrontmatterEntry[];\n /** Tags used elsewhere in the workspace, most used first. */\n vocabulary: readonly string[];\n}\n\nexport const PROPERTIES_SYSTEM_PROMPT = [\n 'You are a writing assistant inside a Markdown editor. You suggest frontmatter properties for the document the user gives you: a title, a description and tags.',\n '',\n 'Rules:',\n '- title: specific to the document, under 80 characters, no trailing period and no quotation marks.',\n '- description: one or two plain sentences under 160 characters that say what the document is about. No marketing wording.',\n '- tags: 3 to 6 short topic tags, lowercase, words joined by hyphens, no \"#\" and no spaces. Nested tags use a slash, like project/active.',\n '- Prefer tags from the list of existing tags when they fit. Add a new tag only when none of them does.',\n '- Do not repeat a tag the document already has.',\n '- Write in the language of the document.',\n '- Reply with the JSON object only.',\n].join('\\n');\n\nfunction fence(text: string, tag: string): string {\n return text.split(`</${tag}>`).join(`<\\\\/${tag}>`);\n}\n\nfunction entryValue(value: string | string[]): string {\n return Array.isArray(value) ? value.join(', ') : value;\n}\n\n/** The tags the entries already carry, normalised (`tags:` or `tag:`, array or comma list). */\nexport function existingTagsOf(existing: readonly AiFrontmatterEntry[]): string[] {\n const out: string[] = [];\n for (const entry of existing) {\n if (!/^tags?$/i.test(entry.key)) {continue;}\n const parts = Array.isArray(entry.value) ? entry.value : entry.value.split(/[,\\s]+/);\n for (const p of parts) {\n const t = normalizeTag(p);\n if (t && !out.includes(t)) {out.push(t);}\n }\n }\n return out;\n}\n\nexport function buildPropertiesMessages(input: PropertiesPromptInput): AiMessage[] {\n const parts: string[] = ['Suggest a title, a description and tags for this document.'];\n const props = input.existing.filter((e) => entryValue(e.value).trim() !== '');\n if (props.length > 0) {\n parts.push(`Properties it already has:\\n<properties>\\n${fence(props.map((e) => `${e.key}: ${entryValue(e.value)}`).join('\\n'), 'properties')}\\n</properties>`);\n }\n const vocabulary = input.vocabulary.slice(0, PROPERTY_VOCABULARY_MAX);\n if (vocabulary.length > 0) {\n parts.push(`Prefer these existing tags when they fit:\\n<tags>\\n${fence(vocabulary.join(', '), 'tags')}\\n</tags>`);\n }\n if (input.outline.length > 0) {\n parts.push(`Headings of the whole document:\\n<headings>\\n${fence(renderHeadingOutline(input.outline), 'headings')}\\n</headings>`);\n }\n parts.push(`Document:\\n<document>\\n${fence(input.document, 'document')}\\n</document>`);\n parts.push(JSON_SHAPE_HINT({ title: 'the title', description: 'one or two sentences', tags: ['tag-one', 'tag-two', 'tag-three'] }));\n return [\n { role: 'system', content: PROPERTIES_SYSTEM_PROMPT },\n { role: 'user', content: parts.join('\\n\\n') },\n ];\n}\n\n/** Output budget: three short fields. */\nexport function propertiesMaxTokens(): number {\n return 384;\n}\n\nexport interface SuggestedTag {\n /** Normalised: lowercase, no `#`. */\n tag: string;\n /** Already used somewhere in the workspace (it is in the vocabulary). */\n known: boolean;\n}\n\nexport interface PropertySuggestions {\n title?: string;\n description?: string;\n tags: SuggestedTag[];\n}\n\nexport type ParsedPropertySuggestions = { ok: true; suggestions: PropertySuggestions } | { ok: false; error: string };\n\n/** A model's tag as a valid one: spaces to hyphens, stray punctuation dropped, `#` removed. */\nfunction cleanTag(raw: string): string | null {\n const t = normalizeTag(raw.trim().replace(/\\s+/g, '-').replace(/[^A-Za-z0-9_/#-]/g, ''));\n if (!t) {return null;}\n const segments = t.split('/').map((s) => s.replace(/^-+|-+$/g, '')).filter(Boolean);\n const joined = segments.join('/');\n return /[a-z]/.test(joined) ? joined : null;\n}\n\nfunction oneLine(value: unknown, max: number): string {\n if (typeof value !== 'string') {return '';}\n const t = value.replace(/\\s+/g, ' ').trim().replace(/^[\"“]+|[\"”]+$/g, '').trim();\n return t.length > max ? t.slice(0, max).trimEnd() : t;\n}\n\nfunction firstScalar(existing: readonly AiFrontmatterEntry[], key: string): string {\n const entry = existing.find((e) => e.key.toLowerCase() === key);\n return entry ? entryValue(entry.value).trim() : '';\n}\n\n/**\n * Validate a reply: title and description trimmed to length and dropped when\n * empty or equal to what the document has, tags normalised, marked `known`\n * from the vocabulary, minus tags already present, de-duplicated and capped at\n * 8. A reply with no title, description or tag at all is a failure; one whose\n * suggestions are all already present succeeds with nothing left in it.\n */\nexport function parsePropertySuggestions(\n raw: string,\n existing: readonly AiFrontmatterEntry[],\n vocabulary: readonly string[],\n): ParsedPropertySuggestions {\n const parsed = parseLenientJson(raw);\n if (!parsed.ok) {return { ok: false, error: parsed.error };}\n const v = parsed.value;\n if (!v || typeof v !== 'object' || Array.isArray(v)) {return { ok: false, error: 'The JSON is not an object.' };}\n const o = v as Record<string, unknown>;\n\n const suggestions: PropertySuggestions = { tags: [] };\n const title = oneLine(o.title, PROPERTY_TITLE_MAX_CHARS).replace(/[.。]+$/, '');\n if (title && title.toLowerCase() !== firstScalar(existing, 'title').toLowerCase()) {suggestions.title = title;}\n const description = oneLine(o.description, PROPERTY_DESCRIPTION_MAX_CHARS);\n if (description && description.toLowerCase() !== firstScalar(existing, 'description').toLowerCase()) {suggestions.description = description;}\n\n const rawTags: unknown[] = Array.isArray(o.tags) ? o.tags : typeof o.tags === 'string' ? o.tags.split(/[,\\n]+/) : [];\n const present = new Set(existingTagsOf(existing));\n const known = new Set<string>();\n for (const t of vocabulary) {\n const n = normalizeTag(t);\n if (n) {known.add(n);}\n }\n const seen = new Set<string>();\n for (const item of rawTags) {\n if (typeof item !== 'string') {continue;}\n const tag = cleanTag(item);\n if (!tag || present.has(tag) || seen.has(tag)) {continue;}\n seen.add(tag);\n suggestions.tags.push({ tag, known: known.has(tag) });\n if (suggestions.tags.length >= PROPERTY_MAX_TAGS) {break;}\n }\n\n // Everything filtered out as already present is a valid \"nothing new\"; no content at all is a bad reply.\n const hadContent = oneLine(o.title, 1) !== '' || oneLine(o.description, 1) !== '' || rawTags.some((t) => typeof t === 'string' && cleanTag(t));\n if (!hadContent) {return { ok: false, error: 'The JSON has no usable title, description or tags.' };}\n return { ok: true, suggestions };\n}\n","/**\n * Privacy copy for cloud engines: the four lines every surface shows (the\n * privacy card, the disclaimer, the consent dialogs), what each feature\n * sends, each provider's policy link, and the consent dialog text both hosts\n * render (VS Code's modal, Desktop's native dialog; Desktop has a Rust port\n * checked against `test/ai-fixtures/consent/`).\n *\n * No claims about any provider's training or retention: those are set by the\n * provider's terms and change. The copy says \"may\" and points at the policy.\n */\n\nimport { getAiFeature } from './features';\nimport { hostOfUrl } from './locality';\nimport { isHostEngineKind } from './schema';\nimport { AI_ENGINE_PRESETS, type AiEnginePreset, type AiProviderPolicy } from './presets';\nimport type { AiConsentRecord, AiSendsScope } from './types';\n\n/** The four lines of the privacy card, in display order. */\nexport const AI_CLOUD_PRIVACY = {\n leaves: 'Text you ask about is sent over the internet to the provider and processed on their servers.',\n terms:\n \"API traffic is covered by the provider's API terms, which differ from its consumer apps. \" +\n 'Whether it is retained or used for training is set by those terms and may change; read them before you decide.',\n local: 'A local engine keeps everything on this machine or your network.',\n keys: \"Your API key stays in this app's secure storage on this machine.\",\n} as const;\n\n/**\n * The privacy card's lines for an engine built into the host (VS Code's\n * Copilot models, `vscode-lm`): no API key, and the text goes through the\n * host to GitHub's Copilot service under the user's Copilot plan. No claim\n * about how a plan handles it; the policy link says.\n */\nexport const AI_HOST_PRIVACY = {\n leaves: 'Text you ask about goes through VS Code to GitHub’s Copilot service and is processed on GitHub’s servers, under your Copilot plan.',\n terms: 'Whether it is retained or used for training is set by your plan’s terms, which differ between individual and business plans and may change; read them before you decide.',\n local: AI_CLOUD_PRIVACY.local,\n permission: 'VS Code also asks for its own permission the first time MikeDown uses Copilot.',\n} as const;\n\n/**\n * The sentence about the consent step, for the cloud privacy card: whether\n * the host asks before a cloud engine's first request (`hostAsks`: it has\n * `requestCloudConsent`) or sends without asking.\n */\nexport function aiConsentStepNote(hostAsks: boolean): string {\n return hostAsks\n ? 'MikeDown asks before a cloud engine\\'s first request in each workspace or folder.'\n : 'Once allowed, this app sends requests to cloud engines without asking again.';\n}\n\n/** What a request of each scope sends, as the object of \"sends …\". */\nexport const AI_SENDS_TEXT: Readonly<Record<AiSendsScope, string>> = {\n selection: 'the selected text and the paragraph before and after it',\n document: 'the whole document, up to what fits the model',\n documentAndTags: 'the document, its frontmatter and the tag names used in this workspace',\n};\n\n/** For a request whose feature is unknown. */\nexport const AI_SENDS_FALLBACK = 'the text you ask the AI about';\n\n/**\n * What a request sends, as the object of a sentence (\"MikeDown sends …\").\n * `sends` wins (whole-document Translate runs as the `rewrite` feature);\n * else the feature's own scope; else a generic phrase.\n */\nexport function whatIsSent(feature?: string, sends?: AiSendsScope): string {\n const scope = sends ?? (feature ? getAiFeature(feature)?.sends : undefined);\n return scope ? AI_SENDS_TEXT[scope] : AI_SENDS_FALLBACK;\n}\n\n/** The policy of the cloud preset on `host` (an engine's base URL host), if one matches. */\nexport function aiProviderPolicyForHost(host: string | null | undefined, presets: readonly AiEnginePreset[] = AI_ENGINE_PRESETS): AiProviderPolicy | undefined {\n const h = host?.trim().toLowerCase();\n if (!h) {return undefined;}\n return presets.find((p) => p.family === 'cloud' && p.policy && hostOfUrl(p.baseUrl) === h)?.policy;\n}\n\n/**\n * The policy for an engine, matched by its base URL's host (engines do not\n * store a preset); a host kind with no address (`vscode-lm`) by its kind.\n */\nexport function aiProviderPolicy(engine: { baseUrl: string; kind?: string }, presets: readonly AiEnginePreset[] = AI_ENGINE_PRESETS): AiProviderPolicy | undefined {\n if (engine.kind && isHostEngineKind(engine.kind)) {\n return presets.find((p) => p.family === 'host' && p.kind === engine.kind && p.policy)?.policy;\n }\n return aiProviderPolicyForHost(hostOfUrl(engine.baseUrl), presets);\n}\n\n/** The service a host kind sends text to, for the consent dialog's `host` (no URL to show). */\nconst HOST_KIND_SERVICE: Readonly<Record<string, string>> = { 'vscode-lm': 'GitHub Copilot' };\n\n/**\n * Where an engine sends text, as the consent dialog's `host`: the base URL's\n * host (\"api.anthropic.com\"), the URL itself when it has none, or the\n * service's name for a host kind (\"GitHub Copilot\" for `vscode-lm`).\n */\nexport function aiConsentHost(engine: { baseUrl: string; kind?: string }): string {\n const service = engine.kind ? HOST_KIND_SERVICE[engine.kind] : undefined;\n return service ?? hostOfUrl(engine.baseUrl) ?? engine.baseUrl;\n}\n\n/** Where a consent answer is remembered: VS Code says workspace, Desktop folder (or session without one). */\nexport type AiConsentScopeWord = 'workspace' | 'folder' | 'session';\n\nexport interface AiConsentTextInput {\n engineName: string;\n /** The engine's kind; a host kind (`vscode-lm`) reads as a service built into VS Code, not an address. */\n kind?: string;\n /** The engine's host (\"api.anthropic.com\"), or its base URL when it has none. */\n host: string;\n feature?: string;\n sends?: AiSendsScope;\n scopeWord: AiConsentScopeWord;\n /** The provider's policy (see `aiProviderPolicyForHost`). */\n policy?: AiProviderPolicy | null;\n /**\n * Spell the policy link out in the body, for a dialog that can neither\n * link nor spare a button for it (Desktop's native dialog). VS Code's modal\n * has a \"Read the provider's policy\" button instead.\n */\n policyLine?: boolean;\n}\n\nexport interface AiConsentText {\n title: string;\n /** Paragraphs separated by a blank line. */\n body: string;\n /** Remembered for the workspace or folder (for the session without one). */\n allow: string;\n /** This request only; nothing is remembered and the next request asks again. */\n allowOnce: string;\n /** Declines; the host does not ask about this engine again until it restarts. */\n deny: string;\n /** The policy button's label, when there is a policy and the body does not spell it out. */\n readPolicy?: string;\n}\n\n/** The consent dialog's words, the single source for both hosts. */\nexport function aiConsentText(input: AiConsentTextInput): AiConsentText {\n const { engineName: name, host, scopeWord, policy } = input;\n const sends = whatIsSent(input.feature, input.sends);\n // A host kind has no address: \"GitHub Copilot is a cloud engine at GitHub Copilot\" would read oddly.\n const paragraphs = input.kind && isHostEngineKind(input.kind)\n ? [\n `${name === host ? name : `${name} (${host})`} is a cloud service built into VS Code. If you allow it, this request sends ${sends}. ` +\n `It goes through VS Code over the internet and is processed on the service's servers. ${AI_HOST_PRIVACY.permission}`,\n AI_HOST_PRIVACY.terms,\n ]\n : [\n `${name} is a cloud engine at ${host}. If you allow it, this request sends ${sends}. ` +\n 'It goes over the internet and is processed on the provider\\'s servers.',\n policy?.note ? `${AI_CLOUD_PRIVACY.terms} ${policy.note}` : AI_CLOUD_PRIVACY.terms,\n ];\n if (policy && input.policyLine) {paragraphs.push(`${policy.label}: ${policy.url}`);}\n const allow = `Allow for this ${scopeWord}`;\n paragraphs.push(scopeWord === 'session'\n ? `You can use a local engine instead. ${allow} lasts until you quit MikeDown.`\n : `You can use a local engine instead. ${allow} is remembered; you can change it in AI settings \\u2192 Cloud consents.`);\n return {\n title: `Send text to ${name}?`,\n body: paragraphs.join('\\n\\n'),\n allow,\n allowOnce: 'Allow once',\n deny: 'Don\\'t allow',\n ...(policy && !input.policyLine ? { readPolicy: 'Read the provider\\'s policy' } : {}),\n };\n}\n\n/** Shown in the Cloud consents list when there is nothing stored. */\nexport const AI_NO_CONSENTS_TEXT = 'No cloud engine has been allowed here.';\n\n/** How each answer reads in the Cloud consents list. */\nconst CONSENT_ANSWER_TEXT: Readonly<Record<AiConsentRecord['answer'], string>> = {\n allow: 'Allowed',\n never: 'Not allowed',\n deny: 'Not allowed until restart',\n};\n\n/**\n * One line of the Cloud consents list, without the Forget button:\n * \"Anthropic · this workspace · Allowed\". A stored \"never\" (older versions)\n * reads \"Not allowed\"; this session's \"Don't allow\", \"Not allowed until restart\".\n */\nexport function aiConsentSummary(record: Pick<AiConsentRecord, 'answer' | 'scope'>, engineName: string): string {\n const where = record.scope === 'folder' ? 'this folder' : 'this workspace';\n return `${engineName} \\u00b7 ${where} \\u00b7 ${CONSENT_ANSWER_TEXT[record.answer] ?? 'Not allowed'}`;\n}\n","/**\n * The setup wizard's first decision, as plain data: which path to offer\n * first (a running local server, VS Code's Copilot models, or a cloud\n * provider), whether this machine can run models locally, and how big a\n * model fits (OQ11, OQ13). No DOM, no network: the host reports the\n * hardware (`hardwareInfo`), the running servers (`detectLocalServers`) and\n * its built-in models (`detectHostEngines`).\n *\n * Every size here is a hint for a 4-bit quantised model, never a gate: the\n * user can pick any model.\n */\n\nimport { AI_ENGINE_PRESETS } from './presets';\nimport type { AiHardwareInfo, AiHostEngineProbe, AiLocalServerProbe } from './types';\n\nconst GIB = 1024 ** 3;\n\n/** Reported memory runs a little under the nominal size (firmware, the iGPU's share): 31.3 GiB is a 32 GB machine. */\nconst NOMINAL_SLACK = 0.94;\n\n/** OQ11: Apple Silicon with this much unified memory can run local models. */\nexport const AI_LOCAL_MIN_UNIFIED_GB = 16;\n/** OQ11: any machine with this much memory can. */\nexport const AI_LOCAL_MIN_MEMORY_GB = 32;\n/** OQ11: or an NVIDIA GPU with this much video memory (Desktop only, where it can be read). */\nexport const AI_LOCAL_MIN_VRAM_GB = 8;\n\nexport type AiSetupPath = 'runningLocal' | 'vscodeLm' | 'cloud';\n\nexport interface AiSetupInput {\n /** `detectLocalServers` results (every candidate, running or not). */\n running: readonly AiLocalServerProbe[];\n /** `hardwareInfo()`, or null when the host cannot tell. */\n hardware: AiHardwareInfo | null;\n /** VS Code only: whether `vscode.lm` offers a Copilot model (see `vscodeLmAvailability`). */\n vscodeLm?: { available: boolean; models: number };\n}\n\nexport interface AiSetupRecommendation {\n /** What the wizard offers first. */\n path: AiSetupPath;\n /** OQ11: this machine is above the threshold, so local is shown as an option (still under Advanced unless a server runs). */\n localCapable: boolean;\n /** The model-size sentence for this machine (OQ11); empty without hardware info. */\n localHint: string;\n /**\n * Step 1's hardware line: \"Local is an option on this machine: Apple\n * Silicon, 32 GB. Models up to about 30B parameters run well.\" when\n * capable, the small-machine sentence when not, empty without hardware\n * info.\n */\n hardwareLine: string;\n /** `runningLocal`: the server to offer (most models first, then preset order). */\n running?: AiLocalServerProbe;\n /** Why, one short sentence per signal checked, in order (for tooltips, logs and tests). */\n reasons: string[];\n}\n\n/** Bytes to gigabytes, for display (\"32 GB\"). */\nexport function formatMemoryGb(bytes: number): string {\n return `${Math.max(1, Math.round(bytes / GIB))} GB`;\n}\n\nfunction atLeastGb(bytes: number | undefined, gb: number): boolean {\n return typeof bytes === 'number' && bytes >= gb * GIB * NOMINAL_SLACK;\n}\n\nfunction nvidiaVram(hw: AiHardwareInfo): number | undefined {\n return hw.gpu?.vendor === 'nvidia' ? hw.gpu.vramBytes : undefined;\n}\n\n/** OQ11's threshold: Apple Silicon with 16 GB or more, any machine with 32 GB or more, or an NVIDIA GPU with 8 GB of VRAM or more. */\nexport function isLocalCapable(hw: AiHardwareInfo | null): boolean {\n if (!hw) {return false;}\n if (hw.appleSilicon && atLeastGb(hw.totalMemoryBytes, AI_LOCAL_MIN_UNIFIED_GB)) {return true;}\n if (atLeastGb(hw.totalMemoryBytes, AI_LOCAL_MIN_MEMORY_GB)) {return true;}\n return atLeastGb(nvidiaVram(hw), AI_LOCAL_MIN_VRAM_GB);\n}\n\n/**\n * The memory a local model runs in: unified memory on Apple Silicon, an\n * NVIDIA card's VRAM when the host read it, else system memory (CPU\n * inference: slower, but it runs).\n */\nexport function modelMemoryBytes(hw: AiHardwareInfo): number {\n if (hw.appleSilicon) {return hw.totalMemoryBytes;}\n const vram = nvidiaVram(hw);\n return typeof vram === 'number' && vram > 0 ? vram : hw.totalMemoryBytes;\n}\n\ntype Band = 'small' | 'medium' | 'large';\n\n/** OQ11's bands: under 24 GB, 24 to 48 GB, above. */\nfunction memoryBand(bytes: number): Band {\n if (!atLeastGb(bytes, 24)) {return 'small';}\n return atLeastGb(bytes, 48) ? 'large' : 'medium';\n}\n\nconst SIZE_SENTENCES: Readonly<Record<Band, string>> = {\n small: 'Models up to about 8B parameters run well.',\n medium: 'Models up to about 30B parameters run well.',\n large: 'Larger models work, well beyond 30B parameters.',\n};\n\n/** OQ11's model-size sentence for a machine (\"Models up to about 30B parameters run well.\"); empty for null. */\nexport function localSizeHint(hw: AiHardwareInfo | null): string {\n return hw ? SIZE_SENTENCES[memoryBand(modelMemoryBytes(hw))] : '';\n}\n\n/** \"Apple Silicon, 32 GB\", \"64 GB of memory, NVIDIA GeForce RTX 4090 (24 GB)\", \"16 GB of memory\". */\nexport function describeHardware(hw: AiHardwareInfo): string {\n if (hw.appleSilicon) {return `Apple Silicon, ${formatMemoryGb(hw.totalMemoryBytes)}`;}\n const parts = [`${formatMemoryGb(hw.totalMemoryBytes)} of memory`];\n if (hw.gpu && hw.gpu.vendor !== 'apple') {\n const name = hw.gpu.name?.trim() || (hw.gpu.vendor === 'nvidia' ? 'NVIDIA GPU' : hw.gpu.vendor === 'amd' ? 'AMD GPU' : 'GPU');\n parts.push(hw.gpu.vramBytes ? `${name} (${formatMemoryGb(hw.gpu.vramBytes)})` : name);\n }\n return parts.join(', ');\n}\n\n/** Shown on a machine below the OQ11 threshold. */\nexport const AI_LOCAL_SMALL_MACHINE = 'This machine is on the small side for local models; cloud is the easier path.';\n\n/** Added when the host read no GPU (VS Code reads none): the hint comes from memory alone. */\nexport const AI_LOCAL_ROUGH_GUESS = 'A rough guess from memory alone; the graphics card was not checked.';\n\nfunction hardwareLine(hw: AiHardwareInfo | null, capable: boolean): string {\n if (!hw) {return '';}\n if (!capable) {return AI_LOCAL_SMALL_MACHINE;}\n const rough = !hw.appleSilicon && !hw.gpu ? ` ${AI_LOCAL_ROUGH_GUESS}` : '';\n return `Local is an option on this machine: ${describeHardware(hw)}. ${localSizeHint(hw)}${rough}`;\n}\n\n/** A server that answered and lists at least one model (or did not say how many). */\nfunction isUsableServer(p: AiLocalServerProbe): boolean {\n return p.ok && p.models !== 0;\n}\n\nfunction presetName(id: string): string {\n return AI_ENGINE_PRESETS.find((p) => p.id === id)?.name ?? id;\n}\n\nfunction modelsText(n: number | undefined): string {\n return n === undefined ? '' : ` with ${n} ${n === 1 ? 'model' : 'models'}`;\n}\n\n/**\n * The wizard's path, checked in order (OQ13):\n *\n * a. a local server is running and lists models → offer it first;\n * b. VS Code offers a Copilot model (`vscode-lm`, OQ12) → offer it first;\n * c. this machine is above the OQ11 threshold → cloud first, with \"Local is\n * an option on this machine\" shown;\n * d. otherwise cloud, with local under Advanced and no hint.\n *\n * `localCapable`, `localHint` and `hardwareLine` are filled whatever the path.\n */\nexport function recommendSetupPath(input: AiSetupInput): AiSetupRecommendation {\n const hw = input.hardware;\n const localCapable = isLocalCapable(hw);\n const base = { localCapable, localHint: localSizeHint(hw), hardwareLine: hardwareLine(hw, localCapable) };\n const reasons: string[] = [];\n\n const servers = input.running\n .map((probe, index) => ({ probe, index }))\n .filter(({ probe }) => isUsableServer(probe))\n .sort((a, b) => (b.probe.models ?? 0) - (a.probe.models ?? 0) || a.index - b.index);\n if (servers.length > 0) {\n const first = servers[0].probe;\n reasons.push(`${presetName(first.presetId)} is running on this machine${modelsText(first.models)}.`);\n return { path: 'runningLocal', ...base, running: first, reasons };\n }\n const idle = input.running.filter((p) => p.ok && p.models === 0);\n reasons.push(idle.length > 0\n ? `${idle.map((p) => presetName(p.presetId)).join(' and ')} answered but lists no models.`\n : 'No local model server is running.');\n\n if (input.vscodeLm?.available && input.vscodeLm.models > 0) {\n reasons.push(`GitHub Copilot offers ${input.vscodeLm.models} ${input.vscodeLm.models === 1 ? 'model' : 'models'} in VS Code.`);\n return { path: 'vscodeLm', ...base, reasons };\n }\n if (input.vscodeLm) {reasons.push('GitHub Copilot offers no model in VS Code.');}\n\n if (!hw) {\n reasons.push('The host did not report this machine’s hardware.');\n } else {\n reasons.push(localCapable\n ? `${describeHardware(hw)} can run local models.`\n : `${describeHardware(hw)} is below the threshold for local models.`);\n }\n return { path: 'cloud', ...base, reasons };\n}\n\n/** The recommendation's `vscodeLm` input from `detectHostEngines` results. */\nexport function vscodeLmAvailability(probes: readonly AiHostEngineProbe[] | null | undefined): { available: boolean; models: number } {\n const probe = probes?.find((p) => p.kind === 'vscode-lm');\n return { available: !!probe?.ok && probe.models > 0, models: probe?.ok ? probe.models : 0 };\n}\n\n// ── Model size tags ──────────────────────────────────────────────────────────\n\n/**\n * A model's parameter count in billions, from its id: `7b`, `30b`, `1.5b`,\n * `270m`, a mixture `8x7b` (total). The first count wins, so `30b-a3b`\n * (active parameters) reads 30. Null when the id names none.\n */\nexport function modelParamsBillions(id: string): number | null {\n const s = id.toLowerCase();\n const moe = /(?:^|[^a-z0-9.])(\\d+)x(\\d+(?:\\.\\d+)?)b(?![a-z0-9])/.exec(s);\n if (moe) {return Number(moe[1]) * Number(moe[2]);}\n const m = /(?:^|[^a-z0-9.])(\\d+(?:\\.\\d+)?)([bm])(?![a-z0-9])/.exec(s);\n if (!m) {return null;}\n const n = Number(m[1]);\n if (!Number.isFinite(n) || n <= 0) {return null;}\n return m[2] === 'm' ? n / 1000 : n;\n}\n\nexport type AiModelSizeTag = 'fits' | 'tight' | 'too large';\n\n/** A model this much over the \"fits\" size is still worth a try. */\nconst TIGHT_FACTOR = 1.75;\n\n/**\n * The largest model (billions) that runs well, by OQ11's bands. A machine\n * above the threshold in the small band gets the band's 8B, as its hint\n * says (\"up to about 8B\"); one below it scales down with memory.\n */\nfunction fitsBillions(hw: AiHardwareInfo): number {\n const memoryBytes = modelMemoryBytes(hw);\n const gb = memoryBytes / GIB;\n switch (memoryBand(memoryBytes)) {\n case 'small':\n return isLocalCapable(hw) ? 8 : Math.min(8, gb / 2);\n case 'medium':\n return 30;\n case 'large':\n return gb * 1.1;\n }\n}\n\n/**\n * \"fits\", \"tight\" or \"too large\" for a model on this machine: from the size\n * the server reported when there is one (`modelSizeHintFromBytes`), else the\n * parameter count in its id (OQ11's bands, 4-bit). Undefined when neither is\n * known or the hardware is unknown: no tag. A hint only; nothing is blocked.\n */\nexport function modelSizeHint(id: string, hardware: AiHardwareInfo | null, sizeBytes?: number): AiModelSizeTag | undefined {\n if (!hardware) {return undefined;}\n if (typeof sizeBytes === 'number' && sizeBytes > 0) {return modelSizeHintFromBytes(sizeBytes, hardware);}\n const params = modelParamsBillions(id);\n if (params === null) {return undefined;}\n const fits = fitsBillions(hardware);\n if (params <= fits) {return 'fits';}\n return params <= fits * TIGHT_FACTOR ? 'tight' : 'too large';\n}\n\n// ── Server-reported sizes, mixture-of-experts, a new local engine's default ──\n\n/** A server-reported size at or under this share of the model memory is \"fits\". */\nconst FITS_SHARE = 0.6;\n/** Up to this share it is \"tight\"; above it, \"too large\". */\nconst TIGHT_SHARE = 0.8;\n\n/**\n * \"fits\", \"tight\" or \"too large\" from a size the server reported (bytes on\n * disk or its load estimate), against this machine's model memory.\n */\nexport function modelSizeHintFromBytes(sizeBytes: number, hardware: AiHardwareInfo): AiModelSizeTag {\n const memory = modelMemoryBytes(hardware);\n if (sizeBytes <= memory * FITS_SHARE) {return 'fits';}\n return sizeBytes <= memory * TIGHT_SHARE ? 'tight' : 'too large';\n}\n\n/** A model size for a picker row: \"16 GB\", \"2.6 GB\", \"0.2 GB\". */\nexport function formatModelSize(bytes: number): string {\n const gb = bytes / GIB;\n return `${gb >= 10 ? Math.round(gb) : Math.round(gb * 10) / 10} GB`;\n}\n\n/**\n * The active parameters (billions) an id names, as mixture-of-experts models\n * do: `Qwen3.6-35B-A3B`, `qwen3.5-122b-a10b`, `Nemotron-3-Super-120B-A12B`.\n * Null when the id names none.\n */\nexport function modelActiveParamsBillions(id: string): number | null {\n const m = /(?:^|[^a-z0-9.])a(\\d+(?:\\.\\d+)?)b(?![a-z0-9])/.exec(id.toLowerCase());\n const n = m ? Number(m[1]) : NaN;\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/**\n * Whether a model is a mixture of experts, from its id's active parameters or\n * the server's word for it (`moe`: LM Studio's `arch`, oMLX's\n * `config_model_type`). Only a few billion parameters run per token, so it is\n * much faster than its size suggests. A row hint only, never a reason to pick it.\n */\nexport function modelMoeHint(id: string, moe?: boolean): { activeBillions: number | null } | null {\n const active = modelActiveParamsBillions(id);\n return active !== null || moe ? { activeBillions: active } : null;\n}\n\n/**\n * The picker's \"MoE · fast\" hint: a mixture of experts that also fits this\n * machine (`fits`). A tight fit (a 409 GB model on a 512 GB Mac) or one too\n * large runs slowly however few parameters work per token, and an unknown fit\n * proves nothing, so neither gets the speed hint (Q5).\n */\nexport function modelMoeFastHint(id: string, moe: boolean | undefined, size: AiModelSizeTag | undefined): { activeBillions: number | null } | null {\n if (size !== 'fits') {return null;}\n return modelMoeHint(id, moe);\n}\n\n/** One model as `localDefaultModel` weighs it. */\nexport interface AiLocalModelChoice {\n id: string;\n /** Can chat (an `llm` or `vlm`; not embeddings, speech or a converter). */\n chat: boolean;\n missing?: boolean;\n /** Loaded on the server right now. */\n loaded?: boolean;\n}\n\n/**\n * The model a new local engine starts with: a chat model the server has\n * loaded, else null and the user picks. Size is a poor guide to speed (a\n * mixture of experts runs far faster than a dense model its size), so no\n * guess is made from it.\n */\nexport function localDefaultModel(models: readonly AiLocalModelChoice[]): string | null {\n return models.find((m) => m.chat && !m.missing && m.loaded)?.id ?? null;\n}\n"],"mappings":"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","names":["isObject","isObject","effort","cut","nl","FENCE_OPEN","HEADING","fence","fence","fence"]}
|