@mikedown/core 0.2.0 → 0.4.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 +19 -0
- package/README.md +121 -2
- package/dist/ai/engines-page.d.ts +137 -0
- package/dist/ai/engines-page.js +11 -0
- package/dist/ai/engines-page.js.map +1 -0
- package/dist/ai/index.d.ts +2722 -0
- package/dist/ai/index.js +684 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai-ui-QFYFN73D.js +6397 -0
- package/dist/ai-ui-QFYFN73D.js.map +1 -0
- package/dist/chunk-APPBNQEY.js +216 -0
- package/dist/chunk-APPBNQEY.js.map +1 -0
- package/dist/chunk-DWXGWVA3.js +5748 -0
- package/dist/chunk-DWXGWVA3.js.map +1 -0
- package/dist/chunk-GWCRPPCX.js +18 -0
- package/dist/chunk-GWCRPPCX.js.map +1 -0
- package/dist/chunk-IWCXWUWW.js +6958 -0
- package/dist/chunk-IWCXWUWW.js.map +1 -0
- package/dist/chunk-L4FO63DF.js +113 -0
- package/dist/chunk-L4FO63DF.js.map +1 -0
- package/dist/chunk-N4N54LK2.js +39 -0
- package/dist/chunk-N4N54LK2.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 +7717 -2103
- package/dist/index.js.map +1 -1
- package/dist/schema-DVfssxQX.d.ts +100 -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 +4562 -108
- package/dist/types-2TZErbJr.d.ts +619 -0
- package/dist/types-DO8UBybZ.d.ts +741 -0
- package/package.json +11 -1
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI provider layer contracts: the settings shape (engines, models, routing,
|
|
3
|
+
* features) and the `ai` host group.
|
|
4
|
+
*
|
|
5
|
+
* Everything under `packages/core/src/ai/` is pure: no network, no DOM, no
|
|
6
|
+
* provider SDK. Inference runs in the host (VS Code extension host, the
|
|
7
|
+
* Desktop Rust side, or an npm consumer's own backend); core only resolves
|
|
8
|
+
* which engine and model should serve a request and renders the UI.
|
|
9
|
+
*
|
|
10
|
+
* Open-ended by design: `routing.tasks` and `features` are open maps, and the
|
|
11
|
+
* parser keeps keys it does not understand on every object (top level,
|
|
12
|
+
* engines, models, routes, feature entries), so a newer version's settings
|
|
13
|
+
* survive a save from an older one. Those extra keys ride along at runtime
|
|
14
|
+
* and are not part of these types.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* How the host talks to an engine. `openai-compatible` covers oMLX, vLLM,
|
|
18
|
+
* llama.cpp server and the hosted OpenAI-style APIs (OpenRouter, Groq,
|
|
19
|
+
* Gemini, Mistral, DeepSeek, xAI). `vscode-lm` is a model VS Code itself
|
|
20
|
+
* offers (GitHub Copilot, through `vscode.lm`): no address, no key, VS Code
|
|
21
|
+
* host only (see `AI_HOST_ENGINE_KINDS`). A settings file written by a newer
|
|
22
|
+
* version may carry a kind this one does not know; such engines are kept on
|
|
23
|
+
* save but never routed (see `isKnownEngineKind`).
|
|
24
|
+
*/
|
|
25
|
+
type AiEngineKind = 'openai-compatible' | 'lmstudio' | 'ollama' | 'anthropic' | 'openai' | 'vscode-lm';
|
|
26
|
+
declare const AI_ENGINE_KINDS: readonly AiEngineKind[];
|
|
27
|
+
/**
|
|
28
|
+
* Kinds only one host can run (no base URL, no key: the host's own model
|
|
29
|
+
* API). A host runs one only when it reports a status for the engine
|
|
30
|
+
* (`listEngines`); elsewhere such an engine is kept and never routed, the
|
|
31
|
+
* same as an unknown kind. The kind picker offers them only to an engine
|
|
32
|
+
* that already has one.
|
|
33
|
+
*/
|
|
34
|
+
declare const AI_HOST_ENGINE_KINDS: readonly AiEngineKind[];
|
|
35
|
+
/** `auto` derives local or cloud from the base URL (see `detectLocality`). */
|
|
36
|
+
type AiLocalitySetting = 'auto' | 'local' | 'cloud';
|
|
37
|
+
type AiLocality = 'local' | 'cloud';
|
|
38
|
+
/** Where a model's capability values came from. Re-discovery refreshes `endpoint` and `heuristic`, never `user`. */
|
|
39
|
+
type AiCapabilitiesSource = 'endpoint' | 'heuristic' | 'user';
|
|
40
|
+
interface AiModelCapabilities {
|
|
41
|
+
/** Chat/instruct completion. */
|
|
42
|
+
chat: boolean;
|
|
43
|
+
/** SSE token streaming (assumed true unless the probe says otherwise). */
|
|
44
|
+
stream: boolean;
|
|
45
|
+
/** Image inputs. */
|
|
46
|
+
vision: boolean;
|
|
47
|
+
/** JSON mode or reliable structured output. */
|
|
48
|
+
json: boolean;
|
|
49
|
+
/** Function calling. */
|
|
50
|
+
tools: boolean;
|
|
51
|
+
/** Fill-in-the-middle / raw completion (ghost text). */
|
|
52
|
+
fim: boolean;
|
|
53
|
+
/** Embeddings endpoint. */
|
|
54
|
+
embed: boolean;
|
|
55
|
+
/** Context window in tokens; null when unknown (treated as `DEFAULT_CONTEXT_TOKENS`). */
|
|
56
|
+
contextTokens: number | null;
|
|
57
|
+
}
|
|
58
|
+
/** The boolean capability flags, for chips and requirement checks. */
|
|
59
|
+
type AiCapabilityFlag = Exclude<keyof AiModelCapabilities, 'contextTokens'>;
|
|
60
|
+
declare const AI_CAPABILITY_FLAGS: readonly AiCapabilityFlag[];
|
|
61
|
+
/** Context window assumed when neither the endpoint nor the id says (OPEN-10). */
|
|
62
|
+
declare const DEFAULT_CONTEXT_TOKENS = 8192;
|
|
63
|
+
interface AiModelConfig {
|
|
64
|
+
/** The id the endpoint knows the model by (sent as `model`). Unique within its engine. */
|
|
65
|
+
id: string;
|
|
66
|
+
/** Display name. Absent means show the id. */
|
|
67
|
+
label?: string;
|
|
68
|
+
enabled: boolean;
|
|
69
|
+
capabilities: AiModelCapabilities;
|
|
70
|
+
capabilitiesSource: AiCapabilitiesSource;
|
|
71
|
+
/**
|
|
72
|
+
* Merged into the request body last, so it wins (OPEN-6): thinking controls
|
|
73
|
+
* such as `reasoning_effort`, `thinking_budget`, `chat_template_kwargs`.
|
|
74
|
+
*/
|
|
75
|
+
requestOverrides?: Record<string, unknown>;
|
|
76
|
+
/** The last discovery did not list this model. Kept (greyed), never routed. */
|
|
77
|
+
missing?: boolean;
|
|
78
|
+
/** Added by hand. A discovery that does not list it leaves it alone (no `missing`). */
|
|
79
|
+
manual?: boolean;
|
|
80
|
+
}
|
|
81
|
+
interface AiApiKeyFile {
|
|
82
|
+
path: string;
|
|
83
|
+
jsonPath: string;
|
|
84
|
+
}
|
|
85
|
+
interface AiEngineConfig {
|
|
86
|
+
/** Stable, generated once, never shown. */
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
kind: AiEngineKind;
|
|
90
|
+
/** e.g. `http://127.0.0.1:8000/v1` or `https://api.anthropic.com`. */
|
|
91
|
+
baseUrl: string;
|
|
92
|
+
enabled: boolean;
|
|
93
|
+
locality: AiLocalitySetting;
|
|
94
|
+
/** Environment variable the host reads when no key is stored. The key itself is never in settings. */
|
|
95
|
+
apiKeyEnv?: string;
|
|
96
|
+
/**
|
|
97
|
+
* A JSON file in the user's home folder the host reads when no key is stored and
|
|
98
|
+
* `apiKeyEnv` is unset. `path` may start with `~/`; `jsonPath` is a dotted path to
|
|
99
|
+
* a string value (`auth.api_key`). The key itself is never in settings.
|
|
100
|
+
*/
|
|
101
|
+
apiKeyFile?: AiApiKeyFile;
|
|
102
|
+
/** `auto` lists models from the endpoint and merges them with manual ones; `manual` never asks. */
|
|
103
|
+
discovery: 'auto' | 'manual';
|
|
104
|
+
models: AiModelConfig[];
|
|
105
|
+
}
|
|
106
|
+
interface AiRoute {
|
|
107
|
+
engineId: string;
|
|
108
|
+
modelId: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* What to do when a task's route is unusable. `none` disables the surface,
|
|
112
|
+
* `default` tries the default route, `anyLocal` then any usable local
|
|
113
|
+
* model, `any` then cloud engines that pass the gate.
|
|
114
|
+
*/
|
|
115
|
+
type AiFallbackMode = 'none' | 'default' | 'anyLocal' | 'any';
|
|
116
|
+
declare const AI_FALLBACK_MODES: readonly AiFallbackMode[];
|
|
117
|
+
interface AiRouting {
|
|
118
|
+
/** Null until the user picks one. */
|
|
119
|
+
default: AiRoute | null;
|
|
120
|
+
fallback: AiFallbackMode;
|
|
121
|
+
/** Open map keyed by task id (see `AI_TASKS`). An absent task uses `default`. */
|
|
122
|
+
tasks: Record<string, AiRoute>;
|
|
123
|
+
}
|
|
124
|
+
/** One feature's saved state. Absent fields fall back to the feature registry. */
|
|
125
|
+
interface AiFeatureSetting {
|
|
126
|
+
enabled?: boolean;
|
|
127
|
+
/** Task override, e.g. `{ task: 'vision' }`. */
|
|
128
|
+
task?: string;
|
|
129
|
+
/** `ghostText` only (see `AiGhostTextOptions`; read through `ghostTextOptions`). */
|
|
130
|
+
delayMs?: number;
|
|
131
|
+
maxTokens?: number;
|
|
132
|
+
trigger?: AiGhostTrigger;
|
|
133
|
+
localOnly?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/** When Inline suggestions fire: after an idle pause, or only from the shortcut / menu. */
|
|
136
|
+
type AiGhostTrigger = 'auto' | 'manual';
|
|
137
|
+
/** The `features.ghostText` knobs with defaults applied and ranges clamped (`ghostTextOptions`). */
|
|
138
|
+
interface AiGhostTextOptions {
|
|
139
|
+
/** Idle time after a keystroke before a request fires, in ms. Default 400. */
|
|
140
|
+
delayMs: number;
|
|
141
|
+
/** Output budget in tokens. Default 48. */
|
|
142
|
+
maxTokens: number;
|
|
143
|
+
/** Default `auto`. */
|
|
144
|
+
trigger: AiGhostTrigger;
|
|
145
|
+
/** Reject cloud engines for this feature. Default true. */
|
|
146
|
+
localOnly: boolean;
|
|
147
|
+
}
|
|
148
|
+
/** Open map keyed by feature id (see `AI_FEATURES`). */
|
|
149
|
+
type AiFeatureSettings = Record<string, AiFeatureSetting>;
|
|
150
|
+
/** Everything under `mikedown.ai.*`. Built by `parseAiSettings`, saved by `serializeAiSettings`. */
|
|
151
|
+
interface AiSettings {
|
|
152
|
+
/** Master switch. Default false. */
|
|
153
|
+
enabled: boolean;
|
|
154
|
+
/** Global gate for cloud engines. Default false; per-workspace consent applies on top. */
|
|
155
|
+
allowCloud: boolean;
|
|
156
|
+
engines: AiEngineConfig[];
|
|
157
|
+
routing: AiRouting;
|
|
158
|
+
features: AiFeatureSettings;
|
|
159
|
+
/** CIDR ranges (and `.suffix` host names) that count as local. Default RFC 1918. */
|
|
160
|
+
localNetworks: string[];
|
|
161
|
+
/** The AI button on the toolbar (hosts with AI support only). Default true. */
|
|
162
|
+
showToolbarButton: boolean;
|
|
163
|
+
/** Record what each AI request sent and received, in memory, and show it in the Activity panel. Default false. */
|
|
164
|
+
activityPanel: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* Documentation bases (`mikedown.ai.docBases`). User-level only: a host
|
|
167
|
+
* reads this key from its user settings and never from a workspace, so a
|
|
168
|
+
* repository cannot add or redirect a base. `parseAiSettings` drops invalid
|
|
169
|
+
* entries with a reason (see `parseDocBases`).
|
|
170
|
+
*/
|
|
171
|
+
docBases: AiDocBase[];
|
|
172
|
+
/** Advanced options for chatting with the bases (`mikedown.ai.docs`), user-level only like `docBases`. */
|
|
173
|
+
docs: AiDocsOptions;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* One documentation base: user-level folders of markdown the chat can
|
|
177
|
+
* navigate and answer from. Roots are absolute, normalized folder paths.
|
|
178
|
+
*/
|
|
179
|
+
interface AiDocBase {
|
|
180
|
+
/** Stable, generated once, never shown. The wire and the consent dialog name a base by id; the host maps it to a name from its own settings. */
|
|
181
|
+
id: string;
|
|
182
|
+
name: string;
|
|
183
|
+
/** Absolute normalized folders (`validateDocRoot`). At least one. */
|
|
184
|
+
roots: string[];
|
|
185
|
+
/** Per-base override for the home document's file name, relative to each root (`HOME.md`). Absent: `INDEX.md`, `README.md`, then a map. */
|
|
186
|
+
homeFile?: string;
|
|
187
|
+
/** May pages from this base be sent to a cloud engine. Default false. */
|
|
188
|
+
allowCloud: boolean;
|
|
189
|
+
/** A disabled base is never indexed for or sent with a request. Default true. */
|
|
190
|
+
enabled: boolean;
|
|
191
|
+
}
|
|
192
|
+
type AiDocsNavigateBy = 'section' | 'page';
|
|
193
|
+
declare const AI_DOCS_NAVIGATE_BY: readonly AiDocsNavigateBy[];
|
|
194
|
+
/** The advanced options (`ai.docs`), without the bases. */
|
|
195
|
+
interface AiDocsOptions {
|
|
196
|
+
/** Navigation calls before the answer. Clamped to `AI_DOCS_MAX_HOPS_MIN`..`AI_DOCS_MAX_HOPS_MAX`. Default 3. */
|
|
197
|
+
maxHops: number;
|
|
198
|
+
/** Percent of the route's context the answer's sections may take. Clamped to `AI_DOCS_SOURCE_SHARE_MIN`..`AI_DOCS_SOURCE_SHARE_MAX`. Default 60. */
|
|
199
|
+
sourceShare: number;
|
|
200
|
+
/** Whether the model asks for `page#heading` sections or whole pages. Default `section`. */
|
|
201
|
+
navigateBy: AiDocsNavigateBy;
|
|
202
|
+
}
|
|
203
|
+
/** The options the loop runs with: `ai.docs` plus the bases (`resolveDocsSettings`). */
|
|
204
|
+
interface AiDocsSettings extends AiDocsOptions {
|
|
205
|
+
bases: AiDocBase[];
|
|
206
|
+
}
|
|
207
|
+
declare const AI_DOCS_MAX_HOPS_MIN = 1;
|
|
208
|
+
declare const AI_DOCS_MAX_HOPS_MAX = 5;
|
|
209
|
+
/**
|
|
210
|
+
* The most requests one "Allow once" can admit in a chat turn: a navigation
|
|
211
|
+
* call and its one repair per hop, then the answer. Hosts stop honouring a grant
|
|
212
|
+
* past this, so a webview that never ends its turn cannot keep sending on it.
|
|
213
|
+
* `TURN_GRANT_MAX_USES` in Desktop's `commands/ai.rs` is the same number.
|
|
214
|
+
*/
|
|
215
|
+
declare const AI_TURN_GRANT_MAX_USES: number;
|
|
216
|
+
declare const AI_DOCS_MAX_HOPS_DEFAULT = 3;
|
|
217
|
+
declare const AI_DOCS_SOURCE_SHARE_MIN = 30;
|
|
218
|
+
declare const AI_DOCS_SOURCE_SHARE_MAX = 80;
|
|
219
|
+
declare const AI_DOCS_SOURCE_SHARE_DEFAULT = 60;
|
|
220
|
+
declare const AI_DOCS_NAVIGATE_BY_DEFAULT: AiDocsNavigateBy;
|
|
221
|
+
/** Where a root's home document came from (decision 3). `map` means none was found and core builds an in-memory map. */
|
|
222
|
+
type AiDocHomeKind = 'override' | 'index' | 'readme' | 'map';
|
|
223
|
+
/** One root of a base, as the host last saw it. */
|
|
224
|
+
interface AiDocRootStatus {
|
|
225
|
+
/** The root as configured (absolute). */
|
|
226
|
+
root: string;
|
|
227
|
+
/** The label ids use for this root (the same rule on both hosts; `docs/index.ts`). */
|
|
228
|
+
label: string;
|
|
229
|
+
/** The folder exists and is readable. False: "Root not found"; the root is skipped for requests, never deleted. */
|
|
230
|
+
found: boolean;
|
|
231
|
+
/** Which home precedence step won; `map` is the "No home document" state. Absent when the root is not found. */
|
|
232
|
+
home?: AiDocHomeKind;
|
|
233
|
+
/** The home document's path relative to the root, when `home` is not `map`. */
|
|
234
|
+
homePath?: string;
|
|
235
|
+
/** Markdown files indexed under this root. */
|
|
236
|
+
fileCount: number;
|
|
237
|
+
/** Files over the byte cap, skipped. */
|
|
238
|
+
oversizeSkipped: number;
|
|
239
|
+
/** The file cap was reached; later files were not indexed. */
|
|
240
|
+
capReached: boolean;
|
|
241
|
+
/** Files that are not UTF-8 text (a UTF-16 or UTF-32 byte order mark, or NUL bytes), skipped. */
|
|
242
|
+
notTextSkipped?: number;
|
|
243
|
+
/** The folder cap was reached; later folders were not read. */
|
|
244
|
+
dirCapReached?: boolean;
|
|
245
|
+
/** The build's time budget ran out; the index covers what was read before that. */
|
|
246
|
+
timedOut?: boolean;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* A base plus its health, as `listBases` reports it. Computed when the
|
|
250
|
+
* manager page opens and on Refresh. "Active" means enabled with at least one
|
|
251
|
+
* root found; only active bases are indexed for or sent to a request.
|
|
252
|
+
*/
|
|
253
|
+
interface AiDocBaseStatus {
|
|
254
|
+
base: AiDocBase;
|
|
255
|
+
/** Enabled and at least one root found. */
|
|
256
|
+
active: boolean;
|
|
257
|
+
roots: AiDocRootStatus[];
|
|
258
|
+
/** Sum over roots. */
|
|
259
|
+
fileCount: number;
|
|
260
|
+
oversizeSkipped: number;
|
|
261
|
+
/** Roots not found on disk. */
|
|
262
|
+
rootsMissing: number;
|
|
263
|
+
/** Any root hit the file cap. */
|
|
264
|
+
capReached: boolean;
|
|
265
|
+
/** Files skipped because they are not UTF-8 text, over every root. */
|
|
266
|
+
notTextSkipped?: number;
|
|
267
|
+
/** Any root hit the folder cap. */
|
|
268
|
+
dirCapReached?: boolean;
|
|
269
|
+
/** The build's time budget ran out; the index and the counts cover what was read before that. */
|
|
270
|
+
timedOut?: boolean;
|
|
271
|
+
/** Relative links that leave every root or point at no page. Absent when the host did not compute it. */
|
|
272
|
+
brokenLinks?: number;
|
|
273
|
+
}
|
|
274
|
+
/** A heading in the index (a section starts at each). Bodies are never in the index. */
|
|
275
|
+
interface AiDocsHeading {
|
|
276
|
+
/** The section id: `<page id>#<slug>`. */
|
|
277
|
+
id: string;
|
|
278
|
+
/** 1 to 6. */
|
|
279
|
+
level: number;
|
|
280
|
+
text: string;
|
|
281
|
+
slug: string;
|
|
282
|
+
}
|
|
283
|
+
/** A link as written in a page. Core resolves it (`docs/links.ts`); the host only extracts. */
|
|
284
|
+
interface AiDocsLink {
|
|
285
|
+
/** The target as written: a relative path with an optional `#fragment`, or a wikilink name. */
|
|
286
|
+
target: string;
|
|
287
|
+
/** The link text. */
|
|
288
|
+
text: string;
|
|
289
|
+
kind: 'markdown' | 'wikilink';
|
|
290
|
+
}
|
|
291
|
+
/** One markdown page of a base, without its body. */
|
|
292
|
+
interface AiDocsPage {
|
|
293
|
+
/** `<root label>/<rel path>`, with `/` separators (`docs/index.ts`). The only thing the model names. */
|
|
294
|
+
id: string;
|
|
295
|
+
/** The root's label (the first segment of `id`). */
|
|
296
|
+
root: string;
|
|
297
|
+
/** Path relative to the root, with `/` separators. */
|
|
298
|
+
relPath: string;
|
|
299
|
+
/** Frontmatter title, else the first heading, else the file name. */
|
|
300
|
+
title: string;
|
|
301
|
+
headings: AiDocsHeading[];
|
|
302
|
+
links: AiDocsLink[];
|
|
303
|
+
/** Size of the file in characters (not bytes), for the budget. */
|
|
304
|
+
chars: number;
|
|
305
|
+
}
|
|
306
|
+
/** The home document chosen for a root. */
|
|
307
|
+
interface AiDocsHome {
|
|
308
|
+
/** The root's label. */
|
|
309
|
+
root: string;
|
|
310
|
+
kind: AiDocHomeKind;
|
|
311
|
+
/** The home page's id; null for `map`. */
|
|
312
|
+
pageId: string | null;
|
|
313
|
+
}
|
|
314
|
+
/** A base's index: pages with headings and links, no bodies. Built lazily on the host, in memory. */
|
|
315
|
+
interface AiDocsIndex {
|
|
316
|
+
baseId: string;
|
|
317
|
+
/** Roots that were found, in configured order. */
|
|
318
|
+
roots: Array<{
|
|
319
|
+
label: string;
|
|
320
|
+
}>;
|
|
321
|
+
pages: AiDocsPage[];
|
|
322
|
+
homes: AiDocsHome[];
|
|
323
|
+
/** The file cap cut the walk short. */
|
|
324
|
+
truncated: boolean;
|
|
325
|
+
oversizeSkipped: number;
|
|
326
|
+
/** Unix milliseconds the index was built. */
|
|
327
|
+
builtAt: number;
|
|
328
|
+
}
|
|
329
|
+
/** A section (or whole page) as `readSections` returns it: the only call that carries a body. */
|
|
330
|
+
interface AiDocsSection {
|
|
331
|
+
/** The id that was asked for: `<page id>#<slug>` or a page id. */
|
|
332
|
+
id: string;
|
|
333
|
+
pageId: string;
|
|
334
|
+
/** The page's title. */
|
|
335
|
+
title: string;
|
|
336
|
+
/** Path relative to the root, for the chip and the relative link. */
|
|
337
|
+
relPath: string;
|
|
338
|
+
/** The section's heading text; absent for a whole page. */
|
|
339
|
+
heading?: string;
|
|
340
|
+
/** The markdown, capped at the section byte cap by the host. */
|
|
341
|
+
text: string;
|
|
342
|
+
/** The host cut `text` at the cap. */
|
|
343
|
+
truncated: boolean;
|
|
344
|
+
}
|
|
345
|
+
/** A keyword match: ids and labels only, no text. The loop reads the bodies through `readSections`. */
|
|
346
|
+
interface AiDocsSectionRef {
|
|
347
|
+
/** `<page id>#<slug>`, or a page id for text before the first heading. */
|
|
348
|
+
id: string;
|
|
349
|
+
pageId: string;
|
|
350
|
+
title: string;
|
|
351
|
+
relPath: string;
|
|
352
|
+
heading?: string;
|
|
353
|
+
/** Higher is a better match; the order is `docs/keyword.ts`'s. */
|
|
354
|
+
score: number;
|
|
355
|
+
}
|
|
356
|
+
type AiMessagePart = {
|
|
357
|
+
type: 'text';
|
|
358
|
+
text: string;
|
|
359
|
+
} | {
|
|
360
|
+
type: 'image';
|
|
361
|
+
mime: string;
|
|
362
|
+
bytes: Uint8Array;
|
|
363
|
+
};
|
|
364
|
+
interface AiMessage {
|
|
365
|
+
role: 'system' | 'user' | 'assistant';
|
|
366
|
+
content: string | AiMessagePart[];
|
|
367
|
+
}
|
|
368
|
+
interface AiCompletionRequest {
|
|
369
|
+
requestId: string;
|
|
370
|
+
/** Feature id, for logging and consent copy. */
|
|
371
|
+
feature: string;
|
|
372
|
+
/** Task id, informational for tier-1 hosts. */
|
|
373
|
+
task: string;
|
|
374
|
+
/** Resolved by core; tier-1 hosts may ignore it. */
|
|
375
|
+
route?: AiRoute;
|
|
376
|
+
messages: AiMessage[];
|
|
377
|
+
format?: 'text' | 'json';
|
|
378
|
+
maxTokens?: number;
|
|
379
|
+
temperature?: number;
|
|
380
|
+
/**
|
|
381
|
+
* Per-request body fields (the popover's thinking dial). The host merges
|
|
382
|
+
* them over the model's configured `requestOverrides`, deeply, so the
|
|
383
|
+
* request wins. Tier-1 hosts may ignore them.
|
|
384
|
+
*/
|
|
385
|
+
requestOverrides?: Record<string, unknown>;
|
|
386
|
+
/** Set on every request of a chat turn (`AiRoutePin`); absent on every other request. */
|
|
387
|
+
turn?: AiCompletionTurn;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* What a request of a chat turn says about that turn. A host uses it to find
|
|
391
|
+
* the "Allow once" it holds for the turn, and to word a dialog it has to raise
|
|
392
|
+
* in the middle of one (`sends`, and `baseIds`, which it maps to names from its
|
|
393
|
+
* own settings). It is a claim, not consent: a host admits the request only
|
|
394
|
+
* against a grant it made itself when the user answered, for this panel or
|
|
395
|
+
* window, this turn, this engine and this set of bases.
|
|
396
|
+
*/
|
|
397
|
+
interface AiCompletionTurn {
|
|
398
|
+
id: string;
|
|
399
|
+
sends?: AiSendsScope;
|
|
400
|
+
baseIds?: string[];
|
|
401
|
+
/** The engine's locality when the turn was routed. A host fails the request if it no longer holds. */
|
|
402
|
+
isLocal?: boolean;
|
|
403
|
+
}
|
|
404
|
+
interface AiServedBy {
|
|
405
|
+
engineId?: string;
|
|
406
|
+
/** e.g. "oMLX (Mac Studio)". */
|
|
407
|
+
engineName: string;
|
|
408
|
+
modelId: string;
|
|
409
|
+
isLocal: boolean;
|
|
410
|
+
}
|
|
411
|
+
type AiFinishReason = 'stop' | 'length' | 'cancelled' | 'error';
|
|
412
|
+
interface AiUsage {
|
|
413
|
+
promptTokens?: number;
|
|
414
|
+
completionTokens?: number;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* How long a completion took, measured by the host from sending the request.
|
|
418
|
+
* Optional on the wire: an older host sends none.
|
|
419
|
+
*/
|
|
420
|
+
interface AiTiming {
|
|
421
|
+
/** To the end of the answer. */
|
|
422
|
+
totalMs: number;
|
|
423
|
+
/** To the first output, answer or reasoning. Absent when the server did not stream. */
|
|
424
|
+
firstTokenMs?: number;
|
|
425
|
+
/** Characters of output (answer plus reasoning), to estimate tokens when the engine reports no usage. */
|
|
426
|
+
outputChars?: number;
|
|
427
|
+
}
|
|
428
|
+
interface AiCompletionResult {
|
|
429
|
+
text: string;
|
|
430
|
+
served: AiServedBy;
|
|
431
|
+
usage?: AiUsage;
|
|
432
|
+
finishReason?: AiFinishReason;
|
|
433
|
+
timing?: AiTiming;
|
|
434
|
+
}
|
|
435
|
+
/** One model as the host last saw it. */
|
|
436
|
+
interface AiEngineStatusModel {
|
|
437
|
+
id: string;
|
|
438
|
+
label?: string;
|
|
439
|
+
capabilities: AiModelCapabilities;
|
|
440
|
+
source: AiCapabilitiesSource;
|
|
441
|
+
missing?: boolean;
|
|
442
|
+
/** Loaded in memory right now (LM Studio, Ollama report it). */
|
|
443
|
+
loaded?: boolean;
|
|
444
|
+
/** What the server says the model takes, in bytes (oMLX, LM Studio, Ollama report it). */
|
|
445
|
+
sizeBytes?: number;
|
|
446
|
+
/** The server says it is a mixture of experts (LM Studio `arch`, oMLX `config_model_type`). */
|
|
447
|
+
moe?: boolean;
|
|
448
|
+
}
|
|
449
|
+
/** Where a host found an engine's key: its secret store, a variable, or a key file. */
|
|
450
|
+
type AiKeySource = 'stored' | 'env' | 'file';
|
|
451
|
+
interface AiEngineStatus {
|
|
452
|
+
engineId: string;
|
|
453
|
+
/** Null = never probed. */
|
|
454
|
+
ok: boolean | null;
|
|
455
|
+
isLocal: boolean;
|
|
456
|
+
hasKey: boolean;
|
|
457
|
+
/**
|
|
458
|
+
* Where the key came from, when `hasKey` and the host knows: its secret store,
|
|
459
|
+
* the engine's `apiKeyEnv` variable, or its `apiKeyFile`. Absent for no key or
|
|
460
|
+
* a host that does not say (the engine form then guesses from the engine's
|
|
461
|
+
* settings).
|
|
462
|
+
*/
|
|
463
|
+
keySource?: AiKeySource;
|
|
464
|
+
latencyMs?: number;
|
|
465
|
+
error?: string;
|
|
466
|
+
models: AiEngineStatusModel[];
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* One local model server as `detectLocalServers` found it: whether its preset's
|
|
470
|
+
* address answered, and how many models it listed when it did.
|
|
471
|
+
*/
|
|
472
|
+
interface AiLocalServerProbe {
|
|
473
|
+
presetId: string;
|
|
474
|
+
baseUrl: string;
|
|
475
|
+
ok: boolean;
|
|
476
|
+
/** Models the server listed (embedding models left out where the server says which they are). */
|
|
477
|
+
models?: number;
|
|
478
|
+
latencyMs?: number;
|
|
479
|
+
}
|
|
480
|
+
/**
|
|
481
|
+
* A model API built into the host (VS Code's `vscode.lm`, which GitHub
|
|
482
|
+
* Copilot serves), as `detectHostEngines` found it. Nothing is saved.
|
|
483
|
+
*/
|
|
484
|
+
interface AiHostEngineProbe {
|
|
485
|
+
/** The preset to add it from (`vscodelm`). */
|
|
486
|
+
presetId: string;
|
|
487
|
+
kind: AiEngineKind;
|
|
488
|
+
/** At least one chat model is available right now. */
|
|
489
|
+
ok: boolean;
|
|
490
|
+
/** Chat models available. */
|
|
491
|
+
models: number;
|
|
492
|
+
/** Why none is available, when the host knows (no Copilot extension, signed out). */
|
|
493
|
+
error?: string;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* What a host can tell about this machine, for the setup wizard's "can it
|
|
497
|
+
* run models locally" hint (`recommendSetupPath`, `modelSizeHint`). A rough
|
|
498
|
+
* call, not a benchmark: VS Code reads no GPU at all.
|
|
499
|
+
*/
|
|
500
|
+
interface AiHardwareInfo {
|
|
501
|
+
/** Node's `process.arch` names: `arm64`, `x64`. */
|
|
502
|
+
arch: string;
|
|
503
|
+
/** Node's `process.platform` names: `darwin`, `win32`, `linux`. */
|
|
504
|
+
platform: string;
|
|
505
|
+
totalMemoryBytes: number;
|
|
506
|
+
/** macOS on arm64: the GPU shares `totalMemoryBytes` (unified memory). */
|
|
507
|
+
appleSilicon: boolean;
|
|
508
|
+
/** CPU brand string, when the host read one ("Apple M2 Max"). */
|
|
509
|
+
cpu?: string;
|
|
510
|
+
/** A discrete GPU, when the host could read one (Desktop: NVIDIA through `nvidia-smi`; Apple Silicon reports `apple`). */
|
|
511
|
+
gpu?: {
|
|
512
|
+
vendor: 'nvidia' | 'amd' | 'apple' | 'other';
|
|
513
|
+
vramBytes?: number;
|
|
514
|
+
name?: string;
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
type AiErrorCode = 'unreachable' | 'auth' | 'modelNotFound' | 'modelLoadFailed' | 'contextOverflow' | 'rateLimited' | 'cloudBlocked' | 'consentDenied' | 'cancelled' | 'badOutput' | 'unknown';
|
|
518
|
+
declare const AI_ERROR_CODES: readonly AiErrorCode[];
|
|
519
|
+
/** What `MikeDownAiHost` methods reject with. A plain object, so it crosses postMessage and Tauri events. */
|
|
520
|
+
interface AiError {
|
|
521
|
+
code: AiErrorCode;
|
|
522
|
+
message: string;
|
|
523
|
+
/** The engine the failed request went to (or the router turned down), when known. */
|
|
524
|
+
engineId?: string;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* The `ai` group on `MikeDownHost`. Absent turns every AI surface off.
|
|
528
|
+
*
|
|
529
|
+
* Tier 1 (`complete` only): core shows the master switch, feature toggles and
|
|
530
|
+
* the indicator from whatever `served` the host reports. Tier 2 (plus the
|
|
531
|
+
* registry methods): core shows the full AI tab and routes.
|
|
532
|
+
*/
|
|
533
|
+
interface MikeDownAiHost {
|
|
534
|
+
complete(req: AiCompletionRequest, onToken: (delta: string) => void, signal: AbortSignal): Promise<AiCompletionResult>;
|
|
535
|
+
embed?(texts: string[], route?: AiRoute, signal?: AbortSignal): Promise<number[][]>;
|
|
536
|
+
transcribe?(audio: {
|
|
537
|
+
mime: string;
|
|
538
|
+
bytes: Uint8Array;
|
|
539
|
+
}, signal?: AbortSignal): Promise<string>;
|
|
540
|
+
listEngines?(): Promise<AiEngineStatus[]>;
|
|
541
|
+
/**
|
|
542
|
+
* Look for model servers on this machine at the local presets' default
|
|
543
|
+
* addresses (`localDetectCandidates()`), one GET each, capped at
|
|
544
|
+
* `AI_LOCAL_DETECT_TIMEOUT_MS`. Only on request (the first-run panel, the
|
|
545
|
+
* quick-start cards), never in the background. The host looks each preset
|
|
546
|
+
* up by id itself and sends a key (oMLX's key file) only to the preset's
|
|
547
|
+
* own address. One result per candidate, in order. Nothing is saved.
|
|
548
|
+
*/
|
|
549
|
+
detectLocalServers?(candidates: {
|
|
550
|
+
presetId: string;
|
|
551
|
+
baseUrl: string;
|
|
552
|
+
}[], signal: AbortSignal): Promise<AiLocalServerProbe[]>;
|
|
553
|
+
/**
|
|
554
|
+
* Model APIs built into the host that need no address or key (VS Code:
|
|
555
|
+
* `vscode.lm`, GitHub Copilot's models), one result per `AI_HOST_ENGINE_KINDS`
|
|
556
|
+
* kind the host runs. Lists models only; sends no text. Absent: the host
|
|
557
|
+
* has none, and the `host` preset family stays hidden.
|
|
558
|
+
*/
|
|
559
|
+
detectHostEngines?(): Promise<AiHostEngineProbe[]>;
|
|
560
|
+
/**
|
|
561
|
+
* This machine's memory, CPU and (where the host can read it) GPU, for the
|
|
562
|
+
* setup wizard's local hint. Static, so hosts may answer from a cache; null
|
|
563
|
+
* when the host cannot tell.
|
|
564
|
+
*/
|
|
565
|
+
hardwareInfo?(): Promise<AiHardwareInfo | null>;
|
|
566
|
+
testEngine?(engineId: string): Promise<AiEngineStatus>;
|
|
567
|
+
discoverModels?(engineId: string): Promise<AiEngineStatus>;
|
|
568
|
+
/** Store (or with null, delete) an engine's key. Never part of `saveSettings`. */
|
|
569
|
+
setSecret?(engineId: string, apiKey: string | null): Promise<void>;
|
|
570
|
+
/**
|
|
571
|
+
* False when the host process may not see variables set in a shell profile
|
|
572
|
+
* (MikeDown Desktop opened from the Dock or Start menu), so `apiKeyEnv` is
|
|
573
|
+
* unreliable there and the engine form points at the key file or a pasted
|
|
574
|
+
* key instead. Absent: the host reads them (VS Code resolves the shell
|
|
575
|
+
* environment) or does not say.
|
|
576
|
+
*/
|
|
577
|
+
readonly shellEnv?: boolean;
|
|
578
|
+
/**
|
|
579
|
+
* Host UI asking to use a cloud engine in this workspace. Absent means the
|
|
580
|
+
* host decides on its own. `about` names the request that asked, so the
|
|
581
|
+
* dialog can say what is sent (`aiConsentText`).
|
|
582
|
+
*/
|
|
583
|
+
requestCloudConsent?(engineId: string, about?: AiConsentAbout): Promise<AiConsentReply>;
|
|
584
|
+
/**
|
|
585
|
+
* `requestCloudConsent` for a chat turn, where `'once'` means the whole turn:
|
|
586
|
+
* the host holds the grant and admits the turn's requests (`req.turn`) on it.
|
|
587
|
+
* Absent: core asks `requestCloudConsent`.
|
|
588
|
+
*/
|
|
589
|
+
requestTurnConsent?(engineId: string, about: AiTurnConsentAbout): Promise<AiConsentReply>;
|
|
590
|
+
/**
|
|
591
|
+
* The chat turn `turnId` is over (answered, refused, failed or cancelled):
|
|
592
|
+
* forget any "Allow once" held for it. Fire and forget. A host that holds no
|
|
593
|
+
* grant per turn can leave it out.
|
|
594
|
+
*/
|
|
595
|
+
endTurn?(turnId: string): void;
|
|
596
|
+
/**
|
|
597
|
+
* The cloud consents for this workspace or folder, for the Cloud consents
|
|
598
|
+
* list on the AI tab: the stored answers, plus this session's "Don't
|
|
599
|
+
* allow" (`deny`), so it can be forgotten without a restart. "Allow once"
|
|
600
|
+
* leaves nothing to list.
|
|
601
|
+
*/
|
|
602
|
+
listConsents?(): Promise<AiConsentRecord[]>;
|
|
603
|
+
/** Forget the answer for an engine (stored or this session's), so the next request asks again. */
|
|
604
|
+
forgetConsent?(engineId: string): Promise<void>;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* What `requestCloudConsent` resolves to: `true` allowed for the workspace
|
|
608
|
+
* (remembered), `'once'` allowed for this request only (the next one asks
|
|
609
|
+
* again), or for the whole turn from `requestTurnConsent`, `false` declined for this request (a host may also stop asking for
|
|
610
|
+
* the session; it then answers `false` without a dialog), `'never'` a stored
|
|
611
|
+
* refusal from an older version (the host answers it without asking). A host
|
|
612
|
+
* that only knows allow and decline keeps returning booleans.
|
|
613
|
+
*/
|
|
614
|
+
type AiConsentReply = boolean | 'once' | 'never';
|
|
615
|
+
/** One stored cloud consent answer (`MikeDownAiHost.listConsents`). */
|
|
616
|
+
interface AiConsentRecord {
|
|
617
|
+
engineId: string;
|
|
618
|
+
/** `allow` and `never` (older versions) are stored; `deny` is this session's "Don't allow". */
|
|
619
|
+
answer: 'allow' | 'never' | 'deny';
|
|
620
|
+
/** Unix milliseconds. */
|
|
621
|
+
at: number;
|
|
622
|
+
/** What the answer covers: VS Code's workspace, Desktop's folder. */
|
|
623
|
+
scope: 'workspace' | 'folder';
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* What a request sends to the engine, for the privacy copy (`whatIsSent`):
|
|
627
|
+
* the selection with its neighbouring paragraphs, the whole document, the
|
|
628
|
+
* document with its frontmatter and the workspace's tag names, pages from
|
|
629
|
+
* the documentation bases the user turned on, or the text around the cursor
|
|
630
|
+
* on every pause (Inline suggestions).
|
|
631
|
+
*/
|
|
632
|
+
type AiSendsScope = 'selection' | 'document' | 'documentAndTags' | 'docsBase' | 'cursor';
|
|
633
|
+
/**
|
|
634
|
+
* The request behind a consent prompt. `sends` overrides the feature's own
|
|
635
|
+
* scope (whole-document Translate; the docs chat sends `docsBase`).
|
|
636
|
+
*/
|
|
637
|
+
interface AiConsentAbout {
|
|
638
|
+
feature?: string;
|
|
639
|
+
sends?: AiSendsScope;
|
|
640
|
+
/**
|
|
641
|
+
* The documentation bases the request would send pages from, by id. The
|
|
642
|
+
* host maps ids to names from its own user-level settings and drops unknown
|
|
643
|
+
* ids, so a name from the webview never reaches its modal.
|
|
644
|
+
*/
|
|
645
|
+
baseIds?: string[];
|
|
646
|
+
}
|
|
647
|
+
/** A consent prompt from a chat turn (`AiCompletionTurn.id`): an "Allow once" holds for that turn and no other. */
|
|
648
|
+
interface AiTurnConsentAbout extends AiConsentAbout {
|
|
649
|
+
turnId: string;
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* The `docs` group on `MikeDownHost`: documentation bases, read through the
|
|
653
|
+
* host. Absent (npm consumers): the chat's scope picker shows "This document"
|
|
654
|
+
* only. Defined next to `MikeDownAiHost` so hosts reach it through
|
|
655
|
+
* `@mikedown/core/ai`; `host.ts` re-exports it.
|
|
656
|
+
*
|
|
657
|
+
* Containment is the host's job and is code, not prompt (S1, S2, S3, S9):
|
|
658
|
+
*
|
|
659
|
+
* - Every method takes a base id and resolves the roots from the host's own
|
|
660
|
+
* user-level configuration. A root that configuration does not list is
|
|
661
|
+
* refused, so a webview message can never name a folder. A disabled base is
|
|
662
|
+
* refused.
|
|
663
|
+
* - `readSections` looks ids up in the host's own index and refuses anything
|
|
664
|
+
* else. No path is ever built from an id's text.
|
|
665
|
+
* - Reads canonicalize the root and the file, refuse anything outside the
|
|
666
|
+
* root (whole path components), non-markdown, symlinks, non-regular files
|
|
667
|
+
* and oversize files.
|
|
668
|
+
* - The model names ids from core's index and nothing else.
|
|
669
|
+
*/
|
|
670
|
+
interface MikeDownDocsHost {
|
|
671
|
+
/**
|
|
672
|
+
* Every configured base with its health (disabled ones included). The bases
|
|
673
|
+
* themselves (`enabled`, `allowCloud`, names) are read afresh on every call.
|
|
674
|
+
* A plain call is cheap and must not walk a base that already has a usable
|
|
675
|
+
* index: the chat loop calls it once per turn. `refresh: true` forces a walk
|
|
676
|
+
* and fills the health (file counts, broken links); the manager page passes
|
|
677
|
+
* it when its section opens and on Refresh.
|
|
678
|
+
*/
|
|
679
|
+
listBases(opts?: {
|
|
680
|
+
refresh?: boolean;
|
|
681
|
+
}): Promise<AiDocBaseStatus[]>;
|
|
682
|
+
/** The base's index (pages, headings, links, homes; no bodies), built lazily and kept in memory. Rejects for an unknown, disabled or inactive base. */
|
|
683
|
+
index(baseId: string): Promise<AiDocsIndex>;
|
|
684
|
+
/**
|
|
685
|
+
* Bodies for section ids (`<page id>#<slug>`) or page ids, each capped at
|
|
686
|
+
* the section byte cap. Ids the host's index does not know are left out of
|
|
687
|
+
* the result (never an error, never a path); a read that fails a
|
|
688
|
+
* containment check is left out too. Order follows `ids`.
|
|
689
|
+
*/
|
|
690
|
+
readSections(baseId: string, ids: string[]): Promise<AiDocsSection[]>;
|
|
691
|
+
/** Case-folded scan over the base's files, headings weighted, best first, at most 20 (`docs/keyword.ts`). Ids and labels only. */
|
|
692
|
+
keywordSearch(baseId: string, query: string): Promise<AiDocsSectionRef[]>;
|
|
693
|
+
/** Tell core when the base's files change (a watcher per root only). Returns the function that stops watching. */
|
|
694
|
+
watch?(baseId: string, onChange: () => void): () => void;
|
|
695
|
+
/**
|
|
696
|
+
* Save the user's bases. The host re-validates with `parseAiSettings` and
|
|
697
|
+
* writes the user-level setting only, never a workspace one. Rejects when
|
|
698
|
+
* nothing valid is left to write over something valid.
|
|
699
|
+
*/
|
|
700
|
+
saveBases(bases: AiDocBase[]): Promise<void>;
|
|
701
|
+
/** VS Code: offer the open workspace folders (a picker when there are several) and resolve one's absolute path; null when cancelled or none is open. */
|
|
702
|
+
addWorkspaceFolder?(): Promise<string | null>;
|
|
703
|
+
/** Desktop: the native folder picker; resolves the chosen absolute path, null when cancelled. */
|
|
704
|
+
pickFolder?(): Promise<string | null>;
|
|
705
|
+
}
|
|
706
|
+
/** A model as one discovery call reported it, before merging into settings. */
|
|
707
|
+
interface AiDiscoveredModel {
|
|
708
|
+
id: string;
|
|
709
|
+
label?: string;
|
|
710
|
+
/** Endpoint values; anything absent is filled from the id heuristics on merge. */
|
|
711
|
+
capabilities: Partial<AiModelCapabilities>;
|
|
712
|
+
/** `endpoint` when the server described the model, `heuristic` when only its id was listed. */
|
|
713
|
+
source: 'endpoint' | 'heuristic';
|
|
714
|
+
loaded?: boolean;
|
|
715
|
+
/** Bytes the model takes, as the server reports it (oMLX `estimated_size`, LM Studio `size_bytes`, Ollama `size`). */
|
|
716
|
+
sizeBytes?: number;
|
|
717
|
+
/** The server says it is a mixture of experts: an architecture name with `moe` in it (LM Studio `arch`, oMLX `config_model_type`). */
|
|
718
|
+
moe?: boolean;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Host-side adapter shape. TypeScript in the extension (`src/ai/providers/`),
|
|
722
|
+
* a Rust trait with the same shape on Desktop. Declared here so both hosts
|
|
723
|
+
* share one contract; core never implements it.
|
|
724
|
+
*/
|
|
725
|
+
interface ProviderAdapter {
|
|
726
|
+
readonly kind: AiEngineKind;
|
|
727
|
+
/** Discovery plus capability detection. */
|
|
728
|
+
listModels(engine: AiEngineConfig, apiKey: string | null, signal: AbortSignal): Promise<AiDiscoveredModel[]>;
|
|
729
|
+
/** Test connection: reachability, auth, latency. */
|
|
730
|
+
probe(engine: AiEngineConfig, apiKey: string | null, signal: AbortSignal): Promise<{
|
|
731
|
+
ok: boolean;
|
|
732
|
+
latencyMs: number;
|
|
733
|
+
error?: AiError;
|
|
734
|
+
}>;
|
|
735
|
+
/** Streaming completion against one model. */
|
|
736
|
+
complete(engine: AiEngineConfig, apiKey: string | null, model: AiModelConfig, req: AiCompletionRequest, onToken: (delta: string) => void, signal: AbortSignal): Promise<AiCompletionResult>;
|
|
737
|
+
embed?(engine: AiEngineConfig, apiKey: string | null, model: AiModelConfig, texts: string[], signal: AbortSignal): Promise<number[][]>;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export { AI_CAPABILITY_FLAGS as H, AI_DOCS_MAX_HOPS_DEFAULT as I, AI_DOCS_MAX_HOPS_MAX as J, AI_DOCS_MAX_HOPS_MIN as K, AI_DOCS_NAVIGATE_BY as L, AI_DOCS_NAVIGATE_BY_DEFAULT as N, AI_DOCS_SOURCE_SHARE_DEFAULT as O, AI_DOCS_SOURCE_SHARE_MAX as P, AI_DOCS_SOURCE_SHARE_MIN as Q, AI_ENGINE_KINDS as R, AI_ERROR_CODES as S, AI_FALLBACK_MODES as T, AI_HOST_ENGINE_KINDS as U, AI_TURN_GRANT_MAX_USES as V, DEFAULT_CONTEXT_TOKENS as ag };
|
|
741
|
+
export type { AiDocHomeKind as $, AiSettings as A, AiDocBaseStatus as B, AiDocsNavigateBy as C, AiConsentRecord as D, AiLocalServerProbe as E, AiHardwareInfo as F, AiHostEngineProbe as G, MikeDownAiHost as M, AiCapabilitiesSource as W, AiCompletionTurn as X, AiConsentAbout as Y, AiConsentReply as Z, AiDocBase as _, AiEngineStatus as a, AiDocRootStatus as a0, AiDocsHeading as a1, AiDocsSettings as a2, AiEngineStatusModel as a3, AiFallbackMode as a4, AiFeatureSetting as a5, AiGhostTextOptions as a6, AiGhostTrigger as a7, AiKeySource as a8, AiLocalitySetting as a9, AiMessagePart as aa, AiRoute as ab, AiRouting as ac, AiServedBy as ad, AiTiming as ae, AiTurnConsentAbout as af, ProviderAdapter as ah, MikeDownDocsHost as b, AiModelCapabilities as c, AiCapabilityFlag as d, AiSendsScope as e, AiFeatureSettings as f, AiLocality as g, AiEngineConfig as h, AiEngineKind as i, AiModelConfig as j, AiDiscoveredModel as k, AiApiKeyFile as l, AiDocsOptions as m, AiFinishReason as n, AiUsage as o, AiErrorCode as p, AiCompletionResult as q, AiCompletionRequest as r, AiMessage as s, AiError as t, AiDocsLink as u, AiDocsIndex as v, AiDocsPage as w, AiDocsHome as x, AiDocsSectionRef as y, AiDocsSection as z };
|