@serviceme/devtools-shared 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/dist/index.d.mts +1020 -1001
  2. package/dist/index.d.ts +1020 -1001
  3. package/dist/index.js +2306 -2350
  4. package/dist/index.mjs +2250 -2268
  5. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -1,2456 +1,2412 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- BUILTIN_PROVIDER_PRESETS: () => BUILTIN_PROVIDER_PRESETS,
24
- ByomSettingsResponse: () => ByomSettingsResponse,
25
- CERTIFICATE_BUNDLE_FORMATS: () => CERTIFICATE_BUNDLE_FORMATS,
26
- CachedServerUrlResponse: () => CachedServerUrlResponse,
27
- DeviceAuthHeaders: () => DeviceAuthHeaders,
28
- GIT_REMOTE_HOST_ALIASES: () => GIT_REMOTE_HOST_ALIASES,
29
- GetByomSettings: () => GetByomSettings,
30
- GetCachedServerUrl: () => GetCachedServerUrl,
31
- GetServerProxyState: () => GetServerProxyState,
32
- GetUtilityModels: () => GetUtilityModels,
33
- LISTABLE_PRESET_MODELS: () => LISTABLE_PRESET_MODELS,
34
- LogLevel: () => LogLevel,
35
- MEDALSOFT_NUGET_PRIVATE_SOURCE: () => MEDALSOFT_NUGET_PRIVATE_SOURCE,
36
- MEDALSOFT_PRIVATE_GATEWAY_URL: () => MEDALSOFT_PRIVATE_GATEWAY_URL,
37
- MODEL_METADATA: () => MODEL_METADATA,
38
- NAMESPACE_ALIASES: () => NAMESPACE_ALIASES,
39
- NAMESPACE_ALIAS_FAMILY: () => NAMESPACE_ALIAS_FAMILY,
40
- PRESET_MODEL_FAMILIES: () => PRESET_MODEL_FAMILIES,
41
- PROVIDER_BASE_URL_PRESETS: () => PROVIDER_BASE_URL_PRESETS,
42
- PROVIDER_CACHE_CONTROL_METADATA: () => PROVIDER_CACHE_CONTROL_METADATA,
43
- ServerProxyStateResponse: () => ServerProxyStateResponse,
44
- SetCacheControlEnabled: () => SetCacheControlEnabled,
45
- SetServerProxyAllowOverride: () => SetServerProxyAllowOverride,
46
- SetServerProxyEnabled: () => SetServerProxyEnabled,
47
- UpdateUtilityModels: () => UpdateUtilityModels,
48
- UtilityModelsResponse: () => UtilityModelsResponse,
49
- WebviewMessageType: () => WebviewMessageType,
50
- __internal: () => __internal,
51
- asAbortSignal: () => asAbortSignal,
52
- buildGitHubLocalEmail: () => buildGitHubLocalEmail,
53
- buildPresetModel: () => buildPresetModel,
54
- checkGitHubOrgMembership: () => checkGitHubOrgMembership,
55
- createConsoleLogger: () => createConsoleLogger,
56
- createDeviceRequestSignature: () => createDeviceRequestSignature,
57
- currencyForBaseUrl: () => currencyForBaseUrl,
58
- effectiveAdapterType: () => effectiveAdapterType,
59
- fetchGitHubUser: () => fetchGitHubUser,
60
- getBuiltinProviderPreset: () => getBuiltinProviderPreset,
61
- getGitHubOrgMembership: () => getGitHubOrgMembership,
62
- getPresetModelDisplayName: () => getPresetModelDisplayName,
63
- getProviderBaseUrlPresets: () => getProviderBaseUrlPresets,
64
- isGitHubLocalEmail: () => isGitHubLocalEmail,
65
- isProviderCacheControlAware: () => isProviderCacheControlAware,
66
- isValidCanonicalSlug: () => isValidCanonicalSlug,
67
- isWholePackageInstall: () => isWholePackageInstall,
68
- listPresetModelGroups: () => listPresetModelGroups,
69
- lookupModelMetadata: () => lookupModelMetadata,
70
- normalizeCanonicalSlug: () => normalizeCanonicalSlug,
71
- normalizeErrorForLog: () => normalizeErrorForLog,
72
- normalizeGitUrl: () => normalizeGitUrl,
73
- parsePayload: () => parsePayload,
74
- protocolForBaseUrl: () => protocolForBaseUrl,
75
- registrationIdFromPackageId: () => registrationIdFromPackageId,
76
- resolvePrimaryEmail: () => resolvePrimaryEmail,
77
- safeJson: () => safeJson,
78
- unionProviderModelWithPreset: () => unionProviderModelWithPreset
79
- });
80
- module.exports = __toCommonJS(index_exports);
81
-
82
- // src/ai/protocol.ts
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let crypto = require("crypto");
3
+ //#region src/ai/protocol.ts
83
4
  function protocolForBaseUrl(baseUrl) {
84
- try {
85
- const url = new URL(baseUrl);
86
- return /(^|\/)anthropic(\/|$)/i.test(url.pathname) ? "anthropic" : "openai";
87
- } catch {
88
- return "openai";
89
- }
5
+ try {
6
+ const url = new URL(baseUrl);
7
+ return /(^|\/)anthropic(\/|$)/i.test(url.pathname) ? "anthropic" : "openai";
8
+ } catch {
9
+ return "openai";
10
+ }
90
11
  }
12
+ /**
13
+ * Resolve the actual `ProviderType` whose adapter should handle a
14
+ * request with the given `configuredType` + `baseUrl`.
15
+ *
16
+ * Most of the time this is just `configuredType`. The exception is
17
+ * the named `zhipu` provider with an Anthropic-protocol baseUrl
18
+ * (`/api/anthropic`); we route it through the `anthropic-compatible`
19
+ * adapter so the URL construction appends `/v1/messages` instead of
20
+ * the OpenAI `/v1/chat/completions`. Without this, the OpenAIAdapter
21
+ * would build `.../api/anthropic/chat/completions` and GLM would
22
+ * 404 (the Anthropic-compatible endpoint only serves
23
+ * `.../api/anthropic/v1/messages`).
24
+ *
25
+ * `minimax` is intentionally NOT in the override list because the
26
+ * curated preset is `https://api.minimaxi.com/anthropic` (already
27
+ * Anthropic-protocol), and the `minimax` case in the adapter
28
+ * factory already wires AnthropicAdapter unconditionally.
29
+ *
30
+ * Everything else (deepseek / kimi / stepfun / agnes / openrouter /
31
+ * novita / openai-compatible / anthropic-compatible) returns
32
+ * `configuredType` unchanged — these vendors don't publish an
33
+ * alternate-protocol endpoint on the same host.
34
+ */
91
35
  function effectiveAdapterType(configuredType, baseUrl) {
92
- if (configuredType === "zhipu" && protocolForBaseUrl(baseUrl) === "anthropic") {
93
- return "anthropic-compatible";
94
- }
95
- return configuredType;
36
+ if (configuredType === "zhipu" && protocolForBaseUrl(baseUrl) === "anthropic") return "anthropic-compatible";
37
+ return configuredType;
96
38
  }
97
-
98
- // src/constants.ts
99
- var MEDALSOFT_PRIVATE_GATEWAY_URL = "http://10.10.10.249:3000/v1";
100
- var MEDALSOFT_NUGET_PRIVATE_SOURCE = "http://192.168.20.209:10010/nuget";
101
-
102
- // src/ai/providers.base-url.ts
103
- var PROVIDER_BASE_URL_PRESETS = {
104
- "openai-compatible": [],
105
- "anthropic-compatible": [],
106
- minimax: [
107
- { label: "\u56FD\u5185", baseUrl: "https://api.minimaxi.com/anthropic" },
108
- { label: "\u5168\u7403", baseUrl: "https://api.minimax.io/anthropic" }
109
- ],
110
- deepseek: [{ label: "\u5B98\u65B9", baseUrl: "https://api.deepseek.com/v1" }],
111
- kimi: [
112
- { label: "\u56FD\u5185", baseUrl: "https://api.moonshot.cn/v1" },
113
- { label: "\u5168\u7403", baseUrl: "https://api.moonshot.ai/v1" }
114
- ],
115
- zhipu: [
116
- // Zhipu / 智谱 GLM 6 endpoint paths × 2 hosts. The 4
117
- // "credential channels" the GLM-for-copilot reference
118
- // distinguishes (region × apiMode — each with its own API
119
- // key) collapse to a 6-row baseUrl dropdown here because we
120
- // keep one API key per provider, not one per channel. The
121
- // user picks the host + path that matches the API key
122
- // they actually have; the curated `MODEL_METADATA` prices
123
- // are host-based (CNY vs USD via `currencyForBaseUrl`).
124
- //
125
- // Source: https://bigmodel.cn/pricing (CN platform, CNY) +
126
- // https://z.ai/pricing (international, USD). The 6 paths
127
- // map to:
128
- // - `/api/paas/v4` → 标准 API (Standard)
129
- // - `/api/coding/paas/v4` → Coding Plan (订阅套餐)
130
- // - `/api/anthropic` → Anthropic 兼容协议
131
- //
132
- // Earlier single-entry dropdown omitted the Coding Plan
133
- // path and the international Z.ai host entirely — users on
134
- // the Coding Plan subscription were 404'ing because they
135
- // pasted `open.bigmodel.cn/api/paas/v4` into a Coding Plan
136
- // key, and Z.ai users had no preset to pick.
137
- // ── 国内 (open.bigmodel.cn — CNY) ───────────────────────
138
- {
139
- label: "\u56FD\u5185 \xB7 \u6807\u51C6 API",
140
- baseUrl: "https://open.bigmodel.cn/api/paas/v4"
141
- },
142
- {
143
- label: "\u56FD\u5185 \xB7 Coding Plan",
144
- baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4"
145
- },
146
- // 重要:Anthropic 协议端点 **复用** Coding Plan 的 key(与
147
- // `/api/coding/paas/v4` 共用同一凭证;不是 Standard API key,
148
- // 也没有独立的 "Anthropic API key")。
149
- // Reference: `docs/references/GLM-for-copilot-main/src/i18n.ts:578-579`
150
- // "Coding Plan and Standard API credentials are independent.
151
- // OpenAI and Anthropic endpoints in the same region share
152
- // the Coding Plan key."
153
- // label 故意重复 "Coding Plan" 两次,让用户从下拉里一眼看出:
154
- // (a) 这个端点**只能配 Coding Plan key**;
155
- // (b) 这是 Coding Plan 的**协议变体**,不是 Standard API 的。
156
- {
157
- label: "\u56FD\u5185 \xB7 Coding Plan \xB7 Anthropic \u534F\u8BAE",
158
- baseUrl: "https://open.bigmodel.cn/api/anthropic"
159
- },
160
- // ── 国际 (api.z.ai USD) ───────────────────────────────
161
- { label: "\u56FD\u9645 \xB7 \u6807\u51C6 API", baseUrl: "https://api.z.ai/api/paas/v4" },
162
- {
163
- label: "\u56FD\u9645 \xB7 Coding Plan",
164
- baseUrl: "https://api.z.ai/api/coding/paas/v4"
165
- },
166
- {
167
- label: "\u56FD\u9645 \xB7 Coding Plan \xB7 Anthropic \u534F\u8BAE",
168
- baseUrl: "https://api.z.ai/api/anthropic"
169
- }
170
- ],
171
- stepfun: [{ label: "\u5B98\u65B9", baseUrl: "https://api.stepfun.com/v1" }],
172
- siliconflow: [
173
- { label: "\u56FD\u5185", baseUrl: "https://api.siliconflow.cn/v1" },
174
- { label: "\u5168\u7403", baseUrl: "https://api.siliconflow.com/v1" }
175
- ],
176
- openrouter: [{ label: "\u5B98\u65B9", baseUrl: "https://openrouter.ai/api/v1" }],
177
- novita: [{ label: "\u5B98\u65B9", baseUrl: "https://api.novita.ai/openai/v1" }],
178
- agnes: [
179
- { label: "\u56FD\u5185", baseUrl: "https://api.agnes-ai.cn/v1" },
180
- { label: "\u5168\u7403", baseUrl: "https://apihub.agnes-ai.com/v1" }
181
- ],
182
- // Medalsoft internal LLM gateway — single OpenAI-compatible endpoint.
183
- // 内网地址见 `constants.ts`(仅办公网可达);外网走 nexus。
184
- medalsoft: [
185
- { label: "\u5185\u7F51", baseUrl: MEDALSOFT_PRIVATE_GATEWAY_URL },
186
- { label: "\u5916\u7F51", baseUrl: "https://nexus.servicemecloud.com/v1" }
187
- ],
188
- // SenseNova (SenseTime) — single OpenAI-compatible token-plan
189
- // gateway (https://platform.sensenova.cn/docs). Also exposes an
190
- // Anthropic-compatible endpoint at the bare host
191
- // `https://token.sensenova.cn` (SDK appends /v1/messages), but the
192
- // named `sensenova` type routes through OpenAIAdapter, so only the
193
- // OpenAI-compat `/v1` base is offered here.
194
- sensenova: [{ label: "\u5B98\u65B9", baseUrl: "https://token.sensenova.cn/v1" }],
195
- "vscode-builtin": []
39
+ //#endregion
40
+ //#region src/constants.ts
41
+ /**
42
+ * Medalsoft-internal infrastructure addresses.
43
+ *
44
+ * These are **private-network only** endpoints — reachable from the
45
+ * company LAN/VPN, NOT from the public internet. External users must
46
+ * use the public equivalents (`nexus.servicemecloud.com`, …). Keeping
47
+ * them as named constants here (instead of inlined literals) gives a
48
+ * single place to update when the internal fleet moves, and makes the
49
+ * "this is an internal address" intent explicit at every use site.
50
+ *
51
+ * NOTE: `packages/serviceme-core` keeps its own copies of the ones it
52
+ * needs ADL-003 forbids core → shared. Keep those in lock-step.
53
+ */
54
+ /** Medalsoft 内网 LLM 网关(OpenAI-compatible `/v1`),仅办公网可达。 */
55
+ const MEDALSOFT_PRIVATE_GATEWAY_URL = "http://10.10.10.249:3000/v1";
56
+ /** Medalsoft 私有 NuGet 源,仅办公网可达。 */
57
+ const MEDALSOFT_NUGET_PRIVATE_SOURCE = "http://192.168.20.209:10010/nuget";
58
+ //#endregion
59
+ //#region src/ai/providers.base-url.ts
60
+ /**
61
+ * Provider-type known baseUrl candidates (e.g. mainland-China vs.
62
+ * global endpoints for the same vendor, like Agnes/MiniMax/DeepSeek).
63
+ * Purely a UI convenience for the Add/Edit form's baseUrl dropdown
64
+ * no runtime auto-switching reads this (that mechanism was removed;
65
+ * see git history for the retired `autoSwitch` feature).
66
+ */
67
+ const PROVIDER_BASE_URL_PRESETS = {
68
+ "openai-compatible": [],
69
+ "anthropic-compatible": [],
70
+ minimax: [{
71
+ label: "国内",
72
+ baseUrl: "https://api.minimaxi.com/anthropic"
73
+ }, {
74
+ label: "全球",
75
+ baseUrl: "https://api.minimax.io/anthropic"
76
+ }],
77
+ deepseek: [{
78
+ label: "官方",
79
+ baseUrl: "https://api.deepseek.com/v1"
80
+ }],
81
+ kimi: [{
82
+ label: "国内",
83
+ baseUrl: "https://api.moonshot.cn/v1"
84
+ }, {
85
+ label: "全球",
86
+ baseUrl: "https://api.moonshot.ai/v1"
87
+ }],
88
+ zhipu: [
89
+ {
90
+ label: "国内 · 标准 API",
91
+ baseUrl: "https://open.bigmodel.cn/api/paas/v4"
92
+ },
93
+ {
94
+ label: "国内 · Coding Plan",
95
+ baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4"
96
+ },
97
+ {
98
+ label: "国内 · Coding Plan · Anthropic 协议",
99
+ baseUrl: "https://open.bigmodel.cn/api/anthropic"
100
+ },
101
+ {
102
+ label: "国际 · 标准 API",
103
+ baseUrl: "https://api.z.ai/api/paas/v4"
104
+ },
105
+ {
106
+ label: "国际 · Coding Plan",
107
+ baseUrl: "https://api.z.ai/api/coding/paas/v4"
108
+ },
109
+ {
110
+ label: "国际 · Coding Plan · Anthropic 协议",
111
+ baseUrl: "https://api.z.ai/api/anthropic"
112
+ }
113
+ ],
114
+ stepfun: [{
115
+ label: "官方",
116
+ baseUrl: "https://api.stepfun.com/v1"
117
+ }],
118
+ siliconflow: [{
119
+ label: "国内",
120
+ baseUrl: "https://api.siliconflow.cn/v1"
121
+ }, {
122
+ label: "全球",
123
+ baseUrl: "https://api.siliconflow.com/v1"
124
+ }],
125
+ openrouter: [{
126
+ label: "官方",
127
+ baseUrl: "https://openrouter.ai/api/v1"
128
+ }],
129
+ novita: [{
130
+ label: "官方",
131
+ baseUrl: "https://api.novita.ai/openai/v1"
132
+ }],
133
+ agnes: [{
134
+ label: "国内",
135
+ baseUrl: "https://api.agnes-ai.cn/v1"
136
+ }, {
137
+ label: "全球",
138
+ baseUrl: "https://apihub.agnes-ai.com/v1"
139
+ }],
140
+ medalsoft: [{
141
+ label: "内网",
142
+ baseUrl: MEDALSOFT_PRIVATE_GATEWAY_URL
143
+ }, {
144
+ label: "外网",
145
+ baseUrl: "https://nexus.servicemecloud.com/v1"
146
+ }],
147
+ sensenova: [{
148
+ label: "官方",
149
+ baseUrl: "https://token.sensenova.cn/v1"
150
+ }],
151
+ "vscode-builtin": []
196
152
  };
153
+ /**
154
+ * Resolve the dropdown candidate list for a provider type. Returns
155
+ * an empty array (NEVER throws) for `-compatible` types or provider
156
+ * types the table doesn't cover.
157
+ */
197
158
  function getProviderBaseUrlPresets(type) {
198
- return PROVIDER_BASE_URL_PRESETS[type] ?? [];
159
+ return PROVIDER_BASE_URL_PRESETS[type] ?? [];
199
160
  }
200
-
201
- // src/ai/providers.cache-control.ts
202
- var PROVIDER_CACHE_CONTROL_METADATA = {
203
- "openai-compatible": {},
204
- "anthropic-compatible": {},
205
- minimax: {
206
- supportsCacheControl: true
207
- // Anthropic-compatible ephemeral cache (P0.1 4-breakpoint)
208
- },
209
- deepseek: {
210
- supportsCacheControl: true
211
- // OpenAI-compat prompt_cache_key
212
- },
213
- kimi: {},
214
- // 2026-08-20 — Zhipu joins the cache-aware set (learnings doc §9
215
- // task #6). Zhipu's OpenAI-compatible endpoint performs automatic
216
- // prefix caching on stable request prefixes; the `prompt_cache_key`
217
- // header the OpenAI adapter writes for cache-aware types gives the
218
- // upstream a stable per-conversation bucket (mirrors the reference's
219
- // Coding-Plan prompt-cache behaviour). The Anthropic-protocol
220
- // `/api/anthropic` routes are unaffected — that path uses the
221
- // AnthropicAdapter's own 4-breakpoint `cache_control` logic and
222
- // ignores this flag.
223
- zhipu: {
224
- supportsCacheControl: true
225
- },
226
- stepfun: {},
227
- siliconflow: {},
228
- openrouter: {},
229
- novita: {},
230
- agnes: {
231
- supportsCacheControl: true
232
- },
233
- // Medalsoft internal gateway — OpenAI-compatible pass-through; the
234
- // upstream's prompt-cache behaviour is unknown from outside, so we
235
- // don't declare support (no `prompt_cache_key` header is written).
236
- // Flip to `supportsCacheControl: true` once the gateway is verified
237
- // to honour stable prompt caching.
238
- medalsoft: {},
239
- // SenseNova (SenseTime) token-plan gateway — the /v1/models payload
240
- // publishes an `input_cache_read` pricing field (all-zero today), so
241
- // the upstream is cache-aware in principle, but there is no
242
- // documented prompt-cache key contract. Off until verified.
243
- sensenova: {},
244
- "vscode-builtin": {}
161
+ //#endregion
162
+ //#region src/ai/providers.cache-control.ts
163
+ /**
164
+ * Provider-type → cache_control capability lookup. Read by the
165
+ * OpenAI adapter's `prompt_cache_key` decision (T-04) via
166
+ * {@link isProviderCacheControlAware}.
167
+ */
168
+ const PROVIDER_CACHE_CONTROL_METADATA = {
169
+ "openai-compatible": {},
170
+ "anthropic-compatible": {},
171
+ minimax: { supportsCacheControl: true },
172
+ deepseek: { supportsCacheControl: true },
173
+ kimi: {},
174
+ zhipu: { supportsCacheControl: true },
175
+ stepfun: {},
176
+ siliconflow: {},
177
+ openrouter: {},
178
+ novita: {},
179
+ agnes: { supportsCacheControl: true },
180
+ medalsoft: {},
181
+ sensenova: {},
182
+ "vscode-builtin": {}
245
183
  };
184
+ /**
185
+ * Does this provider type declare "stable prompt cache" support?
186
+ * The OpenAI path reads this flag to decide whether to write
187
+ * `prompt_cache_key`; the Anthropic path uses its own 4-breakpoint
188
+ * logic and ignores this flag.
189
+ */
246
190
  function isProviderCacheControlAware(type) {
247
- return Boolean(PROVIDER_CACHE_CONTROL_METADATA[type]?.supportsCacheControl);
191
+ return Boolean(PROVIDER_CACHE_CONTROL_METADATA[type]?.supportsCacheControl);
248
192
  }
249
-
250
- // src/ai/providers.metadata.ts
251
- var PRIMARY_METADATA = {
252
- "MiniMax-M3": {
253
- detail: "Native multimodal frontier coding model (1M context, 512K effective)",
254
- imageInput: true,
255
- toolCalling: true,
256
- pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.06 },
257
- pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.42 },
258
- priceCategory: "medium",
259
- thinkingSchema: "thinkingEnabled",
260
- // Official spec is 1M context, but the >512K input tier is still
261
- // limited-availability (needs sales access + 2x billing per the
262
- // pricing-page footnote) and most accounts get a 400 past 512K —
263
- // see `docs/references/minimax-vscode-main/src/models/registry.ts`.
264
- // 512K is the safe effective cap for both input and output.
265
- maxInputTokens: 512e3,
266
- maxOutputTokens: 512e3
267
- },
268
- "MiniMax-M2.7": {
269
- detail: "Self-iterating coding model (~60 TPS)",
270
- imageInput: false,
271
- toolCalling: true,
272
- pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.06 },
273
- pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.42 },
274
- priceCategory: "low",
275
- // Official spec: 204,800 total context, split 131,072 input /
276
- // 73,728 output (no separate docs split exists beyond this).
277
- maxInputTokens: 131072,
278
- maxOutputTokens: 73728
279
- },
280
- "MiniMax-M2.7-highspeed": {
281
- detail: "M2.7 high-speed: same quality, faster (~100 TPS)",
282
- imageInput: false,
283
- toolCalling: true,
284
- // Per https://minimax-ai.chat/pricing (2026-07 verified):
285
- // M2.7-highspeed is 2× M2.7 base on input/output, but
286
- // identical on cache hit (same model + same infra, just a
287
- // serving-side TPS bump). The CNY values mirror USD at the
288
- // project's 1:7 CNY-per-USD convention.
289
- // Previously the CNY input/output were the same as base
290
- // (¥2.1 / ¥8.4) while USD was already 2× — that left the
291
- // USD/CNY ratio at 3.5× instead of 7× and silently
292
- // under-reported CNY cost for users on the China platform.
293
- pricingUSD: { input: 0.6, output: 2.4, cacheRead: 0.06 },
294
- pricingCNY: { input: 4.2, output: 16.8, cacheRead: 0.42 },
295
- priceCategory: "low",
296
- // Inherits M2.7's context window.
297
- maxInputTokens: 131072,
298
- maxOutputTokens: 73728
299
- },
300
- "sensenova-6.8-flash-lite": {
301
- detail: "SenseNova 6.8 Flash-Lite \u2014 \u8F7B\u91CF\u591A\u6A21\u6001\u667A\u80FD\u4F53\u6A21\u578B\uFF0C\u652F\u6301\u56FE\u50CF\u7406\u89E3 / \u5DE5\u5177\u8C03\u7528\uFF08262K \u4E0A\u4E0B\u6587\uFF0Ctoken-plan \u9650\u9891\uFF09",
302
- imageInput: true,
303
- toolCalling: true,
304
- // Per https://platform.sensenova.cn/docs (fetched 2026-08-25): the
305
- // /v1/models listing publishes all-zero pricing (token-plan free
306
- // tier, quota-limited per 5h window rather than per-token billed).
307
- // Both currency blocks pin 0 so the picker shows a free model
308
- // instead of an invented rate; when SenseNova publishes per-token
309
- // pricing, update both blocks here.
310
- pricingUSD: { input: 0, output: 0, cacheRead: 0 },
311
- pricingCNY: { input: 0, output: 0, cacheRead: 0 },
312
- priceCategory: "low",
313
- // Docs advertise `reasoning_effort` ("low"/"medium"/"high"/"none",
314
- // default "medium") and list "reasoning" in supported_features —
315
- // a leveled effort scale, so the picker gets the 3-level dropdown
316
- // and the OpenAI adapter gates the wire field.
317
- thinkingSchema: "reasoningEffort",
318
- supportsReasoningEffort: true,
319
- // Per /v1/models: context_length 262144, max_output_length 65536.
320
- maxInputTokens: 262144,
321
- maxOutputTokens: 65536
322
- },
323
- "deepseek-v4-flash": {
324
- detail: "Fast, general-purpose model",
325
- imageInput: true,
326
- toolCalling: true,
327
- // Per https://api-docs.deepseek.com/zh-cn/quick_start/pricing
328
- // (re-fetched 2026-08-20): peak/off-peak tiered pricing since
329
- // the 2026-08-17 调价. Pinned the PEAK rate (2026-08-20 product
330
- // decision — user request): DeepSeek is surfaced as a CNY
331
- // provider for China accounts, whose working hours
332
- // (09:00–12:00 + 14:00–18:00 Beijing) fall entirely inside
333
- // the peak window — peak is the conservative worst-case
334
- // estimate (cost never exceeds it; off-peak turns are billed
335
- // at exactly half).
336
- // Peak: $0.014 cache hit / $0.44 input / $1.32 output
337
- // ¥0.1 cache hit / ¥3 input / ¥9 output
338
- // Off-peak: exactly 1/2 of peak (¥1.5 / ¥4.5 / ¥0.05).
339
- pricingUSD: { input: 0.44, output: 1.32, cacheRead: 0.014 },
340
- pricingCNY: { input: 3, output: 9, cacheRead: 0.1 },
341
- priceCategory: "low",
342
- // Official docs (api-docs.deepseek.com/quick_start/pricing, fetched
343
- // 2026-07-27): "THINKING MODE: Supports both non-thinking and
344
- // thinking (default) modes" — a binary on/off switch, not a
345
- // leveled effort scale. Tool Calls confirmed supported for both
346
- // flash and pro. Was mis-set to reasoningEffort — corrected.
347
- thinkingSchema: "thinkingEnabled",
348
- // V4 context window per `docs/references/deepseek-v4-for-copilot-main/src/consts.ts`.
349
- maxInputTokens: 655360,
350
- maxOutputTokens: 393216
351
- },
352
- "deepseek-v4-pro": {
353
- detail: "Most capable reasoning model",
354
- imageInput: true,
355
- toolCalling: true,
356
- // Same source + same PEAK-tier decision as deepseek-v4-flash
357
- // (2026-08-20): conservative worst-case estimate; off-peak is
358
- // exactly half (¥4.5 / ¥13.5 / ¥0.15).
359
- // Peak: $0.044 cache hit / $1.32 input / $3.96 output
360
- // ¥0.3 cache hit / ¥9 input / ¥27 output
361
- pricingUSD: { input: 1.32, output: 3.96, cacheRead: 0.044 },
362
- pricingCNY: { input: 9, output: 27, cacheRead: 0.3 },
363
- priceCategory: "low",
364
- thinkingSchema: "thinkingEnabled",
365
- maxInputTokens: 655360,
366
- maxOutputTokens: 393216
367
- },
368
- "agnes-2.0-flash": {
369
- detail: "Fast agentic model \u2014 tool calling, coding, image understanding (512K context)",
370
- imageInput: true,
371
- toolCalling: true,
372
- // Standard (post-promo) pricing per Sapiens AI's docs
373
- // (https://wiki.agnes-ai.com — $0.03 / $0.15 per 1M tokens).
374
- // The docs also list a temporary $0 / $0 promotional rate;
375
- // we curate the standard rate here since a promo is
376
- // time-limited and would silently go stale in this table.
377
- // No separate CNY tier is published, so pricingCNY mirrors
378
- // the USD numbers rather than inventing a discount.
379
- pricingUSD: { input: 0.03, output: 0.15, cacheRead: null },
380
- pricingCNY: { input: 0.03, output: 0.15, cacheRead: null },
381
- priceCategory: "low",
382
- // Binary Thinking on/off, same `thinkingEnabled` schema as
383
- // MiniMax M3 — but translated differently on the wire: Agnes
384
- // ships only the OpenAI-compatible shape, so
385
- // `OpenAIAdapter.chat()` maps `thinkingEnabled` to
386
- // `chat_template_kwargs.enable_thinking` (per Sapiens AI's
387
- // docs) rather than AnthropicAdapter's `thinking.type` field.
388
- thinkingSchema: "thinkingEnabled",
389
- maxInputTokens: 512e3,
390
- maxOutputTokens: 65536
391
- },
392
- "agnes-2.5-pro-alpha": {
393
- detail: "Agnes 2.5 Pro Alpha \u2014 \u4ED8\u8D39\u63A8\u7406\u6A21\u578B\uFF08\u9AD8\u7EA7\u7F16\u7801\u3001\u79D1\u5B66\u63A8\u7406\u3001\u957F\u4E0A\u4E0B\u6587\u3001\u591A\u6A21\u6001\uFF09\u30021M \u4E0A\u4E0B\u6587 / 65K \u6700\u5927\u8F93\u51FA\u3002",
394
- imageInput: true,
395
- toolCalling: true,
396
- // 官方价格:$0.45 input / $0.0038 cache read / $0.90 output per 1M tokens
397
- pricingUSD: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
398
- pricingCNY: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
399
- priceCategory: "medium",
400
- thinkingSchema: "thinkingEnabled",
401
- // 上下文窗口 1M = maxInput 1M - maxOutput 65K = 934,976
402
- maxInputTokens: 934976,
403
- maxOutputTokens: 65536
404
- },
405
- "agnes-2.5-pro": {
406
- detail: "Paid reasoning model \u2014 commercial stable of 2.5 Pro Alpha (advanced coding, scientific reasoning, long context, multimodal). 1M context / 65K max output",
407
- imageInput: true,
408
- toolCalling: true,
409
- // Official pricing per https://wiki.agnes-ai.com/en/docs/agnes-25-pro.md
410
- // (fetched 2026-08-19) — identical to 2.5 Pro Alpha:
411
- // $0.45 input / $0.0038 cache read / $0.90 output per 1M tokens.
412
- // No separate CNY tier is published, so pricingCNY mirrors USD.
413
- pricingUSD: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
414
- pricingCNY: { input: 0.45, output: 0.9, cacheRead: 38e-4 },
415
- priceCategory: "medium",
416
- // Same binary Thinking on/off switch as the other Agnes models
417
- // (`chat_template_kwargs.enable_thinking` on the OpenAI wire).
418
- thinkingSchema: "thinkingEnabled",
419
- // Same published window as 2.5 Pro Alpha: 1M context /
420
- // 65536 max output — input cap mirrors the alpha entry.
421
- maxInputTokens: 934976,
422
- maxOutputTokens: 65536
423
- },
424
- "agnes-2.5-flash": {
425
- detail: "GA upgrade of Agnes 2.0 Flash \u2014 stronger coding, agent workflows, tool calling, image understanding (512K context)",
426
- imageInput: true,
427
- toolCalling: true,
428
- // Standard (post-promo) pricing per
429
- // https://wiki.agnes-ai.com/en/docs/agnes-25-flash.md (fetched
430
- // 2026-08-19) — same list price as 2.0 Flash ($0.03 / $0.15 per
431
- // 1M tokens); the temporary $0 / $0 promo is ignored per the
432
- // same policy as the 2.0 Flash entry. No separate CNY tier is
433
- // published, so pricingCNY mirrors the USD numbers.
434
- pricingUSD: { input: 0.03, output: 0.15, cacheRead: null },
435
- pricingCNY: { input: 0.03, output: 0.15, cacheRead: null },
436
- priceCategory: "low",
437
- // Same binary Thinking on/off switch as 2.0 Flash
438
- // (`chat_template_kwargs.enable_thinking` on the OpenAI wire).
439
- thinkingSchema: "thinkingEnabled",
440
- maxInputTokens: 512e3,
441
- maxOutputTokens: 65536
442
- },
443
- // ── Kimi (月之暗面 / Moonshot AI) ────────────────────────────────
444
- // Source: https://platform.kimi.com/docs/pricing/chat-k3,
445
- // chat-k27-code, chat-k26 (fetched 2026-07-27). Prices are per 1M
446
- // tokens; context window is the combined input+output budget the
447
- // docs publish for the model (not two independent maxima), so the
448
- // input/output split below sums to that published number.
449
- // Capability audit (fetched 2026-07-27, platform.kimi.com/docs/guide/
450
- // use-kimi-api-to-complete-tool-calls, use-reasoning-effort,
451
- // use-thinking-models): all four models support `tools` (confirmed
452
- // via the tool-calls guide's worked example using kimi-k3, and the
453
- // "配置多步工具调用" section explicitly naming kimi-k2.7-code /
454
- // kimi-k2.6). Thinking config differs per model:
455
- // - k3: always reasoning, leveled via top-level `reasoning_effort`
456
- // (low/high/max) -> thinkingSchema: reasoningEffort.
457
- // - k2.7-code(-highspeed): thinking is ALWAYS on, `thinking.type`
458
- // only accepts "enabled" (passing "disabled" errors) -- no user
459
- // control, so no dropdown is shown (thinkingSchema omitted).
460
- // - k2.6: thinking is a binary toggle via `thinking.type`
461
- // ("enabled" default / "disabled") -> thinkingSchema: thinkingEnabled
462
- // (not reasoningEffort -- k2.6 has no effort levels).
463
- "kimi-k3": {
464
- detail: "2.8\u4E07\u4EBF\u53C2\u6570\u65D7\u8230\u5F00\u6E90\u6A21\u578B\uFF0C1M \u4E0A\u4E0B\u6587",
465
- imageInput: true,
466
- toolCalling: true,
467
- // ¥2 cache hit / ¥20 input (cache miss) / ¥100 output per 1M tokens
468
- pricingUSD: { input: 2.8, output: 14, cacheRead: 0.28 },
469
- pricingCNY: { input: 20, output: 100, cacheRead: 2 },
470
- priceCategory: "high",
471
- thinkingSchema: "reasoningEffort",
472
- // Official context window is 1,048,576 tokens total; output
473
- // capped at 128K (consistent with other flagship models in
474
- // this table), remainder reserved for input.
475
- maxInputTokens: 920576,
476
- maxOutputTokens: 128e3
477
- },
478
- "kimi-k2.7-code": {
479
- detail: "Coding \u4E13\u7528\uFF0C\u652F\u6301\u56FE\u7247+\u89C6\u9891\u8F93\u5165\uFF0C\u5DE5\u5177\u8C03\u7528\uFF0C\u59CB\u7EC8\u5F00\u542F\u601D\u8003",
480
- imageInput: true,
481
- toolCalling: true,
482
- // ¥6.5 input / ¥27 output / ¥1.3 cache hit per 1M tokens
483
- pricingUSD: { input: 0.91, output: 3.78, cacheRead: 0.182 },
484
- pricingCNY: { input: 6.5, output: 27, cacheRead: 1.3 },
485
- priceCategory: "medium",
486
- // Official context window: 262,144 tokens total.
487
- maxInputTokens: 229376,
488
- maxOutputTokens: 32768
489
- },
490
- "kimi-k2.7-code-highspeed": {
491
- detail: "K2.7 Code \u9AD8\u901F\u7248\uFF08\u53CC\u500D\u4EF7\u683C\uFF09\uFF0C\u4E0E K2.7 Code \u540C\u4E00\u6A21\u578B",
492
- imageInput: true,
493
- toolCalling: true,
494
- // ¥13 input / ¥54 output / ¥2.6 cache hit per 1M tokens
495
- pricingUSD: { input: 1.82, output: 7.57, cacheRead: 0.364 },
496
- pricingCNY: { input: 13, output: 54, cacheRead: 2.6 },
497
- priceCategory: "high",
498
- maxInputTokens: 229376,
499
- maxOutputTokens: 32768
500
- },
501
- "kimi-k2.6": {
502
- detail: "K2.6 \u2014 \u652F\u6301\u56FE\u7247+\u89C6\u9891\u3001\u5DE5\u5177\u8C03\u7528\uFF0C\u601D\u8003\u53EF\u5F00\u5173",
503
- imageInput: true,
504
- toolCalling: true,
505
- // ¥6.5 input / ¥27 output / ¥1.1 cache hit per 1M tokens
506
- pricingUSD: { input: 0.91, output: 3.78, cacheRead: 0.154 },
507
- pricingCNY: { input: 6.5, output: 27, cacheRead: 1.1 },
508
- priceCategory: "medium",
509
- thinkingSchema: "thinkingEnabled",
510
- maxInputTokens: 229376,
511
- maxOutputTokens: 32768
512
- },
513
- // ── Zhipu / 智谱 AI (GLM) ────────────────────────────────────────
514
- // Source: https://docs.bigmodel.cn/cn/guide/start/model-overview
515
- // (context windows) + https://open.bigmodel.cn/pricing (fetched
516
- // 2026-07-27). Several GLM-5.x models publish tiered pricing by
517
- // input length ([0,32K) vs [32K+)); we curate the higher/[32K+)
518
- // tier here since our schema has no tiering — the ProvidersTab
519
- // user can still override per-provider.
520
- // toolCalling for GLM-5.x: docs.bigmodel.cn's model overview describes
521
- // GLM-5.2 as "从代码生成走向工程交付" (engineering delivery, not just
522
- // code gen), GLM-5.1 as "可自主工作长达 8 小时" (autonomous 8h work),
523
- // and GLM-5-Turbo as "复杂长任务执行连续性好" (sustained complex-task
524
- // execution) — all imply agentic tool use consistent with sibling
525
- // GLM-5's explicit "Agentic 长程规划与执行" description. Corrected
526
- // from false (inconsistent with the rest of the GLM-5 family).
527
- "glm-5.2": {
528
- detail: "GLM-5.2 \u2014 1M \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K\uFF08\u5355\u6863 pricing\uFF09",
529
- imageInput: false,
530
- toolCalling: true,
531
- // Thinking-mode dropdown + `reasoning_effort`. Zhipu official
532
- // OpenAPI (fetched 2026-08-19): "仅 GLM-5.2 及其以上模型支持"
533
- // reasoning_effort; for 5.2 the server maps none/minimal → give
534
- // up thinking, low/medium → high, xhigh → max (we only send the
535
- // native high/max so no client-side aliasing is needed).
536
- thinkingSchema: "reasoningEffort",
537
- supportsReasoningEffort: true,
538
- // Single rate (no input-length tier split) per
539
- // bigmodel.cn/pricing 2026-08-18:
540
- // ¥8 input / ¥28 output / ¥2 cache hit per 1M tokens
541
- // The previous entry's comment said "输入长度 32K+ 档" — that
542
- // was wrong: GLM-5.2 has no tier split on the official page.
543
- pricingUSD: { input: 1.12, output: 3.92, cacheRead: 0.28 },
544
- pricingCNY: { input: 8, output: 28, cacheRead: 2 },
545
- priceCategory: "high",
546
- maxInputTokens: 1e6,
547
- maxOutputTokens: 128e3
548
- },
549
- // GLM-5.3 (2026-08-14) — same 744B base as GLM-5.2 with extended
550
- // post-training for security / long-horizon coding. ~one-tenth the
551
- // per-token price of comparable U.S. frontier models; Z.ai's headline
552
- // result is 84.5% on CyberGym vulnerability-identification benchmark
553
- // (vendor-reported, no third-party replication as of 2026-08-18).
554
- // Available via GLM Coding Plan + ZCode; open weights staged ~2026-08-28.
555
- "glm-5.3": {
556
- detail: "GLM-5.3 \u2014 1M \u4E0A\u4E0B\u6587\uFF0C\u540E\u8BAD\u7EC3\u589E\u5F3A\u4EE3\u7801 / \u7F51\u7EDC\u5B89\u5168",
557
- imageInput: false,
558
- toolCalling: true,
559
- // Thinking-mode dropdown (思考模式 停用/标准/深度) + the
560
- // `reasoning_effort` field. Zhipu official OpenAPI (fetched
561
- // 2026-08-19): GLM-5.3 restricts `thinking.type` to "enabled"
562
- // and controls depth via `reasoning_effort` (low/high/max
563
- // only). Mirrors `supportsReasoningEffort: true` in
564
- // `docs/references/GLM-for-copilot-main/src/consts.ts`.
565
- thinkingSchema: "reasoningEffort",
566
- supportsReasoningEffort: true,
567
- // Vendor pricing (corrected 2026-08-20): bigmodel.cn/pricing
568
- // now lists GLM-5.3 at the SAME rate as GLM-5.2 —
569
- // ¥8 input / ¥28 output / ¥2 cache hit per 1M tokens
570
- // The previous CNY 10/31 was a mis-read of the Decrypt launch
571
- // article (2026-08-14). USD comes from Z.ai's independently-
572
- // set international list ($1.4 / $4.4 / $0.26 — not the ×0.14
573
- // CNY mirror), matching the reference consts.ts USD block.
574
- pricingUSD: { input: 1.4, output: 4.4, cacheRead: 0.26 },
575
- pricingCNY: { input: 8, output: 28, cacheRead: 2 },
576
- priceCategory: "high",
577
- maxInputTokens: 1e6,
578
- maxOutputTokens: 128e3
579
- },
580
- // GLM-5.3-Flash (2026-08-26) — GLM-5 系列首个原生多模态模型,
581
- // 320B 总参 / 18B 激活,稀疏+线性注意力混合架构,1M 上下文。
582
- // 视觉能力原生融入 Coding 循环(自动观察 UI 渲染/交互反馈并迭代)。
583
- // 比 GLM-5.3 attention 计算量与 KV 缓存分别降低 3.01× / 4.44×。
584
- // Source:
585
- // https://docs.bigmodel.cn/cn/guide/start/model-overview
586
- // https://z.ai/blog/glm-5.3-flash
587
- // https://bigmodel.cn/pricing (screenshot 2026-08-26 — 截图列
588
- // 出了限时免费期内的价格,带「5 折限时 2 周」角标,正常定价未
589
- // 公开列在定价页)。
590
- // 截图定价(screenshot 2026-08-26,原价):
591
- // 上下文: 1M
592
- // 输入: ¥0.4 / M tokens(原文"0.4 元",灰显 0.8 元)
593
- // 输出: ¥1.4 / M tokens(原文"1.4 元",灰显 2.8 元)
594
- // 缓存存储: 限时免费(写时按"百万 tokens/小时",我们的
595
- // `ModelPricing` schema 没有缓存存储字段,忽略)
596
- // 缓存命中: ¥0.115 / M tokens(原文"0.115 元",灰显 0.23 元)
597
- // 灰显数字是 5 折限时的折后价,不是新价。优惠结束后回到 0.4/1.4/0.115。
598
- // USD 按项目的 CNY/7 镜像取两位小数,与 glm-5.2 / glm-5.3 风格保持一致。
599
- "glm-5.3-flash": {
600
- detail: "GLM-5.3 Flash \u2014 320B/18B \u6FC0\u6D3B,\u7A00\u758F+\u7EBF\u6027\u6CE8\u610F\u529B\u6DF7\u5408\u67B6\u6784,\u539F\u751F\u591A\u6A21\u6001\u89C6\u89C9 Coding,1M \u4E0A\u4E0B\u6587",
601
- // 原生多模态:支持图片 / 视频 / 文件 / 文本(imageInput=true)。
602
- imageInput: true,
603
- toolCalling: true,
604
- // 思考模式:`thinking.type` 仅支持 `enabled`(不支持关闭),
605
- // 控制深度靠 `reasoning_effort`(low/high/max)。与 glm-5.3
606
- // 共用同一 schema。
607
- thinkingSchema: "reasoningEffort",
608
- supportsReasoningEffort: true,
609
- // CNY 直接取截图数字(¥0.4 / ¥1.4 / ¥0.115);USD = CNY/7
610
- // 镜像取两位小数:
611
- // 0.4 / 7 = 0.0571... → 0.057
612
- // 1.4 / 7 = 0.2
613
- // 0.115 / 7 = 0.01643... → 0.0164
614
- // 这与 glm-5.2 (¥8/¥28/¥2 $1.12/$3.92/$0.28)
615
- // 四舍五入风格保持一致;glm-5.3 用的 USD 国际独立价
616
- // ($1.4/$4.4/$0.26) 不适用于本条目 — 截图明确是 CNY
617
- // 国内口径。
618
- pricingUSD: { input: 0.057, output: 0.2, cacheRead: 0.0164 },
619
- pricingCNY: { input: 0.4, output: 1.4, cacheRead: 0.115 },
620
- priceCategory: "low",
621
- maxInputTokens: 1e6,
622
- // 官方未单独发布 5.3-Flash 的 max output。按 GLM-5.3 系列
623
- // 共识取 128K(与 glm-5.2 / glm-5.3 同),保守留 buffer
624
- // 给输入。
625
- maxOutputTokens: 128e3
626
- },
627
- // GLM-5.1-HighSpeed (2026-05) — production-grade high-throughput
628
- // variant of GLM-5.1. Same 200K context + 128K output, but optimised
629
- // via Zhipu's TileRT inference engine for 400 TPS output (~2× the
630
- // rate of comparable flagship models). Native SSE streaming + MCP
631
- // tool-call support. Listed on BigModel + Alibaba Cloud DashScope
632
- // (model id `glm-5.1-highspeed`). Pricing mirrors GLM-5.1 since the
633
- // rate is identical architecture TileRT is a serving-side optim.
634
- "glm-5.1-highspeed": {
635
- detail: "GLM-5.1 HighSpeed \u2014 400 TPS \u9AD8\u541E\u5410\u751F\u4EA7\u53D8\u4F53\uFF08[0, 32K) tier \u955C\u50CF GLM-5.1\uFF09",
636
- imageInput: false,
637
- toolCalling: true,
638
- // Thinking switch (no reasoning_effort — 5.1 < 5.2 threshold).
639
- thinkingSchema: "reasoningEffort",
640
- // Mirrors GLM-5.1 [0, 32K) tier per bigmodel.cn/pricing 2026-08-18.
641
- // TileRT is a serving-side optim; the per-token rate is the same
642
- // architecture as the base model. USD = the standard ×0.14 mirror
643
- // of 6/24/1.3 (2026-08-20 — fixed a copy-paste slip that had
644
- // cloned glm-4.6's 0.6/2.2/0.11 USD block here, a ×0.10 rate).
645
- pricingUSD: { input: 0.84, output: 3.36, cacheRead: 0.182 },
646
- pricingCNY: { input: 6, output: 24, cacheRead: 1.3 },
647
- priceCategory: "medium",
648
- maxInputTokens: 2e5,
649
- maxOutputTokens: 128e3
650
- },
651
- // GLM-4.7-Flash (2026-01-19) — 200K context, fully-free tier on
652
- // bigmodel.cn/pricing (fetched 2026-08-18). Lightweight + high-
653
- // frequency optimised; coding / writing / translation / reasoning
654
- // at "best-in-class-for-its-size" per the Zhipu release notes.
655
- // Distinct from `glm-4.7-flashx` (the latter is the 快速版 with
656
- // paid pricing).
657
- "glm-4.7-flash": {
658
- detail: "GLM-4.7 Flash \u2014 \u5B8C\u5168\u514D\u8D39\uFF08200K \u4E0A\u4E0B\u6587\uFF09",
659
- imageInput: false,
660
- toolCalling: true,
661
- // Thinking switch (no reasoning_effort — 4.7 < 5.2 threshold).
662
- thinkingSchema: "reasoningEffort",
663
- // Free tier — input / output / cache hit all 0 (bigmodel.cn
664
- // 2026-08-18 lists "免费" for every column). USD mirrors CNY
665
- // rather than inventing a rate.
666
- pricingUSD: { input: 0, output: 0, cacheRead: 0 },
667
- pricingCNY: { input: 0, output: 0, cacheRead: 0 },
668
- priceCategory: "low",
669
- maxInputTokens: 2e5,
670
- maxOutputTokens: 128e3
671
- },
672
- "glm-4.7": {
673
- detail: "GLM-4.7 \u2014 200K \u4E0A\u4E0B\u6587\uFF0C\u5DE5\u5177\u8C03\u7528\uFF083-tier pricing\uFF09",
674
- imageInput: false,
675
- toolCalling: true,
676
- // Thinking switch (GLM-4.7 forces thinking when enabled per the
677
- // official ChatThinking doc; no reasoning_effort — 4.7 < 5.2).
678
- thinkingSchema: "reasoningEffort",
679
- // Pinned the LOWEST tier per bigmodel.cn/pricing 2026-08-18:
680
- // [0, 32K) input × [0, 0.2K) output — ¥2 / ¥8 / ¥0.4 cache hit
681
- // [0, 32K) input × [0.2K+) output — ¥3 / ¥14 / ¥0.6 cache hit
682
- // [32K, 200K) input — ¥4 / ¥16 / ¥0.8 cache hit
683
- // Most real prompts are < 32K input and < 0.2K output, so the
684
- // lowest tier is the most representative per-request price.
685
- // Re-pick from a higher tier if the picker adds a length slider.
686
- pricingUSD: { input: 0.28, output: 1.12, cacheRead: 0.056 },
687
- pricingCNY: { input: 2, output: 8, cacheRead: 0.4 },
688
- priceCategory: "medium",
689
- maxInputTokens: 2e5,
690
- maxOutputTokens: 128e3
691
- },
692
- "glm-5.1": {
693
- detail: "GLM-5.1 \u2014 200K \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K\uFF082-tier pricing\uFF09",
694
- imageInput: false,
695
- toolCalling: true,
696
- // Thinking switch (no reasoning_effort — 5.1 < 5.2 threshold).
697
- thinkingSchema: "reasoningEffort",
698
- // Pinned the LOWER tier per bigmodel.cn/pricing 2026-08-18:
699
- // [0, 32K) — ¥6 input / ¥24 output / ¥1.3 cache hit
700
- // [32K+) — ¥8 input / ¥28 output / ¥2 cache hit
701
- // The previous entry had the higher tier; switched to the lower
702
- // tier so a typical < 32K prompt shows the more accurate price.
703
- pricingUSD: { input: 0.84, output: 3.36, cacheRead: 0.182 },
704
- pricingCNY: { input: 6, output: 24, cacheRead: 1.3 },
705
- priceCategory: "high",
706
- maxInputTokens: 2e5,
707
- maxOutputTokens: 128e3
708
- },
709
- "glm-5": {
710
- detail: "GLM-5 \u2014 200K \u4E0A\u4E0B\u6587\uFF0CAgentic \u5DE5\u5177\u8C03\u7528\uFF0C\u6700\u5927\u8F93\u51FA 128K\uFF082-tier pricing\uFF09",
711
- imageInput: false,
712
- toolCalling: true,
713
- // Thinking switch (no reasoning_effort — 5 < 5.2 threshold).
714
- thinkingSchema: "reasoningEffort",
715
- // Pinned the LOWER tier per bigmodel.cn/pricing 2026-08-18:
716
- // [0, 32K) — ¥4 input / ¥18 output / ¥1 cache hit
717
- // [32K+) — ¥6 input / ¥22 output / ¥1.5 cache hit
718
- pricingUSD: { input: 0.56, output: 2.52, cacheRead: 0.14 },
719
- pricingCNY: { input: 4, output: 18, cacheRead: 1 },
720
- priceCategory: "high",
721
- maxInputTokens: 2e5,
722
- maxOutputTokens: 128e3
723
- },
724
- "glm-5-turbo": {
725
- detail: "GLM-5 Turbo \u2014 200K \u4E0A\u4E0B\u6587\uFF0C\u6700\u5927\u8F93\u51FA 128K\uFF082-tier pricing\uFF09",
726
- imageInput: false,
727
- toolCalling: true,
728
- // Thinking switch (listed in the official ChatThinking doc's
729
- // auto-think family; no reasoning_effort — 5-Turbo < 5.2).
730
- thinkingSchema: "reasoningEffort",
731
- // Pinned the LOWER tier per bigmodel.cn/pricing 2026-08-18:
732
- // [0, 32K) — ¥5 input / ¥22 output / ¥1.2 cache hit
733
- // [32K+) — ¥7 input / ¥26 output / ¥1.8 cache hit
734
- pricingUSD: { input: 0.7, output: 3.08, cacheRead: 0.168 },
735
- pricingCNY: { input: 5, output: 22, cacheRead: 1.2 },
736
- priceCategory: "medium",
737
- maxInputTokens: 2e5,
738
- maxOutputTokens: 128e3
739
- },
740
- "glm-4.7-flashx": {
741
- detail: "GLM-4.7 FlashX \u2014 \u5FEB\u901F\u7248",
742
- imageInput: false,
743
- toolCalling: false,
744
- // Thinking switch (4.7 family; no reasoning_effort).
745
- thinkingSchema: "reasoningEffort",
746
- // ¥0.5 input / ¥3 output / ¥0.1 cache hit per 1M tokens
747
- pricingUSD: { input: 0.07, output: 0.42, cacheRead: 0.014 },
748
- pricingCNY: { input: 0.5, output: 3, cacheRead: 0.1 },
749
- priceCategory: "low",
750
- maxInputTokens: 2e5,
751
- maxOutputTokens: 128e3
752
- },
753
- "glm-4.6": {
754
- detail: "GLM-4.6 \u2014 \u5DE5\u5177\u8C03\u7528",
755
- imageInput: false,
756
- toolCalling: true,
757
- // Thinking switch (auto-think family per the official
758
- // ChatThinking doc; no reasoning_effort — 4.6 < 5.2).
759
- thinkingSchema: "reasoningEffort",
760
- pricingUSD: { input: 0.6, output: 2.2, cacheRead: 0.11 },
761
- pricingCNY: { input: 4.3, output: 15.7, cacheRead: 0.79 },
762
- priceCategory: "medium",
763
- maxInputTokens: 2e5,
764
- maxOutputTokens: 128e3
765
- },
766
- // `glm-4.5` (no suffix) is exposed by Zhipu's `/v1/models`
767
- // endpoint as of 2026-08-18 but is NOT listed on the public
768
- // "模型概览" page (https://docs.bigmodel.cn/cn/guide/start/model-overview).
769
- // It is almost certainly a legacy alias that routes to one of the
770
- // suffixed variants — the only sibling in the 4.5 generation
771
- // that ships tool calling is `glm-4.5-air` (128K context, 96K
772
- // max output, ¥-billed), so we mirror that metadata here. The
773
- // token caps are the most important field for the VSCode chat
774
- // picker (entries with `maxInputTokens: 0` are hidden), and the
775
- // pricing column is honest about it being a best-guess until
776
- // Zhipu publishes a dedicated spec.
777
- "glm-4.5": {
778
- detail: "GLM-4.5 \u2014 \u5DE5\u5177\u8C03\u7528\uFF08\u88F8\u540D\uFF1BZhipu /v1/models \u66B4\u9732\u7684 legacy alias\uFF09",
779
- imageInput: false,
780
- toolCalling: true,
781
- // Thinking switch (4.5 generation is the thinking-param floor
782
- // per the official ChatThinking doc; no reasoning_effort).
783
- thinkingSchema: "reasoningEffort",
784
- pricingUSD: { input: 0, output: 0, cacheRead: null },
785
- pricingCNY: { input: 0, output: 0, cacheRead: null },
786
- priceCategory: "low",
787
- maxInputTokens: 128e3,
788
- maxOutputTokens: 96e3
789
- },
790
- "glm-4.5-air": {
791
- detail: "GLM-4.5 Air \u2014 \u5DE5\u5177\u8C03\u7528\uFF083-tier pricing\uFF09",
792
- imageInput: false,
793
- toolCalling: true,
794
- // Thinking switch (4.5 family; no reasoning_effort).
795
- thinkingSchema: "reasoningEffort",
796
- // Pinned the LOWEST tier per bigmodel.cn/pricing 2026-08-18:
797
- // [0, 32K) × [0, 0.2K) output — ¥0.8 / ¥2 / ¥0.16 cache hit
798
- // [0, 32K) × [0.2K+) output — ¥0.8 / ¥6 / ¥0.16 cache hit
799
- // [32K, 128K) — ¥1.2 / ¥8 / ¥0.24 cache hit
800
- // All cache-hit rates are 4× lower than input — the
801
- // explicit-cache-discount half of BYOM-depth #1.
802
- pricingUSD: { input: 0.112, output: 0.28, cacheRead: 0.0224 },
803
- pricingCNY: { input: 0.8, output: 2, cacheRead: 0.16 },
804
- priceCategory: "low",
805
- maxInputTokens: 128e3,
806
- maxOutputTokens: 96e3
807
- },
808
- "glm-4.5-airx": {
809
- detail: "GLM-4.5 AirX \u2014 \u5FEB\u901F\u7248\uFF08\xA510/M \u5355\u6863\uFF09",
810
- imageInput: false,
811
- toolCalling: false,
812
- // Thinking switch (4.5 family; no reasoning_effort).
813
- thinkingSchema: "reasoningEffort",
814
- // ¥10 / M tokens (single rate, input == output) per
815
- // bigmodel.cn/pricing 2026-08-18 — listed under the "模型推理
816
- // → Language Models" sub-tab, NOT the flagship text section.
817
- // 8K context window per the same sub-tab; 96K max output is a
818
- // best-guess from sibling Air-tier models.
819
- pricingUSD: { input: 1.4, output: 1.4, cacheRead: null },
820
- pricingCNY: { input: 10, output: 10, cacheRead: null },
821
- priceCategory: "low",
822
- maxInputTokens: 8192,
823
- maxOutputTokens: 96e3
824
- },
825
- "glm-4-long": {
826
- detail: "GLM-4 Long \u2014 1M \u4E0A\u4E0B\u6587\uFF0C\u957F\u6587\u6863\u5904\u7406",
827
- imageInput: false,
828
- toolCalling: false,
829
- // ¥1 / M tokens (single rate, input == output per 官方定价页)
830
- pricingUSD: { input: 0.14, output: 0.14, cacheRead: null },
831
- pricingCNY: { input: 1, output: 1, cacheRead: null },
832
- priceCategory: "low",
833
- maxInputTokens: 1e6,
834
- maxOutputTokens: 4e3
835
- },
836
- "glm-4-flashx": {
837
- detail: "GLM-4 FlashX \u2014 \u5FEB\u901F\u7248",
838
- imageInput: false,
839
- toolCalling: false,
840
- // ¥0.1 / M tokens (single rate, input == output per 官方定价页)
841
- pricingUSD: { input: 0.014, output: 0.014, cacheRead: null },
842
- pricingCNY: { input: 0.1, output: 0.1, cacheRead: null },
843
- priceCategory: "low",
844
- maxInputTokens: 128e3,
845
- maxOutputTokens: 16e3
846
- },
847
- "glm-4-plus": {
848
- detail: "GLM-4 Plus \u2014 Function Call \u652F\u6301",
849
- imageInput: false,
850
- toolCalling: true,
851
- // ¥5 input per 1M tokens
852
- pricingUSD: { input: 0.7, output: 0.7, cacheRead: null },
853
- pricingCNY: { input: 5, output: 5, cacheRead: null },
854
- priceCategory: "low",
855
- maxInputTokens: 128e3,
856
- maxOutputTokens: 4e3
857
- },
858
- "glm-4.5v": {
859
- detail: "GLM-4.5V \u89C6\u89C9\u63A8\u7406\u6A21\u578B \u2014 \u56FE\u50CF/\u89C6\u9891/\u6587\u6863/GUI\uFF082-tier pricing\uFF09",
860
- imageInput: true,
861
- toolCalling: true,
862
- // Thinking switch (official ChatThinking doc: GLM-4.5V 为强制
863
- // 思考 when enabled; no reasoning_effort).
864
- thinkingSchema: "reasoningEffort",
865
- // Pinned the LOWER tier per bigmodel.cn/pricing 2026-08-18:
866
- // [0, 32K) — ¥2 input / ¥6 output / ¥0.4 cache hit
867
- // [32, 64K) — ¥4 input / ¥12 output / ¥0.8 cache hit
868
- pricingUSD: { input: 0.28, output: 0.84, cacheRead: 0.056 },
869
- pricingCNY: { input: 2, output: 6, cacheRead: 0.4 },
870
- priceCategory: "medium",
871
- maxInputTokens: 64e3,
872
- maxOutputTokens: 8192
873
- },
874
- // GLM-4.6V (2026 H1) — 视觉推理模型 (paid version, 2-tier
875
- // pricing). Distinct from `glm-4.6v-flash` (免费版, 0/0/0) and
876
- // `glm-4.6v-flashx` (快速版, ¥0.15/¥1.5/¥0.03). Per
877
- // bigmodel.cn/pricing 2026-08-18 (the same snapshot the
878
- // §7 table in `byom-glm-learnings.md` was sourced from, where
879
- // this row was first documented but never landed in
880
- // MODEL_METADATA until 2026-08-19):
881
- // [0, 32K) — ¥1 input / ¥3 output / ¥0.2 cache hit
882
- // [32, 128K) — ¥2 input / ¥6 output / ¥0.4 cache hit
883
- // The lower tier is pinned per the project's "multi-tier →
884
- // lowest tier" convention (most prompts < 32K input × < 0.2K
885
- // output). 4.6V sits between glm-4.5v (¥2/¥6/¥0.4) and the
886
- // flashx variant — the 4.6 generation is meaningfully cheaper
887
- // per the 2026 mid-year price reset.
888
- "glm-4.6v": {
889
- detail: "GLM-4.6V \u89C6\u89C9\u63A8\u7406\u6A21\u578B \u2014 \u56FE\u50CF/\u89C6\u9891/\u6587\u6863/GUI\uFF082-tier pricing\uFF09",
890
- imageInput: true,
891
- toolCalling: true,
892
- // Thinking switch (same auto-think family as glm-4.5v;
893
- // 4.6 < 5.2 threshold, so no reasoning_effort).
894
- thinkingSchema: "reasoningEffort",
895
- // Pinned the LOWER tier [0, 32K) per bigmodel.cn/pricing
896
- // 2026-08-18. USD mirrors the CNY/7 ratio with 2-decimal
897
- // rounding (matches glm-4.5v's USD rounding style).
898
- pricingUSD: { input: 0.14, output: 0.42, cacheRead: 0.028 },
899
- pricingCNY: { input: 1, output: 3, cacheRead: 0.2 },
900
- // Tier boundary is 128K (vs glm-4.5v's 64K), so a
901
- // `medium` tier tag is still appropriate — the 4.6V is
902
- // cheaper than glm-4.5v but not a "low" tier like the
903
- // 0.5/3/0.1 flashx family.
904
- priceCategory: "medium",
905
- maxInputTokens: 128e3,
906
- maxOutputTokens: 8192
907
- },
908
- // GLM-4.6V-Flash — the FULLY-FREE vision model on
909
- // bigmodel.cn/pricing's 视觉理解 tab (every column 免费,
910
- // 2026-08-18 snapshot). This is the exact model the GLM-for-
911
- // copilot reference uses as its transparent vision proxy
912
- // (`DEFAULT_GLM_VISION_MODEL_ID` in its consts.ts — "the
913
- // extension accepts images for text models through the
914
- // GLM-4.6V-Flash vision proxy"). Zero cost makes it the
915
- // natural candidate for our future ProxyVisionAdapter (v2).
916
- // Token caps mirror the reference's entry (96K input + 32K
917
- // output = the 128K shared window). Not in the zhipu preset —
918
- // multimodal models aren't exposed via the public
919
- // `/v1/chat/completions` models list (same trim rationale as
920
- // glm-5v-turbo / glm-4.5v); users add it by hand.
921
- "glm-4.6v-flash": {
922
- detail: "GLM-4.6V Flash \u2014 \u5B8C\u5168\u514D\u8D39\u89C6\u89C9\u7406\u89E3\u6A21\u578B",
923
- imageInput: true,
924
- // Reference consts.ts declares GLM_TOOLS_LIMIT (128) tool
925
- // calling for this model — the flash vision tier keeps the
926
- // full function-call surface.
927
- toolCalling: true,
928
- // Thinking switch (auto-think family per the reference's
929
- // `requiresThinkingParam: true`; no reasoning_effort —
930
- // 4.6 < 5.2 threshold).
931
- thinkingSchema: "reasoningEffort",
932
- // Free tier — input / output / cache hit all 0 per
933
- // bigmodel.cn 2026-08-18. USD mirrors CNY at 0 rather than
934
- // inventing a rate.
935
- pricingUSD: { input: 0, output: 0, cacheRead: 0 },
936
- pricingCNY: { input: 0, output: 0, cacheRead: 0 },
937
- priceCategory: "low",
938
- maxInputTokens: 98304,
939
- maxOutputTokens: 32768
940
- },
941
- // GLM-4.6V-FlashX — the 快速版 (quick) paid sibling of the free
942
- // glm-4.6v-flash. Per bigmodel.cn/pricing 视觉理解 tab
943
- // (2026-08-18):
944
- // [0, 32K) — ¥0.15 input / ¥1.5 output / ¥0.03 cache hit
945
- // [32K, 128K) — ¥0.3 input / ¥3 output / ¥0.03 cache hit
946
- // The LOWER tier is pinned per the project's multi-tier
947
- // convention. Not in the zhipu preset (same multimodal trim
948
- // rationale as glm-4.6v-flash). Token caps are a best-guess
949
- // from sibling glm-4.6v (128K context / 8K output) — Zhipu
950
- // publishes no dedicated spec for the flashx vision tier.
951
- "glm-4.6v-flashx": {
952
- detail: "GLM-4.6V FlashX \u2014 \u5FEB\u901F\u7248\u89C6\u89C9\u7406\u89E3\uFF082-tier pricing\uFF09",
953
- imageInput: true,
954
- // 快速版 convention: the flashx siblings (glm-4.7-flashx,
955
- // glm-4.5-airx) drop tool calling.
956
- toolCalling: false,
957
- thinkingSchema: "reasoningEffort",
958
- pricingUSD: { input: 0.021, output: 0.21, cacheRead: 42e-4 },
959
- pricingCNY: { input: 0.15, output: 1.5, cacheRead: 0.03 },
960
- priceCategory: "low",
961
- maxInputTokens: 128e3,
962
- maxOutputTokens: 8192
963
- },
964
- "glm-5v-turbo": {
965
- detail: "GLM-5V Turbo \u2014 \u591A\u6A21\u6001 Coding \u6A21\u578B\uFF082-tier pricing\uFF09",
966
- imageInput: true,
967
- toolCalling: true,
968
- // Thinking switch (listed in the official ChatThinking doc's
969
- // auto-think family; no reasoning_effort).
970
- thinkingSchema: "reasoningEffort",
971
- // Pinned the LOWER tier per bigmodel.cn/pricing 2026-08-18:
972
- // [0, 32K) — ¥5 input / ¥22 output / ¥1.2 cache hit
973
- // [32K+) — ¥7 input / ¥26 output / ¥1.8 cache hit
974
- // Vendor-published context: 200K / 128K max output.
975
- pricingUSD: { input: 0.7, output: 3.08, cacheRead: 0.168 },
976
- pricingCNY: { input: 5, output: 22, cacheRead: 1.2 },
977
- priceCategory: "medium",
978
- maxInputTokens: 2e5,
979
- maxOutputTokens: 128e3
980
- },
981
- "glm-3-turbo": {
982
- detail: "GLM-3 Turbo \u2014 \u5165\u95E8\u7EA7\uFF08\xA51/1M tokens\uFF09",
983
- imageInput: false,
984
- toolCalling: false,
985
- pricingUSD: { input: 0.14, output: 0.14, cacheRead: null },
986
- pricingCNY: { input: 1, output: 1, cacheRead: null },
987
- priceCategory: "low",
988
- maxInputTokens: 128e3,
989
- maxOutputTokens: 8192
990
- },
991
- // ── StepFun / 阶跃星辰 ───────────────────────────────────────────
992
- // Source: https://platform.stepfun.com/docs/zh/guides/models/
993
- // step-3.7-flash, step-3.5-flash + guides/models (overview page,
994
- // fetched 2026-07-27). Both flash models publish "256K tokens" as
995
- // a single combined context number; the exact input/output split
996
- // below matches Novita's real-world deployment of step-3.7-flash
997
- // (262,144 total / 256,000 max output) since StepFun's own docs
998
- // don't split it further.
999
- "step-3.7-flash": {
1000
- detail: "Step 3.7 Flash \u2014 \u591A\u6A21\u6001\u63A8\u7406\u6A21\u578B\uFF08198B/11B MoE\uFF09",
1001
- imageInput: true,
1002
- // Official model page lists "🛠️ 工具调用: 可靠的工具调用能力,支持多步
1003
- // 任务分解与计划执行" as a core capability — was mis-set to false.
1004
- toolCalling: true,
1005
- // Per https://platform.stepfun.com/docs/zh/pricing/details
1006
- // (2026-08-18 fetched): ¥1.35 input / ¥8.1 output /
1007
- // ¥0.27 cache hit per 1M tokens, USD = $0.20 / $1.15 /
1008
- // $0.04 (StepFun is USD-billed at the same rate as CNY/7
1009
- // with small rounding per the official pricing page).
1010
- // USD values previously 0.189/1.134/0.038 — slightly off
1011
- // from the official page (rounding error from dividing CNY
1012
- // by hand), corrected.
1013
- pricingUSD: { input: 0.2, output: 1.15, cacheRead: 0.04 },
1014
- pricingCNY: { input: 1.35, output: 8.1, cacheRead: 0.27 },
1015
- priceCategory: "medium",
1016
- thinkingSchema: "reasoningEffort",
1017
- // Was 0 (a bug — VSCode's chat picker hides entries whose
1018
- // maxInputTokens is 0). Corrected to the official 256K context
1019
- // window (262,144 tokens) minus the published 256,000 max output.
1020
- maxInputTokens: 262144,
1021
- maxOutputTokens: 256e3
1022
- },
1023
- "step-3.5-flash": {
1024
- detail: "Step 3.5 Flash \u2014 \u63A8\u7406\u6A21\u578B",
1025
- imageInput: false,
1026
- // Official model page lists "🛠️ 工具调用: 可靠的 tools / tool_choice
1027
- // 调用能力" as a core capability — was mis-set to false.
1028
- toolCalling: true,
1029
- // Per https://platform.stepfun.com/docs/zh/pricing/details
1030
- // (2026-08-18 fetched): ¥0.7 input / ¥2.1 output /
1031
- // ¥0.14 cache hit per 1M tokens, USD = $0.10 / $0.30 /
1032
- // $0.02. USD values previously 0.098/0.294/0.02 — slightly
1033
- // off from the official page (rounding error), corrected
1034
- // to the exact published values.
1035
- pricingUSD: { input: 0.1, output: 0.3, cacheRead: 0.02 },
1036
- pricingCNY: { input: 0.7, output: 2.1, cacheRead: 0.14 },
1037
- priceCategory: "low",
1038
- thinkingSchema: "reasoningEffort",
1039
- maxInputTokens: 256e3,
1040
- maxOutputTokens: 256e3
1041
- },
1042
- "step-1o-turbo-vision": {
1043
- detail: "Step 1o Turbo Vision \u2014 \u89C6\u89C9\u6A21\u578B",
1044
- imageInput: true,
1045
- toolCalling: false,
1046
- // Per https://platform.stepfun.com/docs/zh/pricing/details
1047
- // (2026-08-18 fetched): ¥2.5 cache miss / ¥0.5 cache hit /
1048
- // ¥8 output per 1M tokens. USD = $0.357 / $0.071 / $1.143
1049
- // (CNY/7 with rounding). Cache hit was previously
1050
- // undocumented in the curated entry — added.
1051
- pricingUSD: { input: 0.357, output: 1.143, cacheRead: 0.071 },
1052
- pricingCNY: { input: 2.5, output: 8, cacheRead: 0.5 },
1053
- priceCategory: "low",
1054
- // Official model overview: 32K context window.
1055
- maxInputTokens: 32768,
1056
- maxOutputTokens: 32768
1057
- },
1058
- // ── 聚合平台占位 ────────────────────────────────────────────────────
1059
- // 这些平台是 model aggregator — 模型列表由平台动态维护。
1060
- // 当 `provider.baseUrl` 与对应平台匹配时(即 OpenRouter / Novita),
1061
- // 用户可通过 `Fetch from API` 从 `/v1/models` 拉取真实列表;
1062
- // 下面这些 metadata 条目只是为了支持 `BUILTIN_PROVIDER_PRESETS` 中的预设。
1063
- "openrouter/auto": {
1064
- detail: "OpenRouter Auto \u2014 \u81EA\u52A8\u8DEF\u7531\u5230\u5E73\u53F0\u6700\u4F73\u6A21\u578B",
1065
- imageInput: true,
1066
- toolCalling: true,
1067
- // OpenRouter 透传上游价格,自身不标价。占位 0 由用户在 provider form 调整。
1068
- pricingUSD: { input: 0, output: 0, cacheRead: null },
1069
- pricingCNY: { input: 0, output: 0, cacheRead: null },
1070
- priceCategory: "medium",
1071
- maxInputTokens: 0,
1072
- maxOutputTokens: 0
1073
- },
1074
- "novita-ai/novita-3.5-flash": {
1075
- detail: "Novita 3.5 Flash \u2014 Novita \u5E73\u53F0\u70ED\u95E8\u6A21\u578B\uFF08\u5360\u4F4D\uFF0C\u6309\u5B9E\u9645\u9009\u62E9\u8C03\u6574\uFF09",
1076
- imageInput: true,
1077
- toolCalling: true,
1078
- pricingUSD: { input: 0.1, output: 0.4, cacheRead: null },
1079
- pricingCNY: { input: 0.1, output: 0.4, cacheRead: null },
1080
- priceCategory: "low",
1081
- maxInputTokens: 32768,
1082
- maxOutputTokens: 32768
1083
- },
1084
- // ── MiniMax M2.5 (2026-02-13) ──────────────────────────────────────
1085
- // 229B MoE, 80.2% SWE-Bench Verified, the predecessor to M2.7.
1086
- // Source: https://siliconflow.cn/models?series=qwen (MiniMax card),
1087
- // platform.minimaxi.com/docs/release-notes/models (M2.5 release note).
1088
- "MiniMax-M2.5": {
1089
- detail: "MiniMax M2.5 \u2014 229B MoE, SOTA \u7F16\u7A0B / Agent / \u529E\u516C\u751F\u4EA7\u529B\uFF08192K \u4E0A\u4E0B\u6587\uFF09",
1090
- imageInput: false,
1091
- toolCalling: true,
1092
- // Per https://minimax-ai.chat/pricing (M2.5 legacy line):
1093
- // ¥2.1 input / ¥8.4 output / ¥0.21 cache hit per 1M tokens;
1094
- // USD = $0.30 / $1.20 / $0.03 (cloudprice.net 2026-08-13).
1095
- // Cache hit IS the published rate — the previous comment
1096
- // "按官方 10% cache 命中率回填" was wrong (it implied we
1097
- // were estimating, when actually the cache rate is
1098
- // documented at ¥0.21 / $0.03 per 1M tokens).
1099
- pricingUSD: { input: 0.3, output: 1.2, cacheRead: 0.03 },
1100
- pricingCNY: { input: 2.1, output: 8.4, cacheRead: 0.21 },
1101
- priceCategory: "medium",
1102
- // 官方 context 192K;output 按 16K 保守估值(M2.7 标 128K,M2.5
1103
- // 官方未单独发布 max output 数字,按其同代老模型惯例取 16K)。
1104
- maxInputTokens: 192e3,
1105
- maxOutputTokens: 16384
1106
- },
1107
- // ── Qwen3.6-35B-A3B(2026-04-17 通义千问)────────────────────────────
1108
- // 35B MoE,激活仅 3B,256K 上下文;2026 年 Qwen3.6 系列首发
1109
- // small-size open-weight。"激活成本 1/10" 是其与前代 Qwen3.5-27B
1110
- // 相比的核心卖点。Source:
1111
- // https://siliconflow.cn/news/z12t3edpv6ypbuja3o65lgh2
1112
- "Qwen3.6-35B-A3B": {
1113
- detail: "Qwen3.6-35B-A3B \u2014 35B MoE (3B \u6FC0\u6D3B)\uFF0C\u601D\u8003/\u975E\u601D\u8003\u53CC\u6A21\uFF0C256K \u4E0A\u4E0B\u6587",
1114
- imageInput: true,
1115
- toolCalling: true,
1116
- // SiliconFlow 官方价格页(https://siliconflow.cn/pricing,
1117
- // re-fetched 2026-08-20):¥1.8 input / ¥10.8 output per 1M
1118
- // tokens,无缓存价格列。此前 ¥1.6/¥12.8 来自发布新闻稿,
1119
- // 已过时(输入上调、输出下调)。USD = CNY ÷7 镜像取两位
1120
- // 小数(0.26 / 1.54)。
1121
- pricingUSD: { input: 0.26, output: 1.54, cacheRead: null },
1122
- pricingCNY: { input: 1.8, output: 10.8, cacheRead: null },
1123
- priceCategory: "low",
1124
- // Qwen3.6-35B-A3B 官方 256K context;output 上限按同代 27B
1125
- // 同样 32K 取值(Qwen3.6 系列 max output 未单独公布)。
1126
- maxInputTokens: 256e3,
1127
- maxOutputTokens: 32768
1128
- }
193
+ //#endregion
194
+ //#region src/ai/providers.metadata.ts
195
+ const PRIMARY_METADATA = {
196
+ "MiniMax-M3": {
197
+ detail: "Native multimodal frontier coding model (1M context, 512K effective)",
198
+ imageInput: true,
199
+ toolCalling: true,
200
+ pricingUSD: {
201
+ input: .3,
202
+ output: 1.2,
203
+ cacheRead: .06
204
+ },
205
+ pricingCNY: {
206
+ input: 2.1,
207
+ output: 8.4,
208
+ cacheRead: .42
209
+ },
210
+ priceCategory: "medium",
211
+ thinkingSchema: "thinkingEnabled",
212
+ maxInputTokens: 512e3,
213
+ maxOutputTokens: 512e3
214
+ },
215
+ "MiniMax-M2.7": {
216
+ detail: "Self-iterating coding model (~60 TPS)",
217
+ imageInput: false,
218
+ toolCalling: true,
219
+ pricingUSD: {
220
+ input: .3,
221
+ output: 1.2,
222
+ cacheRead: .06
223
+ },
224
+ pricingCNY: {
225
+ input: 2.1,
226
+ output: 8.4,
227
+ cacheRead: .42
228
+ },
229
+ priceCategory: "low",
230
+ maxInputTokens: 131072,
231
+ maxOutputTokens: 73728
232
+ },
233
+ "MiniMax-M2.7-highspeed": {
234
+ detail: "M2.7 high-speed: same quality, faster (~100 TPS)",
235
+ imageInput: false,
236
+ toolCalling: true,
237
+ pricingUSD: {
238
+ input: .6,
239
+ output: 2.4,
240
+ cacheRead: .06
241
+ },
242
+ pricingCNY: {
243
+ input: 4.2,
244
+ output: 16.8,
245
+ cacheRead: .42
246
+ },
247
+ priceCategory: "low",
248
+ maxInputTokens: 131072,
249
+ maxOutputTokens: 73728
250
+ },
251
+ "sensenova-6.8-flash-lite": {
252
+ detail: "SenseNova 6.8 Flash-Lite 轻量多模态智能体模型,支持图像理解 / 工具调用(262K 上下文,token-plan 限频)",
253
+ imageInput: true,
254
+ toolCalling: true,
255
+ pricingUSD: {
256
+ input: 0,
257
+ output: 0,
258
+ cacheRead: 0
259
+ },
260
+ pricingCNY: {
261
+ input: 0,
262
+ output: 0,
263
+ cacheRead: 0
264
+ },
265
+ priceCategory: "low",
266
+ thinkingSchema: "reasoningEffort",
267
+ supportsReasoningEffort: true,
268
+ maxInputTokens: 262144,
269
+ maxOutputTokens: 65536
270
+ },
271
+ "deepseek-v4-flash": {
272
+ detail: "Fast, general-purpose model",
273
+ imageInput: true,
274
+ toolCalling: true,
275
+ pricingUSD: {
276
+ input: .44,
277
+ output: 1.32,
278
+ cacheRead: .014
279
+ },
280
+ pricingCNY: {
281
+ input: 3,
282
+ output: 9,
283
+ cacheRead: .1
284
+ },
285
+ priceCategory: "low",
286
+ thinkingSchema: "thinkingEnabled",
287
+ maxInputTokens: 655360,
288
+ maxOutputTokens: 393216
289
+ },
290
+ "deepseek-v4-pro": {
291
+ detail: "Most capable reasoning model",
292
+ imageInput: true,
293
+ toolCalling: true,
294
+ pricingUSD: {
295
+ input: 1.32,
296
+ output: 3.96,
297
+ cacheRead: .044
298
+ },
299
+ pricingCNY: {
300
+ input: 9,
301
+ output: 27,
302
+ cacheRead: .3
303
+ },
304
+ priceCategory: "low",
305
+ thinkingSchema: "thinkingEnabled",
306
+ maxInputTokens: 655360,
307
+ maxOutputTokens: 393216
308
+ },
309
+ "agnes-2.0-flash": {
310
+ detail: "Fast agentic model — tool calling, coding, image understanding (512K context)",
311
+ imageInput: true,
312
+ toolCalling: true,
313
+ pricingUSD: {
314
+ input: .03,
315
+ output: .15,
316
+ cacheRead: null
317
+ },
318
+ pricingCNY: {
319
+ input: .03,
320
+ output: .15,
321
+ cacheRead: null
322
+ },
323
+ priceCategory: "low",
324
+ thinkingSchema: "thinkingEnabled",
325
+ maxInputTokens: 512e3,
326
+ maxOutputTokens: 65536
327
+ },
328
+ "agnes-2.5-pro-alpha": {
329
+ detail: "Agnes 2.5 Pro Alpha — 付费推理模型(高级编码、科学推理、长上下文、多模态)。1M 上下文 / 65K 最大输出。",
330
+ imageInput: true,
331
+ toolCalling: true,
332
+ pricingUSD: {
333
+ input: .45,
334
+ output: .9,
335
+ cacheRead: .0038
336
+ },
337
+ pricingCNY: {
338
+ input: .45,
339
+ output: .9,
340
+ cacheRead: .0038
341
+ },
342
+ priceCategory: "medium",
343
+ thinkingSchema: "thinkingEnabled",
344
+ maxInputTokens: 934976,
345
+ maxOutputTokens: 65536
346
+ },
347
+ "agnes-2.5-pro": {
348
+ detail: "Paid reasoning model — commercial stable of 2.5 Pro Alpha (advanced coding, scientific reasoning, long context, multimodal). 1M context / 65K max output",
349
+ imageInput: true,
350
+ toolCalling: true,
351
+ pricingUSD: {
352
+ input: .45,
353
+ output: .9,
354
+ cacheRead: .0038
355
+ },
356
+ pricingCNY: {
357
+ input: .45,
358
+ output: .9,
359
+ cacheRead: .0038
360
+ },
361
+ priceCategory: "medium",
362
+ thinkingSchema: "thinkingEnabled",
363
+ maxInputTokens: 934976,
364
+ maxOutputTokens: 65536
365
+ },
366
+ "agnes-2.5-flash": {
367
+ detail: "GA upgrade of Agnes 2.0 Flash — stronger coding, agent workflows, tool calling, image understanding (512K context)",
368
+ imageInput: true,
369
+ toolCalling: true,
370
+ pricingUSD: {
371
+ input: .03,
372
+ output: .15,
373
+ cacheRead: null
374
+ },
375
+ pricingCNY: {
376
+ input: .03,
377
+ output: .15,
378
+ cacheRead: null
379
+ },
380
+ priceCategory: "low",
381
+ thinkingSchema: "thinkingEnabled",
382
+ maxInputTokens: 512e3,
383
+ maxOutputTokens: 65536
384
+ },
385
+ "kimi-k3": {
386
+ detail: "2.8万亿参数旗舰开源模型,1M 上下文",
387
+ imageInput: true,
388
+ toolCalling: true,
389
+ pricingUSD: {
390
+ input: 2.8,
391
+ output: 14,
392
+ cacheRead: .28
393
+ },
394
+ pricingCNY: {
395
+ input: 20,
396
+ output: 100,
397
+ cacheRead: 2
398
+ },
399
+ priceCategory: "high",
400
+ thinkingSchema: "reasoningEffort",
401
+ maxInputTokens: 920576,
402
+ maxOutputTokens: 128e3
403
+ },
404
+ "kimi-k2.7-code": {
405
+ detail: "Coding 专用,支持图片+视频输入,工具调用,始终开启思考",
406
+ imageInput: true,
407
+ toolCalling: true,
408
+ pricingUSD: {
409
+ input: .91,
410
+ output: 3.78,
411
+ cacheRead: .182
412
+ },
413
+ pricingCNY: {
414
+ input: 6.5,
415
+ output: 27,
416
+ cacheRead: 1.3
417
+ },
418
+ priceCategory: "medium",
419
+ maxInputTokens: 229376,
420
+ maxOutputTokens: 32768
421
+ },
422
+ "kimi-k2.7-code-highspeed": {
423
+ detail: "K2.7 Code 高速版(双倍价格),与 K2.7 Code 同一模型",
424
+ imageInput: true,
425
+ toolCalling: true,
426
+ pricingUSD: {
427
+ input: 1.82,
428
+ output: 7.57,
429
+ cacheRead: .364
430
+ },
431
+ pricingCNY: {
432
+ input: 13,
433
+ output: 54,
434
+ cacheRead: 2.6
435
+ },
436
+ priceCategory: "high",
437
+ maxInputTokens: 229376,
438
+ maxOutputTokens: 32768
439
+ },
440
+ "kimi-k2.6": {
441
+ detail: "K2.6 — 支持图片+视频、工具调用,思考可开关",
442
+ imageInput: true,
443
+ toolCalling: true,
444
+ pricingUSD: {
445
+ input: .91,
446
+ output: 3.78,
447
+ cacheRead: .154
448
+ },
449
+ pricingCNY: {
450
+ input: 6.5,
451
+ output: 27,
452
+ cacheRead: 1.1
453
+ },
454
+ priceCategory: "medium",
455
+ thinkingSchema: "thinkingEnabled",
456
+ maxInputTokens: 229376,
457
+ maxOutputTokens: 32768
458
+ },
459
+ "glm-5.2": {
460
+ detail: "GLM-5.2 1M 上下文,最大输出 128K(单档 pricing)",
461
+ imageInput: false,
462
+ toolCalling: true,
463
+ thinkingSchema: "reasoningEffort",
464
+ supportsReasoningEffort: true,
465
+ pricingUSD: {
466
+ input: 1.12,
467
+ output: 3.92,
468
+ cacheRead: .28
469
+ },
470
+ pricingCNY: {
471
+ input: 8,
472
+ output: 28,
473
+ cacheRead: 2
474
+ },
475
+ priceCategory: "high",
476
+ maxInputTokens: 1e6,
477
+ maxOutputTokens: 128e3
478
+ },
479
+ "glm-5.3": {
480
+ detail: "GLM-5.3 — 1M 上下文,后训练增强代码 / 网络安全",
481
+ imageInput: false,
482
+ toolCalling: true,
483
+ thinkingSchema: "reasoningEffort",
484
+ supportsReasoningEffort: true,
485
+ pricingUSD: {
486
+ input: 1.4,
487
+ output: 4.4,
488
+ cacheRead: .26
489
+ },
490
+ pricingCNY: {
491
+ input: 8,
492
+ output: 28,
493
+ cacheRead: 2
494
+ },
495
+ priceCategory: "high",
496
+ maxInputTokens: 1e6,
497
+ maxOutputTokens: 128e3
498
+ },
499
+ "glm-5.3-flash": {
500
+ detail: "GLM-5.3 Flash 320B/18B 激活,稀疏+线性注意力混合架构,原生多模态视觉 Coding,1M 上下文",
501
+ imageInput: true,
502
+ toolCalling: true,
503
+ thinkingSchema: "reasoningEffort",
504
+ supportsReasoningEffort: true,
505
+ pricingUSD: {
506
+ input: .057,
507
+ output: .2,
508
+ cacheRead: .0164
509
+ },
510
+ pricingCNY: {
511
+ input: .4,
512
+ output: 1.4,
513
+ cacheRead: .115
514
+ },
515
+ priceCategory: "low",
516
+ maxInputTokens: 1e6,
517
+ maxOutputTokens: 128e3
518
+ },
519
+ "glm-5.1-highspeed": {
520
+ detail: "GLM-5.1 HighSpeed — 400 TPS 高吞吐生产变体([0, 32K) tier 镜像 GLM-5.1)",
521
+ imageInput: false,
522
+ toolCalling: true,
523
+ thinkingSchema: "reasoningEffort",
524
+ pricingUSD: {
525
+ input: .84,
526
+ output: 3.36,
527
+ cacheRead: .182
528
+ },
529
+ pricingCNY: {
530
+ input: 6,
531
+ output: 24,
532
+ cacheRead: 1.3
533
+ },
534
+ priceCategory: "medium",
535
+ maxInputTokens: 2e5,
536
+ maxOutputTokens: 128e3
537
+ },
538
+ "glm-4.7-flash": {
539
+ detail: "GLM-4.7 Flash — 完全免费(200K 上下文)",
540
+ imageInput: false,
541
+ toolCalling: true,
542
+ thinkingSchema: "reasoningEffort",
543
+ pricingUSD: {
544
+ input: 0,
545
+ output: 0,
546
+ cacheRead: 0
547
+ },
548
+ pricingCNY: {
549
+ input: 0,
550
+ output: 0,
551
+ cacheRead: 0
552
+ },
553
+ priceCategory: "low",
554
+ maxInputTokens: 2e5,
555
+ maxOutputTokens: 128e3
556
+ },
557
+ "glm-4.7": {
558
+ detail: "GLM-4.7 200K 上下文,工具调用(3-tier pricing)",
559
+ imageInput: false,
560
+ toolCalling: true,
561
+ thinkingSchema: "reasoningEffort",
562
+ pricingUSD: {
563
+ input: .28,
564
+ output: 1.12,
565
+ cacheRead: .056
566
+ },
567
+ pricingCNY: {
568
+ input: 2,
569
+ output: 8,
570
+ cacheRead: .4
571
+ },
572
+ priceCategory: "medium",
573
+ maxInputTokens: 2e5,
574
+ maxOutputTokens: 128e3
575
+ },
576
+ "glm-5.1": {
577
+ detail: "GLM-5.1200K 上下文,最大输出 128K(2-tier pricing)",
578
+ imageInput: false,
579
+ toolCalling: true,
580
+ thinkingSchema: "reasoningEffort",
581
+ pricingUSD: {
582
+ input: .84,
583
+ output: 3.36,
584
+ cacheRead: .182
585
+ },
586
+ pricingCNY: {
587
+ input: 6,
588
+ output: 24,
589
+ cacheRead: 1.3
590
+ },
591
+ priceCategory: "high",
592
+ maxInputTokens: 2e5,
593
+ maxOutputTokens: 128e3
594
+ },
595
+ "glm-5": {
596
+ detail: "GLM-5 200K 上下文,Agentic 工具调用,最大输出 128K(2-tier pricing)",
597
+ imageInput: false,
598
+ toolCalling: true,
599
+ thinkingSchema: "reasoningEffort",
600
+ pricingUSD: {
601
+ input: .56,
602
+ output: 2.52,
603
+ cacheRead: .14
604
+ },
605
+ pricingCNY: {
606
+ input: 4,
607
+ output: 18,
608
+ cacheRead: 1
609
+ },
610
+ priceCategory: "high",
611
+ maxInputTokens: 2e5,
612
+ maxOutputTokens: 128e3
613
+ },
614
+ "glm-5-turbo": {
615
+ detail: "GLM-5 Turbo — 200K 上下文,最大输出 128K(2-tier pricing)",
616
+ imageInput: false,
617
+ toolCalling: true,
618
+ thinkingSchema: "reasoningEffort",
619
+ pricingUSD: {
620
+ input: .7,
621
+ output: 3.08,
622
+ cacheRead: .168
623
+ },
624
+ pricingCNY: {
625
+ input: 5,
626
+ output: 22,
627
+ cacheRead: 1.2
628
+ },
629
+ priceCategory: "medium",
630
+ maxInputTokens: 2e5,
631
+ maxOutputTokens: 128e3
632
+ },
633
+ "glm-4.7-flashx": {
634
+ detail: "GLM-4.7 FlashX — 快速版",
635
+ imageInput: false,
636
+ toolCalling: false,
637
+ thinkingSchema: "reasoningEffort",
638
+ pricingUSD: {
639
+ input: .07,
640
+ output: .42,
641
+ cacheRead: .014
642
+ },
643
+ pricingCNY: {
644
+ input: .5,
645
+ output: 3,
646
+ cacheRead: .1
647
+ },
648
+ priceCategory: "low",
649
+ maxInputTokens: 2e5,
650
+ maxOutputTokens: 128e3
651
+ },
652
+ "glm-4.6": {
653
+ detail: "GLM-4.6 — 工具调用",
654
+ imageInput: false,
655
+ toolCalling: true,
656
+ thinkingSchema: "reasoningEffort",
657
+ pricingUSD: {
658
+ input: .6,
659
+ output: 2.2,
660
+ cacheRead: .11
661
+ },
662
+ pricingCNY: {
663
+ input: 4.3,
664
+ output: 15.7,
665
+ cacheRead: .79
666
+ },
667
+ priceCategory: "medium",
668
+ maxInputTokens: 2e5,
669
+ maxOutputTokens: 128e3
670
+ },
671
+ "glm-4.5": {
672
+ detail: "GLM-4.5 工具调用(裸名;Zhipu /v1/models 暴露的 legacy alias)",
673
+ imageInput: false,
674
+ toolCalling: true,
675
+ thinkingSchema: "reasoningEffort",
676
+ pricingUSD: {
677
+ input: 0,
678
+ output: 0,
679
+ cacheRead: null
680
+ },
681
+ pricingCNY: {
682
+ input: 0,
683
+ output: 0,
684
+ cacheRead: null
685
+ },
686
+ priceCategory: "low",
687
+ maxInputTokens: 128e3,
688
+ maxOutputTokens: 96e3
689
+ },
690
+ "glm-4.5-air": {
691
+ detail: "GLM-4.5 Air 工具调用(3-tier pricing)",
692
+ imageInput: false,
693
+ toolCalling: true,
694
+ thinkingSchema: "reasoningEffort",
695
+ pricingUSD: {
696
+ input: .112,
697
+ output: .28,
698
+ cacheRead: .0224
699
+ },
700
+ pricingCNY: {
701
+ input: .8,
702
+ output: 2,
703
+ cacheRead: .16
704
+ },
705
+ priceCategory: "low",
706
+ maxInputTokens: 128e3,
707
+ maxOutputTokens: 96e3
708
+ },
709
+ "glm-4.5-airx": {
710
+ detail: "GLM-4.5 AirX 快速版(¥10/M 单档)",
711
+ imageInput: false,
712
+ toolCalling: false,
713
+ thinkingSchema: "reasoningEffort",
714
+ pricingUSD: {
715
+ input: 1.4,
716
+ output: 1.4,
717
+ cacheRead: null
718
+ },
719
+ pricingCNY: {
720
+ input: 10,
721
+ output: 10,
722
+ cacheRead: null
723
+ },
724
+ priceCategory: "low",
725
+ maxInputTokens: 8192,
726
+ maxOutputTokens: 96e3
727
+ },
728
+ "glm-4-long": {
729
+ detail: "GLM-4 Long 1M 上下文,长文档处理",
730
+ imageInput: false,
731
+ toolCalling: false,
732
+ pricingUSD: {
733
+ input: .14,
734
+ output: .14,
735
+ cacheRead: null
736
+ },
737
+ pricingCNY: {
738
+ input: 1,
739
+ output: 1,
740
+ cacheRead: null
741
+ },
742
+ priceCategory: "low",
743
+ maxInputTokens: 1e6,
744
+ maxOutputTokens: 4e3
745
+ },
746
+ "glm-4-flashx": {
747
+ detail: "GLM-4 FlashX 快速版",
748
+ imageInput: false,
749
+ toolCalling: false,
750
+ pricingUSD: {
751
+ input: .014,
752
+ output: .014,
753
+ cacheRead: null
754
+ },
755
+ pricingCNY: {
756
+ input: .1,
757
+ output: .1,
758
+ cacheRead: null
759
+ },
760
+ priceCategory: "low",
761
+ maxInputTokens: 128e3,
762
+ maxOutputTokens: 16e3
763
+ },
764
+ "glm-4-plus": {
765
+ detail: "GLM-4 Plus — Function Call 支持",
766
+ imageInput: false,
767
+ toolCalling: true,
768
+ pricingUSD: {
769
+ input: .7,
770
+ output: .7,
771
+ cacheRead: null
772
+ },
773
+ pricingCNY: {
774
+ input: 5,
775
+ output: 5,
776
+ cacheRead: null
777
+ },
778
+ priceCategory: "low",
779
+ maxInputTokens: 128e3,
780
+ maxOutputTokens: 4e3
781
+ },
782
+ "glm-4.5v": {
783
+ detail: "GLM-4.5V 视觉推理模型 — 图像/视频/文档/GUI(2-tier pricing)",
784
+ imageInput: true,
785
+ toolCalling: true,
786
+ thinkingSchema: "reasoningEffort",
787
+ pricingUSD: {
788
+ input: .28,
789
+ output: .84,
790
+ cacheRead: .056
791
+ },
792
+ pricingCNY: {
793
+ input: 2,
794
+ output: 6,
795
+ cacheRead: .4
796
+ },
797
+ priceCategory: "medium",
798
+ maxInputTokens: 64e3,
799
+ maxOutputTokens: 8192
800
+ },
801
+ "glm-4.6v": {
802
+ detail: "GLM-4.6V 视觉推理模型 — 图像/视频/文档/GUI(2-tier pricing)",
803
+ imageInput: true,
804
+ toolCalling: true,
805
+ thinkingSchema: "reasoningEffort",
806
+ pricingUSD: {
807
+ input: .14,
808
+ output: .42,
809
+ cacheRead: .028
810
+ },
811
+ pricingCNY: {
812
+ input: 1,
813
+ output: 3,
814
+ cacheRead: .2
815
+ },
816
+ priceCategory: "medium",
817
+ maxInputTokens: 128e3,
818
+ maxOutputTokens: 8192
819
+ },
820
+ "glm-4.6v-flash": {
821
+ detail: "GLM-4.6V Flash 完全免费视觉理解模型",
822
+ imageInput: true,
823
+ toolCalling: true,
824
+ thinkingSchema: "reasoningEffort",
825
+ pricingUSD: {
826
+ input: 0,
827
+ output: 0,
828
+ cacheRead: 0
829
+ },
830
+ pricingCNY: {
831
+ input: 0,
832
+ output: 0,
833
+ cacheRead: 0
834
+ },
835
+ priceCategory: "low",
836
+ maxInputTokens: 98304,
837
+ maxOutputTokens: 32768
838
+ },
839
+ "glm-4.6v-flashx": {
840
+ detail: "GLM-4.6V FlashX 快速版视觉理解(2-tier pricing)",
841
+ imageInput: true,
842
+ toolCalling: false,
843
+ thinkingSchema: "reasoningEffort",
844
+ pricingUSD: {
845
+ input: .021,
846
+ output: .21,
847
+ cacheRead: .0042
848
+ },
849
+ pricingCNY: {
850
+ input: .15,
851
+ output: 1.5,
852
+ cacheRead: .03
853
+ },
854
+ priceCategory: "low",
855
+ maxInputTokens: 128e3,
856
+ maxOutputTokens: 8192
857
+ },
858
+ "glm-5v-turbo": {
859
+ detail: "GLM-5V Turbo 多模态 Coding 模型(2-tier pricing)",
860
+ imageInput: true,
861
+ toolCalling: true,
862
+ thinkingSchema: "reasoningEffort",
863
+ pricingUSD: {
864
+ input: .7,
865
+ output: 3.08,
866
+ cacheRead: .168
867
+ },
868
+ pricingCNY: {
869
+ input: 5,
870
+ output: 22,
871
+ cacheRead: 1.2
872
+ },
873
+ priceCategory: "medium",
874
+ maxInputTokens: 2e5,
875
+ maxOutputTokens: 128e3
876
+ },
877
+ "glm-3-turbo": {
878
+ detail: "GLM-3 Turbo — 入门级(¥1/1M tokens)",
879
+ imageInput: false,
880
+ toolCalling: false,
881
+ pricingUSD: {
882
+ input: .14,
883
+ output: .14,
884
+ cacheRead: null
885
+ },
886
+ pricingCNY: {
887
+ input: 1,
888
+ output: 1,
889
+ cacheRead: null
890
+ },
891
+ priceCategory: "low",
892
+ maxInputTokens: 128e3,
893
+ maxOutputTokens: 8192
894
+ },
895
+ "step-3.7-flash": {
896
+ detail: "Step 3.7 Flash 多模态推理模型(198B/11B MoE)",
897
+ imageInput: true,
898
+ toolCalling: true,
899
+ pricingUSD: {
900
+ input: .2,
901
+ output: 1.15,
902
+ cacheRead: .04
903
+ },
904
+ pricingCNY: {
905
+ input: 1.35,
906
+ output: 8.1,
907
+ cacheRead: .27
908
+ },
909
+ priceCategory: "medium",
910
+ thinkingSchema: "reasoningEffort",
911
+ maxInputTokens: 262144,
912
+ maxOutputTokens: 256e3
913
+ },
914
+ "step-3.5-flash": {
915
+ detail: "Step 3.5 Flash 推理模型",
916
+ imageInput: false,
917
+ toolCalling: true,
918
+ pricingUSD: {
919
+ input: .1,
920
+ output: .3,
921
+ cacheRead: .02
922
+ },
923
+ pricingCNY: {
924
+ input: .7,
925
+ output: 2.1,
926
+ cacheRead: .14
927
+ },
928
+ priceCategory: "low",
929
+ thinkingSchema: "reasoningEffort",
930
+ maxInputTokens: 256e3,
931
+ maxOutputTokens: 256e3
932
+ },
933
+ "step-1o-turbo-vision": {
934
+ detail: "Step 1o Turbo Vision — 视觉模型",
935
+ imageInput: true,
936
+ toolCalling: false,
937
+ pricingUSD: {
938
+ input: .357,
939
+ output: 1.143,
940
+ cacheRead: .071
941
+ },
942
+ pricingCNY: {
943
+ input: 2.5,
944
+ output: 8,
945
+ cacheRead: .5
946
+ },
947
+ priceCategory: "low",
948
+ maxInputTokens: 32768,
949
+ maxOutputTokens: 32768
950
+ },
951
+ "openrouter/auto": {
952
+ detail: "OpenRouter Auto 自动路由到平台最佳模型",
953
+ imageInput: true,
954
+ toolCalling: true,
955
+ pricingUSD: {
956
+ input: 0,
957
+ output: 0,
958
+ cacheRead: null
959
+ },
960
+ pricingCNY: {
961
+ input: 0,
962
+ output: 0,
963
+ cacheRead: null
964
+ },
965
+ priceCategory: "medium",
966
+ maxInputTokens: 0,
967
+ maxOutputTokens: 0
968
+ },
969
+ "novita-ai/novita-3.5-flash": {
970
+ detail: "Novita 3.5 Flash Novita 平台热门模型(占位,按实际选择调整)",
971
+ imageInput: true,
972
+ toolCalling: true,
973
+ pricingUSD: {
974
+ input: .1,
975
+ output: .4,
976
+ cacheRead: null
977
+ },
978
+ pricingCNY: {
979
+ input: .1,
980
+ output: .4,
981
+ cacheRead: null
982
+ },
983
+ priceCategory: "low",
984
+ maxInputTokens: 32768,
985
+ maxOutputTokens: 32768
986
+ },
987
+ "MiniMax-M2.5": {
988
+ detail: "MiniMax M2.5 — 229B MoE, SOTA 编程 / Agent / 办公生产力(192K 上下文)",
989
+ imageInput: false,
990
+ toolCalling: true,
991
+ pricingUSD: {
992
+ input: .3,
993
+ output: 1.2,
994
+ cacheRead: .03
995
+ },
996
+ pricingCNY: {
997
+ input: 2.1,
998
+ output: 8.4,
999
+ cacheRead: .21
1000
+ },
1001
+ priceCategory: "medium",
1002
+ maxInputTokens: 192e3,
1003
+ maxOutputTokens: 16384
1004
+ },
1005
+ "Qwen3.6-35B-A3B": {
1006
+ detail: "Qwen3.6-35B-A3B 35B MoE (3B 激活),思考/非思考双模,256K 上下文",
1007
+ imageInput: true,
1008
+ toolCalling: true,
1009
+ pricingUSD: {
1010
+ input: .26,
1011
+ output: 1.54,
1012
+ cacheRead: null
1013
+ },
1014
+ pricingCNY: {
1015
+ input: 1.8,
1016
+ output: 10.8,
1017
+ cacheRead: null
1018
+ },
1019
+ priceCategory: "low",
1020
+ maxInputTokens: 256e3,
1021
+ maxOutputTokens: 32768
1022
+ }
1129
1023
  };
1130
- var NAMESPACE_ALIASES = {
1131
- // SiliconFlow (CNY-billed China-domiciled platform)
1132
- "deepseek-ai/DeepSeek-V4-Pro": "deepseek-v4-pro",
1133
- "deepseek-ai/DeepSeek-V4-Flash": "deepseek-v4-flash",
1134
- "zai-org/GLM-5.2": "glm-5.2",
1135
- "Qwen/Qwen3.6-35B-A3B": "Qwen3.6-35B-A3B",
1136
- "moonshotai/Kimi-K2.7-Code": "kimi-k2.7-code",
1137
- // Novita (USD-billed global aggregator)
1138
- "deepseek/deepseek-v4-pro": "deepseek-v4-pro",
1139
- "deepseek/deepseek-v4-flash": "deepseek-v4-flash",
1140
- "zai/glm-5.2": "glm-5.2",
1141
- "zai/glm-5.1": "glm-5.1",
1142
- "moonshotai/kimi-k3": "kimi-k3",
1143
- // Zhipu naming-history aliases. The 2026-08 "模型一览" page
1144
- // (https://docs.bigmodel.cn/cn/guide/start/model-overview) lists
1145
- // the FlashX variant under its date-stamped id
1146
- // `GLM-4-FlashX-250414`; the bare `glm-4-flashx` is the
1147
- // historical alias that still resolves in chat-completions calls
1148
- // (and is what every existing ProvidersTab user has stored). Map
1149
- // the new id to the same primary entry so both spellings benefit
1150
- // from the curated detail / pricing / capability columns.
1151
- "glm-4-flashx-250414": "glm-4-flashx"
1024
+ /**
1025
+ * Map of namespaced model ids (used by aggregator platforms like
1026
+ * SiliconFlow and Novita) to their primary entry. **Exported** so
1027
+ * downstream code (e.g. the "Pick from preset" dropdown in
1028
+ * ProvidersTab) can identify which entries in `MODEL_METADATA` are
1029
+ * aliases vs primary curated ids — listing aliases would
1030
+ * duplicate primaries in the UI.
1031
+ *
1032
+ * **Read-only by design.** Don't mutate; if you need to add a new
1033
+ * aggregator alias, do it here so the merged `MODEL_METADATA`
1034
+ * auto-tracks the primary.
1035
+ */
1036
+ const NAMESPACE_ALIASES = {
1037
+ "deepseek-ai/DeepSeek-V4-Pro": "deepseek-v4-pro",
1038
+ "deepseek-ai/DeepSeek-V4-Flash": "deepseek-v4-flash",
1039
+ "zai-org/GLM-5.2": "glm-5.2",
1040
+ "Qwen/Qwen3.6-35B-A3B": "Qwen3.6-35B-A3B",
1041
+ "moonshotai/Kimi-K2.7-Code": "kimi-k2.7-code",
1042
+ "deepseek/deepseek-v4-pro": "deepseek-v4-pro",
1043
+ "deepseek/deepseek-v4-flash": "deepseek-v4-flash",
1044
+ "zai/glm-5.2": "glm-5.2",
1045
+ "zai/glm-5.1": "glm-5.1",
1046
+ "moonshotai/kimi-k3": "kimi-k3",
1047
+ "glm-4-flashx-250414": "glm-4-flashx"
1152
1048
  };
1153
- var NAMESPACE_ALIAS_FAMILY = {
1154
- // SiliconFlow
1155
- "deepseek-ai/DeepSeek-V4-Pro": "SiliconFlow (aggregator)",
1156
- "deepseek-ai/DeepSeek-V4-Flash": "SiliconFlow (aggregator)",
1157
- "zai-org/GLM-5.2": "SiliconFlow (aggregator)",
1158
- "Qwen/Qwen3.6-35B-A3B": "SiliconFlow (aggregator)",
1159
- "moonshotai/Kimi-K2.7-Code": "SiliconFlow (aggregator)",
1160
- // Novita
1161
- "deepseek/deepseek-v4-pro": "Novita (aggregator)",
1162
- "deepseek/deepseek-v4-flash": "Novita (aggregator)",
1163
- "zai/glm-5.2": "Novita (aggregator)",
1164
- "zai/glm-5.1": "Novita (aggregator)",
1165
- "moonshotai/kimi-k3": "Novita (aggregator)"
1049
+ const NAMESPACE_ALIAS_FAMILY = {
1050
+ "deepseek-ai/DeepSeek-V4-Pro": "SiliconFlow (aggregator)",
1051
+ "deepseek-ai/DeepSeek-V4-Flash": "SiliconFlow (aggregator)",
1052
+ "zai-org/GLM-5.2": "SiliconFlow (aggregator)",
1053
+ "Qwen/Qwen3.6-35B-A3B": "SiliconFlow (aggregator)",
1054
+ "moonshotai/Kimi-K2.7-Code": "SiliconFlow (aggregator)",
1055
+ "deepseek/deepseek-v4-pro": "Novita (aggregator)",
1056
+ "deepseek/deepseek-v4-flash": "Novita (aggregator)",
1057
+ "zai/glm-5.2": "Novita (aggregator)",
1058
+ "zai/glm-5.1": "Novita (aggregator)",
1059
+ "moonshotai/kimi-k3": "Novita (aggregator)"
1166
1060
  };
1167
- var MODEL_METADATA = (() => {
1168
- const merged = {
1169
- ...PRIMARY_METADATA
1170
- };
1171
- for (const [alias, target] of Object.entries(NAMESPACE_ALIASES)) {
1172
- const targetEntry = PRIMARY_METADATA[target];
1173
- if (targetEntry !== void 0) {
1174
- merged[alias] = targetEntry;
1175
- }
1176
- }
1177
- return Object.freeze(merged);
1061
+ const MODEL_METADATA = (() => {
1062
+ const merged = { ...PRIMARY_METADATA };
1063
+ for (const [alias, target] of Object.entries(NAMESPACE_ALIASES)) {
1064
+ const targetEntry = PRIMARY_METADATA[target];
1065
+ if (targetEntry !== void 0) merged[alias] = targetEntry;
1066
+ }
1067
+ return Object.freeze(merged);
1178
1068
  })();
1069
+ /**
1070
+ * Look up curated metadata for a model by its un-qualified id
1071
+ * (the part after the last `::` in a qualified id, or the raw id
1072
+ * for a non-namespaced provider). Returns `undefined` for
1073
+ * user-added / `-compatible` models that have no curated entry;
1074
+ * callers should then fall back to whatever the user typed into
1075
+ * the ProvidersTab.
1076
+ */
1179
1077
  function lookupModelMetadata(modelId) {
1180
- return MODEL_METADATA[modelId];
1078
+ return MODEL_METADATA[modelId];
1181
1079
  }
1080
+ /**
1081
+ * Resolve the currency for a baseUrl. Strict hostname match —
1082
+ * `api.minimaxi.com` and `api.minimaxi.cn` map to CNY (the China
1083
+ * platform), `api.minimax.io` maps to USD (the global platform),
1084
+ * everything else falls back to USD (the global default). The
1085
+ * match is exact-host so a typo in the hostname never silently
1086
+ * flips currency.
1087
+ *
1088
+ * `api.deepseek.com` maps to CNY (2026-08-20 product decision —
1089
+ * user request). History: the 8/18 draft put it in CNY by mistake
1090
+ * (per the $ block on the English pricing page), 8/19 moved it to
1091
+ * the USD catch-all, and 8/20 moved it BACK to CNY deliberately —
1092
+ * our user base bills in ¥ on this endpoint (the official zh-cn
1093
+ * page publishes the ¥ table for it), and the picker should show
1094
+ * the price those users actually pay. The DeepSeek entries pin the
1095
+ * PEAK tier (conservative worst-case; off-peak is exactly half).
1096
+ */
1182
1097
  function currencyForBaseUrl(baseUrl) {
1183
- try {
1184
- const hostname = new URL(baseUrl).hostname.toLowerCase();
1185
- if (hostname === "api.minimaxi.com" || hostname === "api.minimaxi.cn" || hostname === "api.moonshot.cn" || hostname === "open.bigmodel.cn" || // Zhipu legacy v3 host. Per the GLM-for-copilot reference
1186
- // (`docs/references/GLM-for-copilot-main/src/endpoint.ts:4`)
1187
- // this was retired to `bigmodel.cn` but is still
1188
- // resolvable for accounts that haven't migrated — we
1189
- // don't surface it in the baseUrl dropdown, but a user
1190
- // may paste it from a saved settings.json, so the
1191
- // currency has to match (CNY, same as the new host).
1192
- hostname === "dev.bigmodel.cn" || // DeepSeek — 2026-08-20 product decision (user request):
1193
- // surface the domestic ¥ prices. The `.com` endpoint
1194
- // serves both regions, but our user base bills in CNY on
1195
- // it (the official zh-cn pricing page publishes the ¥
1196
- // table for this endpoint), so the picker now shows the
1197
- // CNY block. This REVERSES the 2026-08-19 mapping that
1198
- // left it in the USD catch-all ("$ block corresponds to
1199
- // the .com endpoint") — with a CNY-billed user base, the
1200
- // ¥ block is the relevant one. The pricingCNY block in
1201
- // MODEL_METADATA pins the PEAK tier for these entries
1202
- // (conservative worst-case; see the deepseek-v4-flash
1203
- // comment).
1204
- hostname === "api.deepseek.com" || // SenseNova (SenseTime) token-plan gateway — CN-domiciled,
1205
- // quota-billed (all-zero published pricing, so the currency is
1206
- // cosmetic today, but pinning it keeps a future per-token ¥ table
1207
- // from silently rendering as USD).
1208
- hostname === "token.sensenova.cn") {
1209
- return "CNY";
1210
- }
1211
- if (hostname === "api.minimax.io" || hostname === "api.minimaxi.io" || hostname === "api.siliconflow.cn" || hostname === "api.siliconflow.com" || hostname === "api.stepfun.com" || hostname === "openrouter.ai" || hostname === "api.novita.ai" || // Z.ai / Zhipu international. Billed in USD per the
1212
- // official `bigmodel.cn/pricing` page (the CNY-billed
1213
- // list is the China-domiciled `open.bigmodel.cn` only;
1214
- // the international `api.z.ai` is USD regardless of
1215
- // which apiMode / protocol path the user picked). The
1216
- // GLM-for-copilot reference uses the same split
1217
- // (`docs/references/GLM-for-copilot-main/src/endpoint.ts:160-173`).
1218
- // Without this explicit entry, `api.z.ai` would still
1219
- // resolve to USD via the catch-all below — adding it
1220
- // here makes the intent grep-able and pins the host
1221
- // list against accidental removal.
1222
- hostname === "api.z.ai") {
1223
- return "USD";
1224
- }
1225
- } catch {
1226
- }
1227
- return "USD";
1098
+ try {
1099
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
1100
+ if (hostname === "api.minimaxi.com" || hostname === "api.minimaxi.cn" || hostname === "api.moonshot.cn" || hostname === "open.bigmodel.cn" || hostname === "dev.bigmodel.cn" || hostname === "api.deepseek.com" || hostname === "token.sensenova.cn") return "CNY";
1101
+ if (hostname === "api.minimax.io" || hostname === "api.minimaxi.io" || hostname === "api.siliconflow.cn" || hostname === "api.siliconflow.com" || hostname === "api.stepfun.com" || hostname === "openrouter.ai" || hostname === "api.novita.ai" || hostname === "api.z.ai") return "USD";
1102
+ } catch {}
1103
+ return "USD";
1228
1104
  }
1229
-
1230
- // src/ai/providers.presets.ts
1105
+ //#endregion
1106
+ //#region src/ai/providers.presets.ts
1107
+ /**
1108
+ * Default configuration snippets shipped with named vendor types.
1109
+ *
1110
+ * When the user picks `minimax` / `deepseek` / `agnes` in the
1111
+ * ProvidersTab, the form pre-fills `displayName` + `baseUrl` + a
1112
+ * starter `models` list from this table. The user still has to
1113
+ * paste their own `apiKey` (always empty by default — secrets
1114
+ * never ship with the extension).
1115
+ *
1116
+ * Keep `baseUrl` here in sync with the upstream vendor docs.
1117
+ *
1118
+ * Adapter routing (see `apps/extension/src/services/providers/adapters/index.ts`):
1119
+ * - minimax: Anthropic-compatible at `https://api.minimaxi.com/anthropic`
1120
+ * → routed to AnthropicAdapter
1121
+ * - deepseek: OpenAI-compatible at `https://api.deepseek.com/v1`
1122
+ * → routed to OpenAIAdapter (the /anthropic surface v3 exposed
1123
+ * is no longer documented for v4)
1124
+ * - agnes: OpenAI-compatible at `https://apihub.agnes-ai.com/v1`
1125
+ * → routed to OpenAIAdapter
1126
+ * - medalsoft: OpenAI-compatible internal gateway at
1127
+ * `https://nexus.servicemecloud.com/v1` → routed to OpenAIAdapter
1128
+ * (empty starter model list — populate via "Fetch from API")
1129
+ *
1130
+ * Earlier iterations of the minimax default 401'd on the team; do
1131
+ * NOT swap minimax back to one of these without checking with the
1132
+ * user first:
1133
+ * - `https://agent.minimaxi.com/mavis/api/v1/llm/v1` (opencode.json
1134
+ * proxy URL — AnthropicAdapter would double-prefix /v1 to it)
1135
+ * - `https://agent.minimaxi.com/v1` (OpenAI-compat
1136
+ * variant — user tried this in commit 8bd867c then asked to
1137
+ * revert in 71faf6e-era because it 401'd as well)
1138
+ */
1139
+ /**
1140
+ * Build a `ProviderModel` preset entry by joining the static
1141
+ * context-window numbers (token caps are user-visible and depend
1142
+ * on the wire-protocol spec, not the curated metadata) with the
1143
+ * curated metadata in `MODEL_METADATA` (detail / capabilities /
1144
+ * pricing / thinking). The user can override any field in the
1145
+ * ProvidersTab form; the curated values are just the starter
1146
+ * defaults so the picker shows the cost column + thinking
1147
+ * dropdown out of the box for the named vendors.
1148
+ *
1149
+ * Pricing block is selected to match `baseUrl`'s currency (via
1150
+ * `currencyForBaseUrl`): CNY for `open.bigmodel.cn` /
1151
+ * `api.moonshot.cn` / etc., USD for `api.z.ai` / `openrouter.ai` /
1152
+ * etc. Falls back to USD for unrecognised hosts. The caller is
1153
+ * expected to pass a real `baseUrl` — when adding a brand-new
1154
+ * provider the host is already known (came from the
1155
+ * `PROVIDER_BASE_URL_PRESETS` dropdown or user-typed); the empty
1156
+ * `""` default picks USD as a safe fallback.
1157
+ */
1231
1158
  function buildPresetModel(id, displayName, baseUrl) {
1232
- const meta = MODEL_METADATA[id];
1233
- if (!meta) {
1234
- throw new Error(
1235
- `buildPresetModel: no curated MODEL_METADATA entry for '${id}' \u2014 add one before referencing it from BUILTIN_PROVIDER_PRESETS.`
1236
- );
1237
- }
1238
- const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
1239
- return {
1240
- id,
1241
- displayName,
1242
- maxInputTokens: meta.maxInputTokens,
1243
- maxOutputTokens: meta.maxOutputTokens,
1244
- detail: meta.detail,
1245
- capabilities: {
1246
- supportsImageToText: meta.imageInput,
1247
- supportsToolCalling: meta.toolCalling
1248
- },
1249
- pricing: meta[`pricing${currency}`],
1250
- priceCategory: meta.priceCategory,
1251
- thinkingSchema: meta.thinkingSchema ?? "none"
1252
- };
1159
+ const meta = MODEL_METADATA[id];
1160
+ if (!meta) throw new Error(`buildPresetModel: no curated MODEL_METADATA entry for '${id}' — add one before referencing it from BUILTIN_PROVIDER_PRESETS.`);
1161
+ const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
1162
+ return {
1163
+ id,
1164
+ displayName,
1165
+ maxInputTokens: meta.maxInputTokens,
1166
+ maxOutputTokens: meta.maxOutputTokens,
1167
+ detail: meta.detail,
1168
+ capabilities: {
1169
+ supportsImageToText: meta.imageInput,
1170
+ supportsToolCalling: meta.toolCalling
1171
+ },
1172
+ pricing: meta[`pricing${currency}`],
1173
+ priceCategory: meta.priceCategory,
1174
+ thinkingSchema: meta.thinkingSchema ?? "none"
1175
+ };
1253
1176
  }
1177
+ /**
1178
+ * Union a model row returned by the "Fetch from API" flow with its
1179
+ * curated preset metadata, when the id matches an entry in
1180
+ * `MODEL_METADATA`. The API typically returns just `id` (sometimes
1181
+ * `displayName`); the user expects the picker's `detail` / `pricing`
1182
+ * / `capabilities` / `maxInputTokens` columns to be filled in for
1183
+ * any model we curate, not blank.
1184
+ *
1185
+ * Precedence (TDD-pinned 2026-08-19, see
1186
+ * `test/unionProviderModelWithPreset.test.mjs`):
1187
+ *
1188
+ * • **Preset wins** for the curated fields: `detail`,
1189
+ * `capabilities`, `pricing`, `priceCategory`, `thinkingSchema`,
1190
+ * `maxInputTokens`, `maxOutputTokens`. These are the values we
1191
+ * maintain by hand and trust more than what the API publishes,
1192
+ * which is often missing or stale (e.g. the OpenAI `/v1/models`
1193
+ * endpoint does not return pricing or token caps; DeepSeek's
1194
+ * `GET /models` likewise returns id + owned_by only).
1195
+ * • **Fetched wins** for `id` (the API is the source of truth for
1196
+ * what the endpoint actually exposes — a stale preset could list
1197
+ * a model the user no longer has access to).
1198
+ * • **`displayName`** — the 2026-08-19 follow-up: fetched wins
1199
+ * when set, the preset's curated `displayName` (from
1200
+ * `BUILTIN_PROVIDER_PRESETS`, see
1201
+ * `getPresetModelDisplayName`) fills in when fetched omits it
1202
+ * or sends an empty string. Without this fallback, the picker
1203
+ * would render the bare id (`deepseek-v4-flash`) as the model
1204
+ * name right after the fetch result lands — the user picked
1205
+ * "Fetch from API" because they wanted the live catalog, but
1206
+ * the friendly label they would have seen if they'd picked
1207
+ * the vendor from the named-vendor `<select>` should also
1208
+ * surface here. Empty string is treated the same as missing
1209
+ * (a blank label in the picker is strictly worse than the
1210
+ * curated friendly name).
1211
+ * • **No preset match** → returns the fetched model unchanged
1212
+ * (custom / aggregator-only models stay bare; the user fills
1213
+ * in detail / pricing by hand).
1214
+ *
1215
+ * `baseUrl` is consulted to pick the right currency for `pricing`
1216
+ * (CNY for `open.bigmodel.cn` / `api.moonshot.cn` / etc., USD
1217
+ * otherwise — see `currencyForBaseUrl`). Pass the form's current
1218
+ * `editing.baseUrl`; that is the same baseUrl the row was just
1219
+ * fetched from, so the resolved currency matches what the user
1220
+ * will see in the picker. Empty / unset baseUrl → USD fallback.
1221
+ *
1222
+ * Namespaced ids (e.g. `deepseek-ai/DeepSeek-V4-Pro` on
1223
+ * SiliconFlow) resolve transparently — `MODEL_METADATA` aliases
1224
+ * the primary entry's metadata reference under every namespace key,
1225
+ * so `lookupModelMetadata("deepseek-ai/DeepSeek-V4-Pro")` returns
1226
+ * the same object as `lookupModelMetadata("deepseek-v4-pro")`.
1227
+ */
1254
1228
  function unionProviderModelWithPreset(fetched, baseUrl) {
1255
- const meta = MODEL_METADATA[fetched.id];
1256
- if (!meta) {
1257
- return fetched;
1258
- }
1259
- const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
1260
- const presetDisplayName = getPresetModelDisplayName(fetched.id);
1261
- const displayName = typeof fetched.displayName === "string" && fetched.displayName.length > 0 ? fetched.displayName : presetDisplayName;
1262
- return {
1263
- // Spread fetched first so `id` wins, then overlay the
1264
- // curated fields. (Curated fields ALWAYS win over fetched
1265
- // per the precedence above — that's why they're explicit,
1266
- // not relying on spread order.)
1267
- ...fetched,
1268
- displayName,
1269
- detail: meta.detail,
1270
- capabilities: {
1271
- supportsImageToText: meta.imageInput,
1272
- supportsToolCalling: meta.toolCalling
1273
- },
1274
- pricing: meta[`pricing${currency}`],
1275
- priceCategory: meta.priceCategory,
1276
- // thinkingSchema: prefer preset (curated), fall back to
1277
- // fetched (in case the API publishes a schema the
1278
- // preset doesn't know about), then "none" to match
1279
- // `buildPresetModel`'s explicit-none convention so the
1280
- // picker renders identically for preset vs fetched rows.
1281
- thinkingSchema: meta.thinkingSchema ?? fetched.thinkingSchema ?? "none",
1282
- // Token caps: preset is curated; if the preset doesn't
1283
- // publish a cap, keep whatever fetched supplied (the
1284
- // user may have entered it by hand earlier).
1285
- maxInputTokens: meta.maxInputTokens ?? fetched.maxInputTokens,
1286
- maxOutputTokens: meta.maxOutputTokens ?? fetched.maxOutputTokens
1287
- };
1229
+ const meta = MODEL_METADATA[fetched.id];
1230
+ if (!meta) return fetched;
1231
+ const currency = baseUrl ? currencyForBaseUrl(baseUrl) : "USD";
1232
+ const presetDisplayName = getPresetModelDisplayName(fetched.id);
1233
+ const displayName = typeof fetched.displayName === "string" && fetched.displayName.length > 0 ? fetched.displayName : presetDisplayName;
1234
+ return {
1235
+ ...fetched,
1236
+ displayName,
1237
+ detail: meta.detail,
1238
+ capabilities: {
1239
+ supportsImageToText: meta.imageInput,
1240
+ supportsToolCalling: meta.toolCalling
1241
+ },
1242
+ pricing: meta[`pricing${currency}`],
1243
+ priceCategory: meta.priceCategory,
1244
+ thinkingSchema: meta.thinkingSchema ?? fetched.thinkingSchema ?? "none",
1245
+ maxInputTokens: meta.maxInputTokens ?? fetched.maxInputTokens,
1246
+ maxOutputTokens: meta.maxOutputTokens ?? fetched.maxOutputTokens
1247
+ };
1288
1248
  }
1289
- var PRESET_MODEL_FAMILIES = [
1290
- "GLM",
1291
- "DeepSeek",
1292
- "Kimi",
1293
- "StepFun",
1294
- "MiniMax",
1295
- "Agnes",
1296
- "SenseNova",
1297
- "Qwen",
1298
- "SiliconFlow (aggregator)",
1299
- "Novita (aggregator)",
1300
- "OpenRouter (aggregator)"
1249
+ /**
1250
+ * The list of vendor family groups shown in the preset dropdown.
1251
+ * Order is intentional (most common presets first):
1252
+ * 1. GLM (Zhipu / 智谱) — the user explicitly asked us to
1253
+ * support 4-channel endpoints; GLM has the richest preset
1254
+ * list (15+ models) so it gets the top slot.
1255
+ * 2. DeepSeek / Kimi / StepFun / MiniMax — the other named
1256
+ * vendors with curated presets.
1257
+ * 3. Agnes / Qwen — the smaller curated lists.
1258
+ * 4. Aggregators (SiliconFlow / Novita / OpenRouter) — listed
1259
+ * last because users on aggregators usually type the namespaced
1260
+ * id by hand rather than reach for a curated dropdown.
1261
+ */
1262
+ const PRESET_MODEL_FAMILIES = [
1263
+ "GLM",
1264
+ "DeepSeek",
1265
+ "Kimi",
1266
+ "StepFun",
1267
+ "MiniMax",
1268
+ "Agnes",
1269
+ "SenseNova",
1270
+ "Qwen",
1271
+ "SiliconFlow (aggregator)",
1272
+ "Novita (aggregator)",
1273
+ "OpenRouter (aggregator)"
1301
1274
  ];
1275
+ /**
1276
+ * Map a `MODEL_METADATA` id to its vendor family for dropdown
1277
+ * grouping. Pure data — keeps the dropdown order in one place.
1278
+ *
1279
+ * Namespaced alias ids (the SiliconFlow `deepseek-ai/…` /
1280
+ * `zai-org/…` / `Qwen/Qwen*` / `moonshotai/…` cluster, the Novita
1281
+ * `zai/…` / `deepseek/…` cluster, and the historical
1282
+ * `moonshotai/kimi-k3`) consult `NAMESPACE_ALIAS_FAMILY` first so
1283
+ * they land in the matching aggregator group instead of being
1284
+ * filtered out as aliases. The primary entries fall through to the
1285
+ * prefix-based rules below.
1286
+ */
1302
1287
  function vendorFamilyForId(id) {
1303
- const aliasFamily = NAMESPACE_ALIAS_FAMILY[id];
1304
- if (aliasFamily) return aliasFamily;
1305
- if (id.startsWith("glm-")) return "GLM";
1306
- if (id.startsWith("deepseek-")) return "DeepSeek";
1307
- if (id.startsWith("kimi-")) return "Kimi";
1308
- if (id.startsWith("step-")) return "StepFun";
1309
- if (id.startsWith("MiniMax-")) return "MiniMax";
1310
- if (id.startsWith("agnes-")) return "Agnes";
1311
- if (id.startsWith("sensenova-")) return "SenseNova";
1312
- if (id.startsWith("Qwen")) return "Qwen";
1313
- if (id.startsWith("openrouter/")) return "OpenRouter (aggregator)";
1314
- return "Other";
1288
+ const aliasFamily = NAMESPACE_ALIAS_FAMILY[id];
1289
+ if (aliasFamily) return aliasFamily;
1290
+ if (id.startsWith("glm-")) return "GLM";
1291
+ if (id.startsWith("deepseek-")) return "DeepSeek";
1292
+ if (id.startsWith("kimi-")) return "Kimi";
1293
+ if (id.startsWith("step-")) return "StepFun";
1294
+ if (id.startsWith("MiniMax-")) return "MiniMax";
1295
+ if (id.startsWith("agnes-")) return "Agnes";
1296
+ if (id.startsWith("sensenova-")) return "SenseNova";
1297
+ if (id.startsWith("Qwen")) return "Qwen";
1298
+ if (id.startsWith("openrouter/")) return "OpenRouter (aggregator)";
1299
+ return "Other";
1315
1300
  }
1316
- var LISTABLE_PRESET_MODELS = (() => {
1317
- const aggregatorAliasKeys = new Set(Object.keys(NAMESPACE_ALIAS_FAMILY));
1318
- const allIds = Object.keys(MODEL_METADATA).filter((id) => {
1319
- if (NAMESPACE_ALIASES[id] === void 0) return true;
1320
- return aggregatorAliasKeys.has(id);
1321
- }).sort();
1322
- return allIds.map((id) => ({
1323
- id,
1324
- displayName: id,
1325
- vendorFamily: vendorFamilyForId(id)
1326
- }));
1301
+ /**
1302
+ * The "Pick from preset" dropdown options — every curated primary
1303
+ * entry in `MODEL_METADATA` PLUS every alias tagged with an
1304
+ * aggregator family in `NAMESPACE_ALIAS_FAMILY`. Grouped by
1305
+ * vendor family. Sorted alphabetically within each family so the
1306
+ * dropdown order is stable across runs.
1307
+ *
1308
+ * Two alias flavours exist in `NAMESPACE_ALIASES`:
1309
+ * - **Aggregator aliases** (SiliconFlow `deepseek-ai/…`,
1310
+ * Novita `zai/…` / `deepseek/…`, etc.) — KEPT in the list so
1311
+ * the dropdown surfaces the namespaced ids aggregator users
1312
+ * actually need to type. They are routed to the matching
1313
+ * aggregator <optgroup> via `NAMESPACE_ALIAS_FAMILY`.
1314
+ * - **Historical / naming aliases** (e.g. the Zhipu
1315
+ * `glm-4-flashx-250414` rebrand of `glm-4-flashx`) — DROPPED
1316
+ * because they're duplicates of an existing primary entry
1317
+ * that already appears in the dropdown. Users with the
1318
+ * historical id already in their settings.json keep
1319
+ * working at the chat-registration layer
1320
+ * (see `MODEL_METADATA`'s alias merge) — the dropdown just
1321
+ * doesn't surface a redundant second option.
1322
+ *
1323
+ * The filter rule is the inverse of the aggregator tag presence:
1324
+ * any alias with a `NAMESPACE_ALIAS_FAMILY` entry is kept, every
1325
+ * other alias is filtered. The pinning test
1326
+ * `test/listPresetModelGroups.test.mjs` asserts this 1:1 mapping
1327
+ * between the two structures.
1328
+ *
1329
+ * Used by `ProvidersTab.tsx` to render the `<select>` next to the
1330
+ * "+ Add model" button. Selecting an option calls
1331
+ * `buildPresetModel(id, displayName, baseUrl)` and appends the
1332
+ * resulting `ProviderModel` to the editing list. The `displayName`
1333
+ * mirrors the model id verbatim (the canonical form is what users
1334
+ * see in /v1/models, what VSCode's chat picker surfaces, and what
1335
+ * the existing `BUILTIN_PROVIDER_PRESETS` pre-fills); the user can
1336
+ * still rename the field after picking — this is just a starter
1337
+ * label.
1338
+ */
1339
+ const LISTABLE_PRESET_MODELS = (() => {
1340
+ const aggregatorAliasKeys = new Set(Object.keys(NAMESPACE_ALIAS_FAMILY));
1341
+ return Object.keys(MODEL_METADATA).filter((id) => {
1342
+ if (NAMESPACE_ALIASES[id] === void 0) return true;
1343
+ return aggregatorAliasKeys.has(id);
1344
+ }).sort().map((id) => ({
1345
+ id,
1346
+ displayName: id,
1347
+ vendorFamily: vendorFamilyForId(id)
1348
+ }));
1327
1349
  })();
1328
1350
  function listPresetModelGroups() {
1329
- const byFamily = /* @__PURE__ */ new Map();
1330
- for (const entry of LISTABLE_PRESET_MODELS) {
1331
- const bucket = byFamily.get(entry.vendorFamily) ?? [];
1332
- bucket.push(entry);
1333
- byFamily.set(entry.vendorFamily, bucket);
1334
- }
1335
- return PRESET_MODEL_FAMILIES.filter((f) => byFamily.has(f)).map((family) => ({
1336
- family,
1337
- entries: byFamily.get(family) ?? []
1338
- }));
1351
+ const byFamily = /* @__PURE__ */ new Map();
1352
+ for (const entry of LISTABLE_PRESET_MODELS) {
1353
+ const bucket = byFamily.get(entry.vendorFamily) ?? [];
1354
+ bucket.push(entry);
1355
+ byFamily.set(entry.vendorFamily, bucket);
1356
+ }
1357
+ return PRESET_MODEL_FAMILIES.filter((f) => byFamily.has(f)).map((family) => ({
1358
+ family,
1359
+ entries: byFamily.get(family) ?? []
1360
+ }));
1339
1361
  }
1340
- var BUILTIN_PROVIDER_PRESETS = {
1341
- minimax: {
1342
- displayName: "MiniMax",
1343
- baseUrl: "https://api.minimaxi.com/anthropic",
1344
- // Token limits (see the `MODEL_METADATA` entries above) are
1345
- // aligned with the upstream `minimax-vscode-main` reference
1346
- // (`docs/references/minimax-vscode-main/src/models/registry.ts`),
1347
- // which scrapes the official model cards:
1348
- // - M3: official spec is 1M context, but the >512K input tier
1349
- // is still limited-availability (needs sales access + 2x
1350
- // billing per the pricing-page footnote) and most accounts
1351
- // get a 400 past 512K. We report the safe 512K effective cap
1352
- // for both input and output so VSCode's "上下文窗口" indicator
1353
- // matches what a normal account can actually push.
1354
- // - M2.7 / M2.7-highspeed: official spec is 204,800 total
1355
- // context, split 131,072 input / 73,728 output (no separate
1356
- // docs split exists beyond this; do not invent a different
1357
- // one).
1358
- // `maxInputTokens` / `maxOutputTokens` are forwarded to
1359
- // `vscode.lm.registerLanguageModelChatProvider` — VSCode's chat
1360
- // model picker hides / disables entries whose `maxInputTokens`
1361
- // is 0, so every curated entry must carry an explicit number
1362
- // (otherwise the user's added model never shows up in the
1363
- // Copilot Chat picker).
1364
- models: [
1365
- buildPresetModel("MiniMax-M3", "MiniMax-M3", "https://api.minimaxi.com/anthropic"),
1366
- buildPresetModel("MiniMax-M2.7", "MiniMax-M2.7", "https://api.minimaxi.com/anthropic"),
1367
- buildPresetModel(
1368
- "MiniMax-M2.7-highspeed",
1369
- "MiniMax-M2.7-highspeed",
1370
- "https://api.minimaxi.com/anthropic"
1371
- ),
1372
- // M2.5 (2026-02-13, 80.2% SWE-Bench Verified) — the
1373
- // predecessor of M2.7. Still in the catalog and often
1374
- // available on MiniMax's promotional $0.30/$1.20 rate, so
1375
- // keep it as a preset for users on the M2.5 plan tier.
1376
- buildPresetModel("MiniMax-M2.5", "MiniMax-M2.5", "https://api.minimaxi.com/anthropic")
1377
- ]
1378
- },
1379
- deepseek: {
1380
- displayName: "DeepSeek",
1381
- // DeepSeek v4 ships only the OpenAI-compatible endpoint (no
1382
- // Anthropic-compatible surface). The /v1 suffix is intentional
1383
- // it matches the public docs (https://api.deepseek.com/v1)
1384
- // and is the path the OpenAI client uses to build
1385
- // `/v1/chat/completions`. The reference implementation
1386
- // (`docs/references/deepseek-v4-for-copilot-main`) uses
1387
- // `https://api.deepseek.com` and appends `/chat/completions`
1388
- // (no /v1), which is technically a different host layout — we
1389
- // prefer the explicit /v1 here so the user can verify the path
1390
- // in their browser without ambiguity.
1391
- baseUrl: "https://api.deepseek.com/v1",
1392
- // V4 context window: 655,360 input / 393,216 output per
1393
- // `docs/references/deepseek-v4-for-copilot-main/src/consts.ts`.
1394
- // V3 (deepseek-chat / deepseek-reasoner, 64K) is no longer
1395
- // the recommended default — users still on V3 can override
1396
- // these defaults in the ProvidersTab form.
1397
- models: [
1398
- buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash", "https://api.deepseek.com/v1"),
1399
- buildPresetModel("deepseek-v4-pro", "DeepSeek V4 Pro", "https://api.deepseek.com/v1")
1400
- ]
1401
- },
1402
- agnes: {
1403
- displayName: "Agnes",
1404
- // Sapiens AI's Agnes — OpenAI-compatible endpoint per
1405
- // https://wiki.agnes-ai.com (`POST /v1/chat/completions`).
1406
- baseUrl: "https://apihub.agnes-ai.com/v1",
1407
- // Order mirrors the vendor docs index (wiki.agnes-ai.com,
1408
- // fetched 2026-08-19): paid reasoning models first — the
1409
- // commercial stable `agnes-2.5-pro` leads, then the
1410
- // benchmark-listed alpha — followed by the flash tier
1411
- // newest-first.
1412
- models: [
1413
- buildPresetModel("agnes-2.5-pro", "Agnes 2.5 Pro", "https://apihub.agnes-ai.com/v1"),
1414
- buildPresetModel(
1415
- "agnes-2.5-pro-alpha",
1416
- "Agnes 2.5 Pro Alpha",
1417
- "https://apihub.agnes-ai.com/v1"
1418
- ),
1419
- buildPresetModel("agnes-2.5-flash", "Agnes 2.5 Flash", "https://apihub.agnes-ai.com/v1"),
1420
- buildPresetModel("agnes-2.0-flash", "Agnes 2.0 Flash", "https://apihub.agnes-ai.com/v1")
1421
- ]
1422
- },
1423
- kimi: {
1424
- displayName: "Kimi",
1425
- baseUrl: "https://api.moonshot.cn/v1",
1426
- models: [
1427
- buildPresetModel("kimi-k3", "Kimi K3", "https://api.moonshot.cn/v1"),
1428
- buildPresetModel("kimi-k2.7-code", "Kimi K2.7 Code", "https://api.moonshot.cn/v1"),
1429
- buildPresetModel(
1430
- "kimi-k2.7-code-highspeed",
1431
- "Kimi K2.7 Code HighSpeed",
1432
- "https://api.moonshot.cn/v1"
1433
- ),
1434
- buildPresetModel("kimi-k2.6", "Kimi K2.6", "https://api.moonshot.cn/v1")
1435
- ]
1436
- },
1437
- zhipu: {
1438
- displayName: "Zhipu",
1439
- baseUrl: "https://open.bigmodel.cn/api/paas/v4",
1440
- // The preset is the **intersection** of (a) the curated
1441
- // `MODEL_METADATA` table above and (b) what Zhipu's
1442
- // `/v1/models` endpoint actually returns as of 2026-08-18
1443
- // (verified by the user's "Fetch from API" pull in
1444
- // ProvidersTab — see screenshot in the 2026-08-18 review).
1445
- // The two sources are kept in sync deliberately: a curated
1446
- // entry without a `/v1/models` listing is dead weight in the
1447
- // starter list (the user can still add it by hand), and a
1448
- // `/v1/models` listing without a curated entry breaks
1449
- // `buildPresetModel`'s fail-loudly contract.
1450
- //
1451
- // 2026-08-18 trim — the following 8 entries were removed
1452
- // because they no longer show up in Zhipu's `/v1/models`
1453
- // response (they were either retired, never exposed via
1454
- // chat-completions, or only reachable on private/coding-plan
1455
- // endpoints that the public `/v1/models` doesn't advertise):
1456
- //
1457
- // glm-5.1-highspeed — production 400-TPS variant of
1458
- // GLM-5.1 served via TileRT; still
1459
- // listed in some third-party mirrors
1460
- // (Alibaba Cloud DashScope) but not
1461
- // on Zhipu's own /v1/models.
1462
- // glm-5v-turbo — multimodal coding base; only
1463
- // reachable via the dedicated
1464
- // multimodal endpoint, not
1465
- // /v1/chat/completions.
1466
- // glm-4.7-flash — free-tier 4.7 lite; advertised on
1467
- // the docs pricing page but absent
1468
- // from /v1/models.
1469
- // glm-4.7-flashx — quick-response 4.7; same situation
1470
- // as glm-4.7-flash.
1471
- // glm-4.5v — multimodal 4.5; only on the
1472
- // dedicated VLM endpoint.
1473
- // glm-4.5-airx — quick-response 4.5 Air; not in
1474
- // /v1/models anymore.
1475
- // glm-4-long — 1M-context 4-Long; the `/long`
1476
- // path was retired in 2026 H1.
1477
- // glm-4-flashx — quick-response 4 FlashX; the
1478
- // `-250414` dated alias (see
1479
- // `NAMESPACE_ALIASES` in
1480
- // `providers.metadata.ts`) is the
1481
- // only spelling still exposed.
1482
- //
1483
- // Note: `glm-4.5` (no suffix) IS in the preset now. It is
1484
- // NOT listed on the public "模型概览" page but it IS
1485
- // returned by /v1/models — almost certainly a legacy alias
1486
- // that routes to one of the suffixed 4.5 variants. The
1487
- // curated metadata entry marks it as such; users on a
1488
- // private coding-plan endpoint that distinguishes `glm-4.5`
1489
- // from `glm-4.5-air` should override the model id in the
1490
- // ProvidersTab.
1491
- //
1492
- // Earlier (also 2026-08-18) trim — `glm-4-plus` and
1493
- // `glm-3-turbo` were removed from the preset on the same
1494
- // date. Both are no longer listed in Zhipu's public
1495
- // "模型一览": `GLM-4-0520` is in the "即将弃用模型" list
1496
- // and `GLM-3-Turbo` has been retired without a formal
1497
- // redirect. Their API endpoints may still respond for
1498
- // legacy accounts (the `glm-4-plus` 429 "余额不足" log we
1499
- // saw on 2026-08-18 is one such case), but they shouldn't
1500
- // be the default starter pick for a freshly added Zhipu
1501
- // provider. Users with a paid legacy plan that still works
1502
- // can add the id back by hand in the ProvidersTab; the
1503
- // `MODEL_METADATA` entries are kept so the id is still
1504
- // resolvable for the curated detail / pricing columns.
1505
- models: [
1506
- buildPresetModel("glm-5.3", "GLM-5.3", "https://open.bigmodel.cn/api/paas/v4"),
1507
- // GLM-5.3-Flash (2026-08-26) — GLM-5 系列首个原生多模态
1508
- // 模型,性价比高(¥0.4/¥1.4 per 1M tokens),接入 GLM Coding Plan
1509
- // 后额度较 GLM-5.3 翻 3 倍。放在 5.3 后面,符合
1510
- // 旗舰/快速版成对的展示惯例。
1511
- buildPresetModel("glm-5.3-flash", "GLM-5.3 Flash", "https://open.bigmodel.cn/api/paas/v4"),
1512
- buildPresetModel("glm-5.2", "GLM-5.2", "https://open.bigmodel.cn/api/paas/v4"),
1513
- buildPresetModel("glm-5.1", "GLM-5.1", "https://open.bigmodel.cn/api/paas/v4"),
1514
- buildPresetModel("glm-5", "GLM-5", "https://open.bigmodel.cn/api/paas/v4"),
1515
- buildPresetModel("glm-5-turbo", "GLM-5 Turbo", "https://open.bigmodel.cn/api/paas/v4"),
1516
- buildPresetModel("glm-4.7", "GLM-4.7", "https://open.bigmodel.cn/api/paas/v4"),
1517
- buildPresetModel("glm-4.6", "GLM-4.6", "https://open.bigmodel.cn/api/paas/v4"),
1518
- buildPresetModel("glm-4.5-air", "GLM-4.5 Air", "https://open.bigmodel.cn/api/paas/v4"),
1519
- buildPresetModel("glm-4.5", "GLM-4.5", "https://open.bigmodel.cn/api/paas/v4")
1520
- ]
1521
- },
1522
- stepfun: {
1523
- displayName: "StepFun",
1524
- baseUrl: "https://api.stepfun.com/v1",
1525
- models: [
1526
- buildPresetModel("step-3.7-flash", "Step 3.7 Flash", "https://api.stepfun.com/v1"),
1527
- buildPresetModel("step-3.5-flash", "Step 3.5 Flash", "https://api.stepfun.com/v1"),
1528
- buildPresetModel(
1529
- "step-1o-turbo-vision",
1530
- "Step 1o Turbo Vision",
1531
- "https://api.stepfun.com/v1"
1532
- )
1533
- ]
1534
- },
1535
- siliconflow: {
1536
- displayName: "SiliconFlow",
1537
- baseUrl: "https://api.siliconflow.cn/v1",
1538
- // 聚合平台 — 模型列表由平台动态维护(>100 个)。这里列的
1539
- // 6 个是 2026 年 7-8 月各家最新的旗舰/代表型号,给 ProvidersTab
1540
- // 一个 "一眼能看到" 的起点;用户添加 provider 后可继续通过
1541
- // `/v1/models` 拉取完整列表。
1542
- // 这里的 id 是 SiliconFlow API 用的 namespaced 字符串,必须
1543
- // 与 MODEL_METADATA 的 alias 严格一致。
1544
- models: [
1545
- // DeepSeek V4 系列 (2026-04)
1546
- buildPresetModel(
1547
- "deepseek-ai/DeepSeek-V4-Pro",
1548
- "DeepSeek V4 Pro (via SiliconFlow)",
1549
- "https://api.siliconflow.cn/v1"
1550
- ),
1551
- buildPresetModel(
1552
- "deepseek-ai/DeepSeek-V4-Flash",
1553
- "DeepSeek V4 Flash (via SiliconFlow)",
1554
- "https://api.siliconflow.cn/v1"
1555
- ),
1556
- // GLM-5.2 (2026-06-17) — open-weight 编程旗舰,1M context
1557
- buildPresetModel(
1558
- "zai-org/GLM-5.2",
1559
- "GLM-5.2 (via SiliconFlow)",
1560
- "https://api.siliconflow.cn/v1"
1561
- ),
1562
- // Qwen3.6-35B-A3B (2026-04) — 35B MoE, 3B 激活,"小而强"
1563
- buildPresetModel(
1564
- "Qwen/Qwen3.6-35B-A3B",
1565
- "Qwen3.6-35B-A3B (via SiliconFlow)",
1566
- "https://api.siliconflow.cn/v1"
1567
- ),
1568
- // Kimi K2.7-Code (2026-06-12) — Moonshot coding 旗舰
1569
- buildPresetModel(
1570
- "moonshotai/Kimi-K2.7-Code",
1571
- "Kimi K2.7 Code (via SiliconFlow)",
1572
- "https://api.siliconflow.cn/v1"
1573
- )
1574
- ]
1575
- },
1576
- openrouter: {
1577
- displayName: "OpenRouter",
1578
- baseUrl: "https://openrouter.ai/api/v1",
1579
- models: [
1580
- buildPresetModel("openrouter/auto", "OpenRouter Auto", "https://openrouter.ai/api/v1")
1581
- ]
1582
- },
1583
- novita: {
1584
- displayName: "Novita",
1585
- baseUrl: "https://api.novita.ai/openai/v1",
1586
- // 聚合平台:Novita 本身不产出自有模型,只是把 Kimi K3 / GLM 5.x /
1587
- // DeepSeek V4 等第三方开源或授权模型挂到统一 OpenAI 兼容网关下
1588
- // (见 https://novita.ai/llm-api)。这里列 5 个 2026 旗舰作为
1589
- // preset 起点;用户添加 provider 后可继续通过 `/v1/models` 拉取
1590
- // 完整列表。id 严格匹配 Novita API 的 namespaced 字符串。
1591
- models: [
1592
- // DeepSeek V4 系列 (2026-04)
1593
- buildPresetModel(
1594
- "deepseek/deepseek-v4-pro",
1595
- "DeepSeek V4 Pro (via Novita)",
1596
- "https://api.novita.ai/openai/v1"
1597
- ),
1598
- buildPresetModel(
1599
- "deepseek/deepseek-v4-flash",
1600
- "DeepSeek V4 Flash (via Novita)",
1601
- "https://api.novita.ai/openai/v1"
1602
- ),
1603
- // GLM-5 系列 (2026-04/06)
1604
- buildPresetModel("zai/glm-5.2", "GLM-5.2 (via Novita)", "https://api.novita.ai/openai/v1"),
1605
- buildPresetModel("zai/glm-5.1", "GLM-5.1 (via Novita)", "https://api.novita.ai/openai/v1"),
1606
- // Kimi K3 (2026-07-16 API, 2026-07-27 开源) — 1M context, 2.8T MoE
1607
- buildPresetModel(
1608
- "moonshotai/kimi-k3",
1609
- "Kimi K3 (via Novita)",
1610
- "https://api.novita.ai/openai/v1"
1611
- )
1612
- ]
1613
- },
1614
- medalsoft: {
1615
- displayName: "Medalsoft",
1616
- // Medalsoft internal LLM gateway (公司内部代理) — OpenAI-compatible
1617
- // `/v1/chat/completions`. The gateway forwards to upstream vendors
1618
- // (GLM / DeepSeek / Kimi / ...), so the model catalogue is dynamic
1619
- // and NOT curated here: the preset ships an EMPTY starter list and
1620
- // the user populates it via "Fetch from API" (`GET /v1/models`) in
1621
- // the ProvidersTab. `buildPresetModel`'s fail-loudly contract is
1622
- // why we don't guess ids — a curated `MODEL_METADATA` entry only
1623
- // exists for vendor-native ids, not the gateway's routing table.
1624
- //
1625
- // NOTE: models fetched from the gateway carry the upstream model
1626
- // ids, so the curated metadata (pricing / thinking dropdown /
1627
- // token caps) still resolves via `MODEL_METADATA` after the fetch.
1628
- baseUrl: "https://nexus.servicemecloud.com/v1",
1629
- models: []
1630
- },
1631
- sensenova: {
1632
- displayName: "SenseNova",
1633
- // SenseTime's SenseNova OpenAI-compatible gateway (see
1634
- // https://platform.sensenova.cn/docs — `POST /v1/chat/completions`,
1635
- // `Authorization: Bearer`, `GET /v1/models`). Token-plan quota per
1636
- // model (e.g. 1500 req / 5h for flash-lite) is enforced upstream.
1637
- //
1638
- // The starter list mirrors the live `GET /v1/models` response
1639
- // (fetched 2026-08-25 with a real key). The image-generation
1640
- // models (sensenova-u1.5-lite / sensenova-u1-fast) live on
1641
- // /v1/images/* endpoints and are explicitly documented as NOT
1642
- // usable as chat Model IDs, so they are not curated here.
1643
- // `sensenova-6.7-flash-lite` is likewise excluded — the docs
1644
- // state it is a compat alias whose calls auto-redirect to
1645
- // `sensenova-6.8-flash-lite` (through 2026-08-31).
1646
- //
1647
- // The relayed deepseek-v4-flash / glm-5.2 rows reuse their
1648
- // MODEL_METADATA entries (keyed on the un-qualified id) and then
1649
- // OVERRIDE the gateway-published specifics below: SenseNova
1650
- // serves them with a 1M context window and TEXT-ONLY input
1651
- // (per the live /v1/models payload), which differs from the
1652
- // vendor-official curated values (655K input / vision on).
1653
- // The global MODEL_METADATA stays vendor-official; only this
1654
- // preset carries the gateway-specific caps so other providers
1655
- // (DeepSeek official / Zhipu) are unaffected.
1656
- baseUrl: "https://token.sensenova.cn/v1",
1657
- models: [
1658
- buildPresetModel(
1659
- "sensenova-6.8-flash-lite",
1660
- "SenseNova 6.8 Flash-Lite",
1661
- "https://token.sensenova.cn/v1"
1662
- ),
1663
- {
1664
- // Live /v1/models: context_length 1048576,
1665
- // max_output_length 65536, input_modalities ["text"] only.
1666
- ...buildPresetModel(
1667
- "deepseek-v4-flash",
1668
- "DeepSeek V4 Flash (via SenseNova)",
1669
- "https://token.sensenova.cn/v1"
1670
- ),
1671
- maxInputTokens: 1048576,
1672
- maxOutputTokens: 65536,
1673
- capabilities: { supportsImageToText: false, supportsToolCalling: true }
1674
- },
1675
- {
1676
- // Live /v1/models: context_length 1048576,
1677
- // max_output_length 131072, input_modalities ["text"] only.
1678
- ...buildPresetModel("glm-5.2", "GLM-5.2 (via SenseNova)", "https://token.sensenova.cn/v1"),
1679
- maxInputTokens: 1048576,
1680
- maxOutputTokens: 131072,
1681
- capabilities: { supportsImageToText: false, supportsToolCalling: true }
1682
- }
1683
- ]
1684
- }
1362
+ const BUILTIN_PROVIDER_PRESETS = {
1363
+ minimax: {
1364
+ displayName: "MiniMax",
1365
+ baseUrl: "https://api.minimaxi.com/anthropic",
1366
+ models: [
1367
+ buildPresetModel("MiniMax-M3", "MiniMax-M3", "https://api.minimaxi.com/anthropic"),
1368
+ buildPresetModel("MiniMax-M2.7", "MiniMax-M2.7", "https://api.minimaxi.com/anthropic"),
1369
+ buildPresetModel("MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed", "https://api.minimaxi.com/anthropic"),
1370
+ buildPresetModel("MiniMax-M2.5", "MiniMax-M2.5", "https://api.minimaxi.com/anthropic")
1371
+ ]
1372
+ },
1373
+ deepseek: {
1374
+ displayName: "DeepSeek",
1375
+ baseUrl: "https://api.deepseek.com/v1",
1376
+ models: [buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash", "https://api.deepseek.com/v1"), buildPresetModel("deepseek-v4-pro", "DeepSeek V4 Pro", "https://api.deepseek.com/v1")]
1377
+ },
1378
+ agnes: {
1379
+ displayName: "Agnes",
1380
+ baseUrl: "https://apihub.agnes-ai.com/v1",
1381
+ models: [
1382
+ buildPresetModel("agnes-2.5-pro", "Agnes 2.5 Pro", "https://apihub.agnes-ai.com/v1"),
1383
+ buildPresetModel("agnes-2.5-pro-alpha", "Agnes 2.5 Pro Alpha", "https://apihub.agnes-ai.com/v1"),
1384
+ buildPresetModel("agnes-2.5-flash", "Agnes 2.5 Flash", "https://apihub.agnes-ai.com/v1"),
1385
+ buildPresetModel("agnes-2.0-flash", "Agnes 2.0 Flash", "https://apihub.agnes-ai.com/v1")
1386
+ ]
1387
+ },
1388
+ kimi: {
1389
+ displayName: "Kimi",
1390
+ baseUrl: "https://api.moonshot.cn/v1",
1391
+ models: [
1392
+ buildPresetModel("kimi-k3", "Kimi K3", "https://api.moonshot.cn/v1"),
1393
+ buildPresetModel("kimi-k2.7-code", "Kimi K2.7 Code", "https://api.moonshot.cn/v1"),
1394
+ buildPresetModel("kimi-k2.7-code-highspeed", "Kimi K2.7 Code HighSpeed", "https://api.moonshot.cn/v1"),
1395
+ buildPresetModel("kimi-k2.6", "Kimi K2.6", "https://api.moonshot.cn/v1")
1396
+ ]
1397
+ },
1398
+ zhipu: {
1399
+ displayName: "Zhipu",
1400
+ baseUrl: "https://open.bigmodel.cn/api/paas/v4",
1401
+ models: [
1402
+ buildPresetModel("glm-5.3", "GLM-5.3", "https://open.bigmodel.cn/api/paas/v4"),
1403
+ buildPresetModel("glm-5.3-flash", "GLM-5.3 Flash", "https://open.bigmodel.cn/api/paas/v4"),
1404
+ buildPresetModel("glm-5.2", "GLM-5.2", "https://open.bigmodel.cn/api/paas/v4"),
1405
+ buildPresetModel("glm-5.1", "GLM-5.1", "https://open.bigmodel.cn/api/paas/v4"),
1406
+ buildPresetModel("glm-5", "GLM-5", "https://open.bigmodel.cn/api/paas/v4"),
1407
+ buildPresetModel("glm-5-turbo", "GLM-5 Turbo", "https://open.bigmodel.cn/api/paas/v4"),
1408
+ buildPresetModel("glm-4.7", "GLM-4.7", "https://open.bigmodel.cn/api/paas/v4"),
1409
+ buildPresetModel("glm-4.6", "GLM-4.6", "https://open.bigmodel.cn/api/paas/v4"),
1410
+ buildPresetModel("glm-4.5-air", "GLM-4.5 Air", "https://open.bigmodel.cn/api/paas/v4"),
1411
+ buildPresetModel("glm-4.5", "GLM-4.5", "https://open.bigmodel.cn/api/paas/v4")
1412
+ ]
1413
+ },
1414
+ stepfun: {
1415
+ displayName: "StepFun",
1416
+ baseUrl: "https://api.stepfun.com/v1",
1417
+ models: [
1418
+ buildPresetModel("step-3.7-flash", "Step 3.7 Flash", "https://api.stepfun.com/v1"),
1419
+ buildPresetModel("step-3.5-flash", "Step 3.5 Flash", "https://api.stepfun.com/v1"),
1420
+ buildPresetModel("step-1o-turbo-vision", "Step 1o Turbo Vision", "https://api.stepfun.com/v1")
1421
+ ]
1422
+ },
1423
+ siliconflow: {
1424
+ displayName: "SiliconFlow",
1425
+ baseUrl: "https://api.siliconflow.cn/v1",
1426
+ models: [
1427
+ buildPresetModel("deepseek-ai/DeepSeek-V4-Pro", "DeepSeek V4 Pro (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
1428
+ buildPresetModel("deepseek-ai/DeepSeek-V4-Flash", "DeepSeek V4 Flash (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
1429
+ buildPresetModel("zai-org/GLM-5.2", "GLM-5.2 (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
1430
+ buildPresetModel("Qwen/Qwen3.6-35B-A3B", "Qwen3.6-35B-A3B (via SiliconFlow)", "https://api.siliconflow.cn/v1"),
1431
+ buildPresetModel("moonshotai/Kimi-K2.7-Code", "Kimi K2.7 Code (via SiliconFlow)", "https://api.siliconflow.cn/v1")
1432
+ ]
1433
+ },
1434
+ openrouter: {
1435
+ displayName: "OpenRouter",
1436
+ baseUrl: "https://openrouter.ai/api/v1",
1437
+ models: [buildPresetModel("openrouter/auto", "OpenRouter Auto", "https://openrouter.ai/api/v1")]
1438
+ },
1439
+ novita: {
1440
+ displayName: "Novita",
1441
+ baseUrl: "https://api.novita.ai/openai/v1",
1442
+ models: [
1443
+ buildPresetModel("deepseek/deepseek-v4-pro", "DeepSeek V4 Pro (via Novita)", "https://api.novita.ai/openai/v1"),
1444
+ buildPresetModel("deepseek/deepseek-v4-flash", "DeepSeek V4 Flash (via Novita)", "https://api.novita.ai/openai/v1"),
1445
+ buildPresetModel("zai/glm-5.2", "GLM-5.2 (via Novita)", "https://api.novita.ai/openai/v1"),
1446
+ buildPresetModel("zai/glm-5.1", "GLM-5.1 (via Novita)", "https://api.novita.ai/openai/v1"),
1447
+ buildPresetModel("moonshotai/kimi-k3", "Kimi K3 (via Novita)", "https://api.novita.ai/openai/v1")
1448
+ ]
1449
+ },
1450
+ medalsoft: {
1451
+ displayName: "Medalsoft",
1452
+ baseUrl: "https://nexus.servicemecloud.com/v1",
1453
+ models: []
1454
+ },
1455
+ sensenova: {
1456
+ displayName: "SenseNova",
1457
+ baseUrl: "https://token.sensenova.cn/v1",
1458
+ models: [
1459
+ buildPresetModel("sensenova-6.8-flash-lite", "SenseNova 6.8 Flash-Lite", "https://token.sensenova.cn/v1"),
1460
+ {
1461
+ ...buildPresetModel("deepseek-v4-flash", "DeepSeek V4 Flash (via SenseNova)", "https://token.sensenova.cn/v1"),
1462
+ maxInputTokens: 1048576,
1463
+ maxOutputTokens: 65536,
1464
+ capabilities: {
1465
+ supportsImageToText: false,
1466
+ supportsToolCalling: true
1467
+ }
1468
+ },
1469
+ {
1470
+ ...buildPresetModel("glm-5.2", "GLM-5.2 (via SenseNova)", "https://token.sensenova.cn/v1"),
1471
+ maxInputTokens: 1048576,
1472
+ maxOutputTokens: 131072,
1473
+ capabilities: {
1474
+ supportsImageToText: false,
1475
+ supportsToolCalling: true
1476
+ }
1477
+ }
1478
+ ]
1479
+ }
1685
1480
  };
1686
- var PRESET_MODEL_DISPLAY_NAMES = (() => {
1687
- const map = {};
1688
- for (const preset of Object.values(BUILTIN_PROVIDER_PRESETS)) {
1689
- for (const model of preset.models) {
1690
- if (model.displayName !== void 0) {
1691
- map[model.id] = model.displayName;
1692
- }
1693
- }
1694
- }
1695
- return Object.freeze(map);
1481
+ /**
1482
+ * Curated `id → displayName` map derived from `BUILTIN_PROVIDER_PRESETS`
1483
+ * at module-load time. Single source of truth for the friendly
1484
+ * model label shown in the picker — both the "Reset to preset
1485
+ * models" / vendor `<select>` flows (which call `buildPresetModel`
1486
+ * and get the displayName as a parameter) and the "Fetch from API"
1487
+ * union (which calls `getPresetModelDisplayName` to fill in the
1488
+ * `displayName` field when the API doesn't return one).
1489
+ *
1490
+ * Built at module load (not lazily) so the lookup is O(1) on the
1491
+ * hot path — `unionProviderModelWithPreset` runs once per fetched
1492
+ * model row, and the union runs every time the user clicks "Fetch
1493
+ * from API". A lazy Map would also work, but the upfront cost is
1494
+ * ~30 entries (the largest preset is Zhipu with ~20 GLM rows) and
1495
+ * pays for itself after the first fetch.
1496
+ */
1497
+ const PRESET_MODEL_DISPLAY_NAMES = (() => {
1498
+ const map = {};
1499
+ for (const preset of Object.values(BUILTIN_PROVIDER_PRESETS)) for (const model of preset.models) if (model.displayName !== void 0) map[model.id] = model.displayName;
1500
+ return Object.freeze(map);
1696
1501
  })();
1502
+ /**
1503
+ * Look up the curated friendly displayName for a model id. Two
1504
+ * sources, in priority order:
1505
+ *
1506
+ * 1. **Explicit map** (`PRESET_MODEL_DISPLAY_NAMES`, derived from
1507
+ * `BUILTIN_PROVIDER_PRESETS` at module load). Curated by hand;
1508
+ * wins when present so a curated prettier name
1509
+ * (e.g. "DeepSeek V4 Flash" for `deepseek-v4-flash`) is
1510
+ * always preferred over whatever the detail's prefix would
1511
+ * produce.
1512
+ * 2. **`MODEL_METADATA.detail` fallback** (2026-08-19 follow-up).
1513
+ * For ids that are in `MODEL_METADATA` (have curated pricing
1514
+ * / capabilities) but NOT in any vendor preset — e.g.
1515
+ * `glm-4.7-flash`, `glm-4.5v`, `glm-5v-turbo`, `glm-4.6v` —
1516
+ * derive the display name from the `detail` field by
1517
+ * splitting on the first ` — ` and keeping the left half.
1518
+ * This restores friendly labels for models the v1 lookup
1519
+ * missed (the user-reported case: `glm-4.7-flash` came
1520
+ * back from "Fetch from API" without a display name
1521
+ * because it was excluded from the Zhipu preset on
1522
+ * 2026-08-18, but its detail field already said
1523
+ * "GLM-4.7 Flash — 完全免费(200K 上下文)").
1524
+ *
1525
+ * Returns `undefined` for:
1526
+ * • ids that aren't in `BUILTIN_PROVIDER_PRESETS` AND aren't in
1527
+ * `MODEL_METADATA` (genuinely custom / aggregator-only
1528
+ * models the user added by hand — the consumer falls back
1529
+ * to the literal fetched id)
1530
+ * • ids in `MODEL_METADATA` whose `detail` is empty /
1531
+ * whitespace, or whose detail has no ` — ` boundary and the
1532
+ * whole string is the qualifier (defensive — every entry
1533
+ * today has a usable detail).
1534
+ *
1535
+ * Used by `unionProviderModelWithPreset` to fill in the
1536
+ * `displayName` field when the API payload omits it — the
1537
+ * picker's model name column then renders "DeepSeek V4 Flash"
1538
+ * or "GLM-4.7 Flash" instead of the bare id right after the
1539
+ * fetch result lands.
1540
+ */
1697
1541
  function getPresetModelDisplayName(id) {
1698
- const explicit = PRESET_MODEL_DISPLAY_NAMES[id];
1699
- if (explicit !== void 0) {
1700
- return explicit;
1701
- }
1702
- const meta = MODEL_METADATA[id];
1703
- if (meta === void 0) {
1704
- return void 0;
1705
- }
1706
- const detail = meta.detail;
1707
- if (typeof detail !== "string" || detail.trim() === "") {
1708
- return void 0;
1709
- }
1710
- const dashIndex = detail.indexOf(" \u2014 ");
1711
- if (dashIndex === -1) {
1712
- return detail;
1713
- }
1714
- const head = detail.slice(0, dashIndex).trim();
1715
- return head === "" ? void 0 : head;
1542
+ const explicit = PRESET_MODEL_DISPLAY_NAMES[id];
1543
+ if (explicit !== void 0) return explicit;
1544
+ const meta = MODEL_METADATA[id];
1545
+ if (meta === void 0) return;
1546
+ const detail = meta.detail;
1547
+ if (typeof detail !== "string" || detail.trim() === "") return;
1548
+ const dashIndex = detail.indexOf(" — ");
1549
+ if (dashIndex === -1) return detail;
1550
+ const head = detail.slice(0, dashIndex).trim();
1551
+ return head === "" ? void 0 : head;
1716
1552
  }
1553
+ /**
1554
+ * Look up the default config (displayName / baseUrl / models) for a
1555
+ * named vendor type. Returns `null` for the `-compatible` family —
1556
+ * those have no canned defaults; the user enters them by hand.
1557
+ */
1717
1558
  function getBuiltinProviderPreset(type) {
1718
- switch (type) {
1719
- case "minimax":
1720
- case "deepseek":
1721
- case "agnes":
1722
- case "kimi":
1723
- case "zhipu":
1724
- case "stepfun":
1725
- case "siliconflow":
1726
- case "openrouter":
1727
- case "novita":
1728
- case "medalsoft":
1729
- case "sensenova":
1730
- return BUILTIN_PROVIDER_PRESETS[type];
1731
- default:
1732
- return null;
1733
- }
1559
+ switch (type) {
1560
+ case "minimax":
1561
+ case "deepseek":
1562
+ case "agnes":
1563
+ case "kimi":
1564
+ case "zhipu":
1565
+ case "stepfun":
1566
+ case "siliconflow":
1567
+ case "openrouter":
1568
+ case "novita":
1569
+ case "medalsoft":
1570
+ case "sensenova": return BUILTIN_PROVIDER_PRESETS[type];
1571
+ default: return null;
1572
+ }
1734
1573
  }
1735
-
1736
- // src/certificate-bundle.ts
1737
- var CERTIFICATE_BUNDLE_FORMATS = [
1738
- {
1739
- format: "pem",
1740
- label: "Nginx / Generic PEM",
1741
- description: "cert.pem + key.pem + fullchain.pem (Nginx, HAProxy, most Unix servers).",
1742
- icon: "file-lock-2",
1743
- requiresPassword: false,
1744
- artifactExtension: ".pem"
1745
- },
1746
- {
1747
- format: "pfx",
1748
- label: "IIS / Tomcat PFX",
1749
- description: "PKCS#12 bundle (.pfx) for Windows IIS, Tomcat, ColdFusion. Requires a password.",
1750
- icon: "shield",
1751
- requiresPassword: true,
1752
- artifactExtension: ".pfx"
1753
- },
1754
- {
1755
- format: "crt",
1756
- label: "Apache CRT",
1757
- description: "Separate .crt + .key for Apache httpd and other Unix servers that prefer DER/PEM split.",
1758
- icon: "file-badge",
1759
- requiresPassword: false,
1760
- artifactExtension: ".crt"
1761
- },
1762
- {
1763
- format: "jks",
1764
- label: "Java JKS",
1765
- description: "Java KeyStore (.jks) for Tomcat and Java applications. Requires a JDK with `keytool` on PATH.",
1766
- icon: "coffee",
1767
- requiresPassword: true,
1768
- artifactExtension: ".jks"
1769
- }
1574
+ //#endregion
1575
+ //#region src/certificate-bundle.ts
1576
+ const CERTIFICATE_BUNDLE_FORMATS = [
1577
+ {
1578
+ format: "pem",
1579
+ label: "Nginx / Generic PEM",
1580
+ description: "cert.pem + key.pem + fullchain.pem (Nginx, HAProxy, most Unix servers).",
1581
+ icon: "file-lock-2",
1582
+ requiresPassword: false,
1583
+ artifactExtension: ".pem"
1584
+ },
1585
+ {
1586
+ format: "pfx",
1587
+ label: "IIS / Tomcat PFX",
1588
+ description: "PKCS#12 bundle (.pfx) for Windows IIS, Tomcat, ColdFusion. Requires a password.",
1589
+ icon: "shield",
1590
+ requiresPassword: true,
1591
+ artifactExtension: ".pfx"
1592
+ },
1593
+ {
1594
+ format: "crt",
1595
+ label: "Apache CRT",
1596
+ description: "Separate .crt + .key for Apache httpd and other Unix servers that prefer DER/PEM split.",
1597
+ icon: "file-badge",
1598
+ requiresPassword: false,
1599
+ artifactExtension: ".crt"
1600
+ },
1601
+ {
1602
+ format: "jks",
1603
+ label: "Java JKS",
1604
+ description: "Java KeyStore (.jks) for Tomcat and Java applications. Requires a JDK with `keytool` on PATH.",
1605
+ icon: "coffee",
1606
+ requiresPassword: true,
1607
+ artifactExtension: ".jks"
1608
+ }
1770
1609
  ];
1771
-
1772
- // src/copilot-customizations.ts
1610
+ //#endregion
1611
+ //#region src/copilot-customizations.ts
1612
+ /**
1613
+ * Whole-package registrar install marker: the explicit `wholePackage`
1614
+ * flag, or (legacy shape) the synthetic `::package:` artifact. Such
1615
+ * packages are PERSONAL-scope only — registrar-owned, so per-artifact
1616
+ * scope actions (move to workspace) do not apply.
1617
+ */
1773
1618
  function isWholePackageInstall(definition) {
1774
- return definition.wholePackage === true || definition.artifacts?.some((artifact) => artifact.id.includes("::package:")) === true;
1619
+ return definition.wholePackage === true || definition.artifacts?.some((artifact) => artifact.id.includes("::package:")) === true;
1775
1620
  }
1621
+ /**
1622
+ * Registrar record id (`repo:plugin`) from a view/package payload id
1623
+ * (`repo::plugin`). Both spellings float around the wire; the registrar
1624
+ * and the settings keys always use the single-colon form.
1625
+ */
1776
1626
  function registrationIdFromPackageId(packageId) {
1777
- return packageId.replace("::", ":");
1627
+ return packageId.replace("::", ":");
1778
1628
  }
1779
-
1780
- // ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/node.mjs
1781
- var import_crypto = require("crypto");
1782
-
1783
- // ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/shared.mjs
1629
+ //#endregion
1630
+ //#region ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/shared.mjs
1784
1631
  var INPUT_ERROR = "input is invalid type";
1785
1632
  var FINALIZE_ERROR = "finalize already called";
1786
1633
  var ARRAY_BUFFER = typeof ArrayBuffer !== "undefined";
1787
1634
  var formatMessage = function(message) {
1788
- var type = typeof message;
1789
- if (type === "string") {
1790
- return [message, true];
1791
- }
1792
- if (Array.isArray(message)) {
1793
- return [message, false];
1794
- }
1795
- if (ARRAY_BUFFER && message) {
1796
- if (message.constructor === ArrayBuffer) {
1797
- return [new Uint8Array(message), false];
1798
- } else if (ArrayBuffer.isView(message)) {
1799
- return [message, false];
1800
- }
1801
- }
1802
- throw new Error(INPUT_ERROR);
1635
+ if (typeof message === "string") return [message, true];
1636
+ if (Array.isArray(message)) return [message, false];
1637
+ if (ARRAY_BUFFER && message) {
1638
+ if (message.constructor === ArrayBuffer) return [new Uint8Array(message), false];
1639
+ else if (ArrayBuffer.isView(message)) return [message, false];
1640
+ }
1641
+ throw new Error(INPUT_ERROR);
1803
1642
  };
1804
-
1805
- // ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/node.mjs
1643
+ //#endregion
1644
+ //#region ../../node_modules/.pnpm/js-sha256@1.0.0/node_modules/js-sha256/src/node.mjs
1806
1645
  function toNodeInput(message) {
1807
- const [msg, isString] = formatMessage(message);
1808
- return isString ? Buffer.from(msg, "utf8") : Buffer.from(msg);
1646
+ const [msg, isString] = formatMessage(message);
1647
+ return isString ? Buffer.from(msg, "utf8") : Buffer.from(msg);
1809
1648
  }
1810
1649
  var NodeHasher = class {
1811
- constructor(hash) {
1812
- this.hash = hash;
1813
- this.result = void 0;
1814
- }
1815
- update(message) {
1816
- if (this.result) {
1817
- throw new Error(FINALIZE_ERROR);
1818
- }
1819
- this.hash.update(toNodeInput(message));
1820
- return this;
1821
- }
1822
- finalize() {
1823
- if (!this.result) {
1824
- this.result = this.hash.digest();
1825
- this.hash = void 0;
1826
- }
1827
- }
1828
- hex() {
1829
- this.finalize();
1830
- return this.result.toString("hex");
1831
- }
1832
- toString() {
1833
- return this.hex();
1834
- }
1835
- array() {
1836
- this.finalize();
1837
- return Array.from(this.result);
1838
- }
1839
- digest() {
1840
- return this.array();
1841
- }
1842
- arrayBuffer() {
1843
- return Uint8Array.from(this.array()).buffer;
1844
- }
1650
+ constructor(hash) {
1651
+ this.hash = hash;
1652
+ this.result = void 0;
1653
+ }
1654
+ update(message) {
1655
+ if (this.result) throw new Error(FINALIZE_ERROR);
1656
+ this.hash.update(toNodeInput(message));
1657
+ return this;
1658
+ }
1659
+ finalize() {
1660
+ if (!this.result) {
1661
+ this.result = this.hash.digest();
1662
+ this.hash = void 0;
1663
+ }
1664
+ }
1665
+ hex() {
1666
+ this.finalize();
1667
+ return this.result.toString("hex");
1668
+ }
1669
+ toString() {
1670
+ return this.hex();
1671
+ }
1672
+ array() {
1673
+ this.finalize();
1674
+ return Array.from(this.result);
1675
+ }
1676
+ digest() {
1677
+ return this.array();
1678
+ }
1679
+ arrayBuffer() {
1680
+ return Uint8Array.from(this.array()).buffer;
1681
+ }
1845
1682
  };
1846
1683
  function addOutputMethods(method, createHasher) {
1847
- method.hex = method;
1848
- method.array = function(...args) {
1849
- return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).array();
1850
- };
1851
- method.digest = method.array;
1852
- method.arrayBuffer = function(...args) {
1853
- return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).arrayBuffer();
1854
- };
1855
- return method;
1684
+ method.hex = method;
1685
+ method.array = function(...args) {
1686
+ return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).array();
1687
+ };
1688
+ method.digest = method.array;
1689
+ method.arrayBuffer = function(...args) {
1690
+ return createHasher(...args.slice(0, -1)).update(args[args.length - 1]).arrayBuffer();
1691
+ };
1692
+ return method;
1856
1693
  }
1857
1694
  function createNodeMethod(algorithm) {
1858
- const createHasher = () => new NodeHasher((0, import_crypto.createHash)(algorithm));
1859
- const method = function(message) {
1860
- return createHasher().update(message).hex();
1861
- };
1862
- addOutputMethods(method, createHasher);
1863
- method.create = createHasher;
1864
- method.update = function(message) {
1865
- return method.create().update(message);
1866
- };
1867
- return method;
1695
+ const createHasher = () => new NodeHasher((0, crypto.createHash)(algorithm));
1696
+ const method = function(message) {
1697
+ return createHasher().update(message).hex();
1698
+ };
1699
+ addOutputMethods(method, createHasher);
1700
+ method.create = createHasher;
1701
+ method.update = function(message) {
1702
+ return method.create().update(message);
1703
+ };
1704
+ return method;
1868
1705
  }
1869
1706
  function createNodeHmacMethod(algorithm) {
1870
- const createHasher = (key) => new NodeHasher((0, import_crypto.createHmac)(algorithm, toNodeInput(key)));
1871
- const method = function(key, message) {
1872
- return createHasher(key).update(message).hex();
1873
- };
1874
- addOutputMethods(method, createHasher);
1875
- method.create = createHasher;
1876
- method.update = function(key, message) {
1877
- return method.create(key).update(message);
1878
- };
1879
- return method;
1707
+ const createHasher = (key) => new NodeHasher((0, crypto.createHmac)(algorithm, toNodeInput(key)));
1708
+ const method = function(key, message) {
1709
+ return createHasher(key).update(message).hex();
1710
+ };
1711
+ addOutputMethods(method, createHasher);
1712
+ method.create = createHasher;
1713
+ method.update = function(key, message) {
1714
+ return method.create(key).update(message);
1715
+ };
1716
+ return method;
1880
1717
  }
1881
- var sha256 = createNodeMethod("sha256");
1882
- var sha224 = createNodeMethod("sha224");
1718
+ const sha256 = createNodeMethod("sha256");
1719
+ const sha224 = createNodeMethod("sha224");
1883
1720
  sha256.sha256 = sha256;
1884
1721
  sha256.sha224 = sha224;
1885
1722
  sha256.hmac = createNodeHmacMethod("sha256");
1886
1723
  sha224.hmac = createNodeHmacMethod("sha224");
1887
-
1888
- // src/device-auth.ts
1889
- var DeviceAuthHeaders = {
1890
- deviceId: "x-ms-device-id",
1891
- deviceSecret: "x-ms-device-secret",
1892
- signature: "x-ms-device-signature",
1893
- timestamp: "x-ms-device-timestamp",
1894
- secretVersion: "x-ms-device-secret-version"
1724
+ //#endregion
1725
+ //#region src/device-auth.ts
1726
+ /**
1727
+ * Device-auth wire contract shared by the extension (signer) and the
1728
+ * server (verifier).
1729
+ *
1730
+ * Single source of truth for the `x-ms-device-*` header names and the
1731
+ * HMAC-SHA-256 request-signature algorithm. Previously the same
1732
+ * constants + function were copy-pasted in three places
1733
+ * (extension `services/device/deviceAuth.ts`, core `device/deviceAuth.ts`,
1734
+ * server `lib/auth/device-signature-guard.ts`) and kept in sync by
1735
+ * comments alone. Server and extension now import from here.
1736
+ *
1737
+ * NOTE: `packages/serviceme-core/src/device/deviceAuth.ts` keeps its own
1738
+ * copy — ADL-003 forbids core → shared (and shared → core) so the core
1739
+ * copy is a documented boundary exception. Keep it in lock-step with
1740
+ * this file. See `docs/architecture/phase-5-device-header-spec.md` §5
1741
+ * for the wire format.
1742
+ */
1743
+ /** Canonical header names — MUST match the server's verifier. */
1744
+ const DeviceAuthHeaders = {
1745
+ deviceId: "x-ms-device-id",
1746
+ deviceSecret: "x-ms-device-secret",
1747
+ signature: "x-ms-device-signature",
1748
+ timestamp: "x-ms-device-timestamp",
1749
+ secretVersion: "x-ms-device-secret-version"
1895
1750
  };
1751
+ /**
1752
+ * Basis is `METHOD\nPATH\nTIMESTAMP\nBODY\nSECRET` (LF-joined, NOT JSON).
1753
+ * Output is lowercase hex SHA-256.
1754
+ *
1755
+ * Uses `js-sha256` (pure JS, synchronous, browser + Node) instead of
1756
+ * `node:crypto` so this module stays importable from the webview (the
1757
+ * shared barrel is consumed by browser bundles — `node:crypto` breaks
1758
+ * the vite/rollup build).
1759
+ */
1896
1760
  function createDeviceRequestSignature(params) {
1897
- const basis = [
1898
- params.method.toUpperCase(),
1899
- params.path,
1900
- String(params.timestamp),
1901
- params.body,
1902
- params.secret
1903
- ].join("\n");
1904
- return sha256(basis);
1761
+ const basis = [
1762
+ params.method.toUpperCase(),
1763
+ params.path,
1764
+ String(params.timestamp),
1765
+ params.body,
1766
+ params.secret
1767
+ ].join("\n");
1768
+ return sha256(basis);
1905
1769
  }
1906
-
1907
- // src/git-utils.ts
1908
- var GIT_REMOTE_HOST_ALIASES = {
1909
- "github-msc": "github.com"
1910
- };
1770
+ //#endregion
1771
+ //#region src/git-utils.ts
1772
+ /**
1773
+ * Git URL Utilities
1774
+ *
1775
+ * Pure functions for parsing and validating Git remote URLs into canonical slugs.
1776
+ * No platform-specific logic — suitable for both Node.js and browser environments.
1777
+ */
1778
+ /**
1779
+ * Built-in hostname aliases for Git remotes.
1780
+ *
1781
+ * Some users/teams configure `~/.ssh/config` `Host` aliases (e.g. to pick a
1782
+ * specific SSH identity for a work GitHub account) so their remotes read
1783
+ * `git@github-msc:owner/repo.git` instead of `git@github.com:owner/repo.git`.
1784
+ * Resolving these here — rather than only on the client — keeps the server's
1785
+ * independently-recomputed canonical slug (see `ensureRemotesMatchCanonicalSlug`
1786
+ * in `apps/server/src/app/api/v1/projects/_validators.ts`) consistent with
1787
+ * whatever the client already resolved and sent as `canonical_slug`.
1788
+ */
1789
+ const GIT_REMOTE_HOST_ALIASES = { "github-msc": "github.com" };
1911
1790
  function normalizeGitRemoteHost(host) {
1912
- const normalizedHost = host.trim().toLowerCase();
1913
- return GIT_REMOTE_HOST_ALIASES[normalizedHost] ?? normalizedHost;
1791
+ const normalizedHost = host.trim().toLowerCase();
1792
+ return GIT_REMOTE_HOST_ALIASES[normalizedHost] ?? normalizedHost;
1914
1793
  }
1794
+ /**
1795
+ * Apply {@link GIT_REMOTE_HOST_ALIASES} to an already-canonical `host/owner/repo`
1796
+ * slug string (as opposed to a raw Git URL — see `normalizeGitUrl` for that).
1797
+ */
1915
1798
  function normalizeCanonicalSlug(slug) {
1916
- const trimmedSlug = slug.trim().toLowerCase();
1917
- const firstSlashIndex = trimmedSlug.indexOf("/");
1918
- if (firstSlashIndex <= 0) {
1919
- return trimmedSlug;
1920
- }
1921
- const host = trimmedSlug.slice(0, firstSlashIndex);
1922
- const path = trimmedSlug.slice(firstSlashIndex + 1);
1923
- return `${normalizeGitRemoteHost(host)}/${path}`;
1799
+ const trimmedSlug = slug.trim().toLowerCase();
1800
+ const firstSlashIndex = trimmedSlug.indexOf("/");
1801
+ if (firstSlashIndex <= 0) return trimmedSlug;
1802
+ const host = trimmedSlug.slice(0, firstSlashIndex);
1803
+ const path = trimmedSlug.slice(firstSlashIndex + 1);
1804
+ return `${normalizeGitRemoteHost(host)}/${path}`;
1924
1805
  }
1806
+ /**
1807
+ * Normalize a Git remote URL to a canonical slug: host/owner/repo
1808
+ * - Supports HTTPS, SSH, and SCP-like syntax
1809
+ * - Strips credentials, ports, and .git suffix
1810
+ * - Lowercases host and every path segment
1811
+ * - Resolves known host aliases (see {@link GIT_REMOTE_HOST_ALIASES})
1812
+ *
1813
+ * @throws Error if the input cannot be parsed into a valid slug
1814
+ */
1925
1815
  function normalizeGitUrl(input) {
1926
- const trimmed = input.trim();
1927
- const scpMatch = !trimmed.includes("://") && trimmed.match(/^([^@]+)@([^:]+):(.+)$/);
1928
- if (scpMatch) {
1929
- const host = scpMatch[2];
1930
- const path = scpMatch[3];
1931
- return toCanonicalSlug(`ssh://${host}/${path}`);
1932
- }
1933
- try {
1934
- const url = new URL(trimmed);
1935
- const host = normalizeGitRemoteHost(url.hostname);
1936
- let pathname = url.pathname;
1937
- if (pathname.startsWith("/")) pathname = pathname.slice(1);
1938
- if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
1939
- const parts = pathname.split("/").filter(Boolean);
1940
- if (parts.length < 2) {
1941
- throw new Error("Invalid Git URL: missing owner/repo path");
1942
- }
1943
- const normalizedPath = parts.map((part) => part.toLowerCase()).join("/");
1944
- return `${host}/${normalizedPath}`;
1945
- } catch {
1946
- if (isValidCanonicalSlug(trimmed)) return normalizeCanonicalSlug(trimmed);
1947
- throw new Error(`Unparseable Git URL: ${input}`);
1948
- }
1816
+ const trimmed = input.trim();
1817
+ const scpMatch = !trimmed.includes("://") && trimmed.match(/^([^@]+)@([^:]+):(.+)$/);
1818
+ if (scpMatch) {
1819
+ const host = scpMatch[2];
1820
+ const path = scpMatch[3];
1821
+ return toCanonicalSlug(`ssh://${host}/${path}`);
1822
+ }
1823
+ try {
1824
+ const url = new URL(trimmed);
1825
+ const host = normalizeGitRemoteHost(url.hostname);
1826
+ let pathname = url.pathname;
1827
+ if (pathname.startsWith("/")) pathname = pathname.slice(1);
1828
+ if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
1829
+ const parts = pathname.split("/").filter(Boolean);
1830
+ if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
1831
+ return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
1832
+ } catch {
1833
+ if (isValidCanonicalSlug(trimmed)) return normalizeCanonicalSlug(trimmed);
1834
+ throw new Error(`Unparseable Git URL: ${input}`);
1835
+ }
1949
1836
  }
1837
+ /**
1838
+ * Validate whether a string matches the canonical slug format: host/owner/repo
1839
+ * Requires at least three segments (host + two path parts).
1840
+ * Allows percent-encoded characters (e.g. %20) for hosts like Azure DevOps
1841
+ * that permit spaces in project/repo names.
1842
+ */
1950
1843
  function isValidCanonicalSlug(slug) {
1951
- const segment = "(?:[a-zA-Z0-9_.-]|%[0-9A-Fa-f]{2})+";
1952
- const canonicalSlugPattern = new RegExp(`^${segment}(?:\\/${segment}){2,}$`);
1953
- return canonicalSlugPattern.test(slug.trim());
1844
+ const segment = "(?:[a-zA-Z0-9_.-]|%[0-9A-Fa-f]{2})+";
1845
+ return new RegExp(`^${segment}(?:\\/${segment}){2,}$`).test(slug.trim());
1954
1846
  }
1955
1847
  function toCanonicalSlug(coerced) {
1956
- const url = new URL(coerced);
1957
- const host = normalizeGitRemoteHost(url.hostname);
1958
- let pathname = url.pathname;
1959
- if (pathname.startsWith("/")) pathname = pathname.slice(1);
1960
- if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
1961
- const parts = pathname.split("/").filter(Boolean);
1962
- if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
1963
- return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
1848
+ const url = new URL(coerced);
1849
+ const host = normalizeGitRemoteHost(url.hostname);
1850
+ let pathname = url.pathname;
1851
+ if (pathname.startsWith("/")) pathname = pathname.slice(1);
1852
+ if (pathname.endsWith(".git")) pathname = pathname.slice(0, -4);
1853
+ const parts = pathname.split("/").filter(Boolean);
1854
+ if (parts.length < 2) throw new Error("Invalid Git URL: missing owner/repo path");
1855
+ return `${host}/${parts.map((part) => part.toLowerCase()).join("/")}`;
1964
1856
  }
1965
-
1966
- // src/github-api.ts
1857
+ //#endregion
1858
+ //#region src/github-api.ts
1859
+ /**
1860
+ * Fetch user info from GitHub API
1861
+ * @param token GitHub Personal Access Token or OAuth Access Token
1862
+ * @returns GitHubUser object
1863
+ * @throws Error if request fails or token is invalid
1864
+ */
1967
1865
  async function fetchGitHubUser(token) {
1968
- const resp = await fetch("https://api.github.com/user", {
1969
- headers: {
1970
- Authorization: `token ${token}`,
1971
- Accept: "application/vnd.github.v3+json"
1972
- }
1973
- });
1974
- if (resp.ok) {
1975
- const user = await resp.json();
1976
- user.email = await resolveGitHubUserEmail(token, user);
1977
- return user;
1978
- }
1979
- if (resp.status === 401) {
1980
- throw new Error("GitHub API Error: 401 Unauthorized");
1981
- }
1982
- const status = resp.status;
1983
- const statusText = resp.statusText;
1984
- let body = "";
1985
- try {
1986
- body = await resp.text();
1987
- } catch (_e) {
1988
- body = "[Failed to read response body]";
1989
- }
1990
- throw new Error(`GitHub API Error: ${status} ${statusText} - ${body}`);
1866
+ const resp = await fetch("https://api.github.com/user", { headers: {
1867
+ Authorization: `token ${token}`,
1868
+ Accept: "application/vnd.github.v3+json"
1869
+ } });
1870
+ if (resp.ok) {
1871
+ const user = await resp.json();
1872
+ user.email = await resolveGitHubUserEmail(token, user);
1873
+ return user;
1874
+ }
1875
+ if (resp.status === 401) throw new Error("GitHub API Error: 401 Unauthorized");
1876
+ const status = resp.status;
1877
+ const statusText = resp.statusText;
1878
+ let body = "";
1879
+ try {
1880
+ body = await resp.text();
1881
+ } catch (_e) {
1882
+ body = "[Failed to read response body]";
1883
+ }
1884
+ throw new Error(`GitHub API Error: ${status} ${statusText} - ${body}`);
1991
1885
  }
1992
1886
  async function resolveGitHubUserEmail(token, user) {
1993
- const directEmail = sanitizeEmail(user.email);
1994
- if (directEmail) {
1995
- return directEmail;
1996
- }
1997
- const emails = await fetchGitHubUserEmails(token);
1998
- const preferredEmail = pickPreferredGitHubEmail(emails);
1999
- return preferredEmail ?? directEmail;
1887
+ const directEmail = sanitizeEmail(user.email);
1888
+ if (directEmail) return directEmail;
1889
+ return pickPreferredGitHubEmail(await fetchGitHubUserEmails(token)) ?? directEmail;
2000
1890
  }
2001
1891
  async function fetchGitHubUserEmails(token) {
2002
- const resp = await fetch("https://api.github.com/user/emails", {
2003
- headers: {
2004
- Authorization: `token ${token}`,
2005
- Accept: "application/vnd.github.v3+json"
2006
- }
2007
- });
2008
- if (resp.ok) {
2009
- return await resp.json();
2010
- }
2011
- if (resp.status === 404 || resp.status === 403) {
2012
- return [];
2013
- }
2014
- if (resp.status === 401) {
2015
- throw new Error("GitHub API Error: 401 Unauthorized");
2016
- }
2017
- let body = "";
2018
- try {
2019
- body = await resp.text();
2020
- } catch (_e) {
2021
- body = "[Failed to read response body]";
2022
- }
2023
- throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
1892
+ const resp = await fetch("https://api.github.com/user/emails", { headers: {
1893
+ Authorization: `token ${token}`,
1894
+ Accept: "application/vnd.github.v3+json"
1895
+ } });
1896
+ if (resp.ok) return await resp.json();
1897
+ if (resp.status === 404 || resp.status === 403) return [];
1898
+ if (resp.status === 401) throw new Error("GitHub API Error: 401 Unauthorized");
1899
+ let body = "";
1900
+ try {
1901
+ body = await resp.text();
1902
+ } catch (_e) {
1903
+ body = "[Failed to read response body]";
1904
+ }
1905
+ throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
2024
1906
  }
2025
1907
  function pickPreferredGitHubEmail(emails) {
2026
- const sanitizedEmails = emails.map((emailRecord) => ({
2027
- ...emailRecord,
2028
- email: sanitizeEmail(emailRecord.email)
2029
- })).filter(
2030
- (emailRecord) => Boolean(emailRecord.email)
2031
- );
2032
- const preferred = sanitizedEmails.find((emailRecord) => emailRecord.primary && emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.primary) ?? sanitizedEmails[0];
2033
- return preferred?.email ?? null;
1908
+ const sanitizedEmails = emails.map((emailRecord) => ({
1909
+ ...emailRecord,
1910
+ email: sanitizeEmail(emailRecord.email)
1911
+ })).filter((emailRecord) => Boolean(emailRecord.email));
1912
+ return (sanitizedEmails.find((emailRecord) => emailRecord.primary && emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.verified) ?? sanitizedEmails.find((emailRecord) => emailRecord.primary) ?? sanitizedEmails[0])?.email ?? null;
2034
1913
  }
2035
1914
  function sanitizeEmail(email) {
2036
- if (!email) {
2037
- return null;
2038
- }
2039
- const normalizedEmail = email.trim();
2040
- if (!normalizedEmail) {
2041
- return null;
2042
- }
2043
- return normalizedEmail;
1915
+ if (!email) return null;
1916
+ const normalizedEmail = email.trim();
1917
+ if (!normalizedEmail) return null;
1918
+ return normalizedEmail;
2044
1919
  }
2045
- var __internal = {
2046
- pickPreferredGitHubEmail,
2047
- sanitizeEmail
1920
+ const __internal = {
1921
+ pickPreferredGitHubEmail,
1922
+ sanitizeEmail
2048
1923
  };
2049
1924
  function createOrgMembershipResult(organization, status, httpStatus, membership) {
2050
- return {
2051
- status,
2052
- httpStatus,
2053
- organization,
2054
- role: typeof membership?.role === "string" ? membership.role : null,
2055
- directMembership: typeof membership?.direct_membership === "boolean" ? membership.direct_membership : null
2056
- };
1925
+ return {
1926
+ status,
1927
+ httpStatus,
1928
+ organization,
1929
+ role: typeof membership?.role === "string" ? membership.role : null,
1930
+ directMembership: typeof membership?.direct_membership === "boolean" ? membership.direct_membership : null
1931
+ };
2057
1932
  }
1933
+ /**
1934
+ * Fetch the authenticated user's membership details for a GitHub organization.
1935
+ * Uses the memberships list endpoint so callers can distinguish active, pending,
1936
+ * and indeterminate states instead of collapsing everything into a boolean.
1937
+ */
2058
1938
  async function getGitHubOrgMembership(token, org) {
2059
- const normalizedOrg = org.trim().toLowerCase();
2060
- const resp = await fetch("https://api.github.com/user/memberships/orgs", {
2061
- headers: {
2062
- Authorization: `token ${token}`,
2063
- Accept: "application/vnd.github+json",
2064
- "X-GitHub-Api-Version": "2022-11-28"
2065
- }
2066
- });
2067
- if (resp.status === 401) {
2068
- return createOrgMembershipResult(org, "unauthorized", resp.status);
2069
- }
2070
- if (resp.status === 403) {
2071
- return createOrgMembershipResult(org, "forbidden", resp.status);
2072
- }
2073
- if (!resp.ok) {
2074
- let body = "";
2075
- try {
2076
- body = await resp.text();
2077
- } catch (_e) {
2078
- body = "[Failed to read response body]";
2079
- }
2080
- throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
2081
- }
2082
- const memberships = await resp.json();
2083
- const membership = memberships.find(
2084
- (entry) => entry.organization?.login?.toLowerCase() === normalizedOrg
2085
- );
2086
- if (!membership) {
2087
- return createOrgMembershipResult(org, "not_member", resp.status);
2088
- }
2089
- if (membership.state === "pending") {
2090
- return createOrgMembershipResult(org, "pending", resp.status, membership);
2091
- }
2092
- if (membership.state === "active") {
2093
- return createOrgMembershipResult(org, "active", resp.status, membership);
2094
- }
2095
- return createOrgMembershipResult(org, "not_member", resp.status, membership);
1939
+ const normalizedOrg = org.trim().toLowerCase();
1940
+ const resp = await fetch("https://api.github.com/user/memberships/orgs", { headers: {
1941
+ Authorization: `token ${token}`,
1942
+ Accept: "application/vnd.github+json",
1943
+ "X-GitHub-Api-Version": "2022-11-28"
1944
+ } });
1945
+ if (resp.status === 401) return createOrgMembershipResult(org, "unauthorized", resp.status);
1946
+ if (resp.status === 403) return createOrgMembershipResult(org, "forbidden", resp.status);
1947
+ if (!resp.ok) {
1948
+ let body = "";
1949
+ try {
1950
+ body = await resp.text();
1951
+ } catch (_e) {
1952
+ body = "[Failed to read response body]";
1953
+ }
1954
+ throw new Error(`GitHub API Error: ${resp.status} ${resp.statusText} - ${body}`);
1955
+ }
1956
+ const membership = (await resp.json()).find((entry) => entry.organization?.login?.toLowerCase() === normalizedOrg);
1957
+ if (!membership) return createOrgMembershipResult(org, "not_member", resp.status);
1958
+ if (membership.state === "pending") return createOrgMembershipResult(org, "pending", resp.status, membership);
1959
+ if (membership.state === "active") return createOrgMembershipResult(org, "active", resp.status, membership);
1960
+ return createOrgMembershipResult(org, "not_member", resp.status, membership);
2096
1961
  }
1962
+ /**
1963
+ * Check if the token owner is authorized for a given GitHub organization.
1964
+ * Uses the authenticated user's own token — works for both public and private membership.
1965
+ * Returns true if the user is active or has a pending invitation, false otherwise.
1966
+ * @param token GitHub Personal Access Token or OAuth Access Token
1967
+ * @param org GitHub organization name
1968
+ */
2097
1969
  async function checkGitHubOrgMembership(token, org) {
2098
- const membership = await getGitHubOrgMembership(token, org);
2099
- if (membership.status === "forbidden" || membership.status === "unauthorized") {
2100
- throw new Error(`GitHub org membership indeterminate: HTTP ${membership.httpStatus}`);
2101
- }
2102
- return membership.status === "active" || membership.status === "pending";
1970
+ const membership = await getGitHubOrgMembership(token, org);
1971
+ if (membership.status === "forbidden" || membership.status === "unauthorized") throw new Error(`GitHub org membership indeterminate: HTTP ${membership.httpStatus}`);
1972
+ return membership.status === "active" || membership.status === "pending";
2103
1973
  }
2104
-
2105
- // src/github-user-email.ts
1974
+ //#endregion
1975
+ //#region src/github-user-email.ts
2106
1976
  function isGitHubLocalEmail(email) {
2107
- return typeof email === "string" && email.trim().toLowerCase().endsWith("@github.local");
1977
+ return typeof email === "string" && email.trim().toLowerCase().endsWith("@github.local");
2108
1978
  }
2109
1979
  function buildGitHubLocalEmail(login) {
2110
- return `${login}@github.local`;
1980
+ return `${login}@github.local`;
2111
1981
  }
2112
1982
  function resolvePrimaryEmail(login, email) {
2113
- const normalizedEmail = email?.trim();
2114
- return normalizedEmail || buildGitHubLocalEmail(login);
1983
+ return email?.trim() || buildGitHubLocalEmail(login);
2115
1984
  }
2116
-
2117
- // src/logger/index.ts
2118
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
2119
- LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
2120
- LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
2121
- LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
2122
- LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
2123
- return LogLevel2;
2124
- })(LogLevel || {});
1985
+ //#endregion
1986
+ //#region src/logger/index.ts
1987
+ /**
1988
+ * Unified, environment-agnostic logger contract for the SERVICEME monorepo.
1989
+ *
1990
+ * This module intentionally has NO dependency on `vscode`, the extension
1991
+ * runtime, or `@serviceme/devtools-core` so it can be consumed by every
1992
+ * package (shared → protocol → core → cli → webview → extension → server)
1993
+ * without creating cycles or pulling in heavyweight environment-specific
1994
+ * APIs.
1995
+ *
1996
+ * Consumers route their telemetry through an {@link ILogger}:
1997
+ * - the extension uses the OutputChannel + file-backed `Logger` (see
1998
+ * `apps/extension/src/core/logger/Logger.ts`);
1999
+ * - server / cli / webview use the console / postMessage-backed
2000
+ * implementations provided by their own package, or the
2001
+ * {@link createConsoleLogger} fallback defined here.
2002
+ */
2003
+ /** Severity levels, ordered low → high. */
2004
+ let LogLevel = /* @__PURE__ */ function(LogLevel) {
2005
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
2006
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
2007
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
2008
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
2009
+ return LogLevel;
2010
+ }({});
2011
+ /**
2012
+ * Normalize any thrown value into a serializable record for logging.
2013
+ *
2014
+ * Handles `Error` (canonical fields + protocol-level extras), strings,
2015
+ * primitives, and plain objects, so log sinks can `JSON.stringify` the
2016
+ * result without throwing on circular refs or dropping context. This is a
2017
+ * pure function with no environment dependencies and was promoted from
2018
+ * `apps/extension/src/core/logger/Logger.ts` so every package shares one
2019
+ * normalization path.
2020
+ */
2125
2021
  function normalizeErrorForLog(error) {
2126
- if (error === null || error === void 0) {
2127
- return { kind: "nullish", value: null };
2128
- }
2129
- const t = typeof error;
2130
- if (t === "string") {
2131
- return { kind: "string", message: error };
2132
- }
2133
- if (t === "number" || t === "boolean" || t === "bigint") {
2134
- return { kind: t, value: String(error) };
2135
- }
2136
- if (t === "object") {
2137
- const obj = error;
2138
- if (error instanceof Error) {
2139
- const result2 = {
2140
- kind: "error",
2141
- name: error.name,
2142
- message: error.message,
2143
- stack: error.stack
2144
- };
2145
- if (typeof obj.code === "string") {
2146
- result2.code = obj.code;
2147
- }
2148
- if (typeof obj.retryable === "boolean") {
2149
- result2.retryable = obj.retryable;
2150
- }
2151
- if ("details" in obj) {
2152
- result2.details = obj.details;
2153
- }
2154
- if (typeof obj.cause !== "undefined") {
2155
- result2.cause = normalizeErrorForLog(obj.cause);
2156
- }
2157
- return result2;
2158
- }
2159
- const result = { kind: "object" };
2160
- let captured = 0;
2161
- for (const [k, v] of Object.entries(obj)) {
2162
- result[k] = v;
2163
- captured += 1;
2164
- }
2165
- if (captured === 0) {
2166
- result.message = "(empty object)";
2167
- }
2168
- return result;
2169
- }
2170
- return { kind: t, value: String(error) };
2022
+ if (error === null || error === void 0) return {
2023
+ kind: "nullish",
2024
+ value: null
2025
+ };
2026
+ const t = typeof error;
2027
+ if (t === "string") return {
2028
+ kind: "string",
2029
+ message: error
2030
+ };
2031
+ if (t === "number" || t === "boolean" || t === "bigint") return {
2032
+ kind: t,
2033
+ value: String(error)
2034
+ };
2035
+ if (t === "object") {
2036
+ const obj = error;
2037
+ if (error instanceof Error) {
2038
+ const result = {
2039
+ kind: "error",
2040
+ name: error.name,
2041
+ message: error.message,
2042
+ stack: error.stack
2043
+ };
2044
+ if (typeof obj.code === "string") result.code = obj.code;
2045
+ if (typeof obj.retryable === "boolean") result.retryable = obj.retryable;
2046
+ if ("details" in obj) result.details = obj.details;
2047
+ if (typeof obj.cause !== "undefined") result.cause = normalizeErrorForLog(obj.cause);
2048
+ return result;
2049
+ }
2050
+ const result = { kind: "object" };
2051
+ let captured = 0;
2052
+ for (const [k, v] of Object.entries(obj)) {
2053
+ result[k] = v;
2054
+ captured += 1;
2055
+ }
2056
+ if (captured === 0) result.message = "(empty object)";
2057
+ return result;
2058
+ }
2059
+ return {
2060
+ kind: t,
2061
+ value: String(error)
2062
+ };
2171
2063
  }
2064
+ /**
2065
+ * Best-effort check for `NODE_ENV === "production"`.
2066
+ *
2067
+ * Written against `globalThis` (rather than the `process` global) so this
2068
+ * module stays environment-agnostic and type-checks without `@types/node`.
2069
+ * In a browser / webview `globalThis.process` is undefined, so we treat that
2070
+ * as "not production" (verbose logging on) — which matches the prior
2071
+ * always-on `console.*` behavior of the webview.
2072
+ */
2172
2073
  function isProduction() {
2173
- const proc = globalThis.process;
2174
- return proc?.env?.NODE_ENV === "production";
2074
+ return globalThis.process?.env?.NODE_ENV === "production";
2175
2075
  }
2076
+ /**
2077
+ * Create a console-backed {@link ILogger}.
2078
+ *
2079
+ * - `debug` is gated to `NODE_ENV !== "production"` (verbose traces only in
2080
+ * dev / test), mirroring the server's legacy `logDebug` behavior.
2081
+ * - `info` / `warn` / `error` always emit to the matching `console` method.
2082
+ * - `error`'s leading `error` argument is run through
2083
+ * {@link normalizeErrorForLog} so serialized errors stay structured and
2084
+ * circular-ref safe.
2085
+ *
2086
+ * This is the canonical fallback for packages without a richer sink (cli,
2087
+ * webview bootstrap, extension logger self-diagnostics).
2088
+ */
2176
2089
  function createConsoleLogger(name) {
2177
- let level = isProduction() ? 1 /* INFO */ : 0 /* DEBUG */;
2178
- const emit = (lvl, method, message, args) => {
2179
- if (lvl < level) {
2180
- return;
2181
- }
2182
- const prefixed = `[${name}] ${message}`;
2183
- switch (method) {
2184
- case "log":
2185
- console.log(prefixed, ...args);
2186
- break;
2187
- case "info":
2188
- console.info(prefixed, ...args);
2189
- break;
2190
- case "warn":
2191
- console.warn(prefixed, ...args);
2192
- break;
2193
- case "error":
2194
- console.error(prefixed, ...args);
2195
- break;
2196
- }
2197
- };
2198
- return {
2199
- debug(message, ...args) {
2200
- emit(0 /* DEBUG */, "log", message, args);
2201
- },
2202
- info(message, ...args) {
2203
- emit(1 /* INFO */, "info", message, args);
2204
- },
2205
- warn(message, ...args) {
2206
- emit(2 /* WARN */, "warn", message, args);
2207
- },
2208
- error(message, error, ...args) {
2209
- const payload = error === void 0 ? args : [normalizeErrorForLog(error), ...args];
2210
- emit(3 /* ERROR */, "error", message, payload);
2211
- },
2212
- setLevel(next) {
2213
- level = next;
2214
- }
2215
- };
2090
+ let level = isProduction() ? 1 : 0;
2091
+ const emit = (lvl, method, message, args) => {
2092
+ if (lvl < level) return;
2093
+ const prefixed = `[${name}] ${message}`;
2094
+ switch (method) {
2095
+ case "log":
2096
+ console.log(prefixed, ...args);
2097
+ break;
2098
+ case "info":
2099
+ console.info(prefixed, ...args);
2100
+ break;
2101
+ case "warn":
2102
+ console.warn(prefixed, ...args);
2103
+ break;
2104
+ case "error": console.error(prefixed, ...args);
2105
+ }
2106
+ };
2107
+ return {
2108
+ debug(message, ...args) {
2109
+ emit(0, "log", message, args);
2110
+ },
2111
+ info(message, ...args) {
2112
+ emit(1, "info", message, args);
2113
+ },
2114
+ warn(message, ...args) {
2115
+ emit(2, "warn", message, args);
2116
+ },
2117
+ error(message, error, ...args) {
2118
+ const payload = error === void 0 ? args : [normalizeErrorForLog(error), ...args];
2119
+ emit(3, "error", message, payload);
2120
+ },
2121
+ setLevel(next) {
2122
+ level = next;
2123
+ }
2124
+ };
2216
2125
  }
2217
-
2218
- // src/messages.ts
2219
- var WebviewMessageType = /* @__PURE__ */ ((WebviewMessageType2) => {
2220
- WebviewMessageType2["WebviewReady"] = "webviewReady";
2221
- WebviewMessageType2["Ready"] = "ready";
2222
- WebviewMessageType2["Log"] = "log";
2223
- WebviewMessageType2["ExecuteCommand"] = "executeCommand";
2224
- WebviewMessageType2["OpenUrl"] = "openUrl";
2225
- WebviewMessageType2["UsePrompt"] = "usePrompt";
2226
- WebviewMessageType2["UpdateAzureProfiles"] = "updateAzureProfiles";
2227
- WebviewMessageType2["UpdateAzureProfile"] = "updateAzureProfile";
2228
- WebviewMessageType2["GetApiConfig"] = "getApiConfig";
2229
- WebviewMessageType2["UpdateApiConfig"] = "updateApiConfig";
2230
- WebviewMessageType2["SaveApiConfig"] = "saveApiConfig";
2231
- WebviewMessageType2["ApiConfigSaveFailed"] = "apiConfigSaveFailed";
2232
- WebviewMessageType2["ExportApiConfig"] = "exportApiConfig";
2233
- WebviewMessageType2["ImportApiConfig"] = "importApiConfig";
2234
- WebviewMessageType2["ApiConfigImported"] = "apiConfigImported";
2235
- WebviewMessageType2["AddExternalTool"] = "addExternalTool";
2236
- WebviewMessageType2["UpdateExternalTool"] = "updateExternalTool";
2237
- WebviewMessageType2["UpdateExternalTools"] = "updateExternalTools";
2238
- WebviewMessageType2["DeleteExternalTool"] = "deleteExternalTool";
2239
- WebviewMessageType2["ReorderExternalTools"] = "reorderExternalTools";
2240
- WebviewMessageType2["UpdateNgrokStatus"] = "updateNgrokStatus";
2241
- WebviewMessageType2["UpdateServerStatus"] = "updateServerStatus";
2242
- WebviewMessageType2["UpdateOcxStatus"] = "updateOcxStatus";
2243
- WebviewMessageType2["UpdateRtkStatus"] = "updateRtkStatus";
2244
- WebviewMessageType2["UpdateCodegraphStatus"] = "updateCodegraphStatus";
2245
- WebviewMessageType2["GetAuthState"] = "getAuthState";
2246
- WebviewMessageType2["UpdateAuthState"] = "updateAuthState";
2247
- WebviewMessageType2["Login"] = "login";
2248
- WebviewMessageType2["Logout"] = "logout";
2249
- WebviewMessageType2["GetAccounts"] = "getAccounts";
2250
- WebviewMessageType2["UpdateAccounts"] = "updateAccounts";
2251
- WebviewMessageType2["SwitchAccount"] = "switchAccount";
2252
- WebviewMessageType2["GetUserProfile"] = "getUserProfile";
2253
- WebviewMessageType2["UpdateUserProfile"] = "updateUserProfile";
2254
- WebviewMessageType2["UserProfileUpdated"] = "userProfileUpdated";
2255
- WebviewMessageType2["AddExtraEmail"] = "addExtraEmail";
2256
- WebviewMessageType2["DeleteExtraEmail"] = "deleteExtraEmail";
2257
- WebviewMessageType2["GetCalendarHolidays"] = "getCalendarHolidays";
2258
- WebviewMessageType2["UpdateCalendarHolidays"] = "updateCalendarHolidays";
2259
- WebviewMessageType2["GetCalendarLeaves"] = "getCalendarLeaves";
2260
- WebviewMessageType2["UpdateCalendarLeaves"] = "updateCalendarLeaves";
2261
- WebviewMessageType2["CreateCalendarLeave"] = "createCalendarLeave";
2262
- WebviewMessageType2["DeleteCalendarLeave"] = "deleteCalendarLeave";
2263
- WebviewMessageType2["GetCalendarNotes"] = "getCalendarNotes";
2264
- WebviewMessageType2["UpdateCalendarNotes"] = "updateCalendarNotes";
2265
- WebviewMessageType2["SaveCalendarNote"] = "saveCalendarNote";
2266
- WebviewMessageType2["GetCalendarMonthSchedule"] = "getCalendarMonthSchedule";
2267
- WebviewMessageType2["GetScheduledTasks"] = "getScheduledTasks";
2268
- WebviewMessageType2["UpdateScheduledTasks"] = "updateScheduledTasks";
2269
- WebviewMessageType2["CreateScheduledTask"] = "createScheduledTask";
2270
- WebviewMessageType2["EditScheduledTask"] = "editScheduledTask";
2271
- WebviewMessageType2["DeleteScheduledTask"] = "deleteScheduledTask";
2272
- WebviewMessageType2["ToggleScheduledTask"] = "toggleScheduledTask";
2273
- WebviewMessageType2["TriggerScheduledTask"] = "triggerScheduledTask";
2274
- WebviewMessageType2["CancelTaskExecution"] = "cancelTaskExecution";
2275
- WebviewMessageType2["GetTaskExecutionLogs"] = "getTaskExecutionLogs";
2276
- WebviewMessageType2["ClearTaskExecutionLogs"] = "clearTaskExecutionLogs";
2277
- WebviewMessageType2["UpdateTaskExecutionLogs"] = "updateTaskExecutionLogs";
2278
- WebviewMessageType2["UpdateTaskExecutionState"] = "updateTaskExecutionState";
2279
- WebviewMessageType2["GetCurrentWorkspace"] = "getCurrentWorkspace";
2280
- WebviewMessageType2["UpdateCurrentWorkspace"] = "updateCurrentWorkspace";
2281
- WebviewMessageType2["GetProviders"] = "getProviders";
2282
- WebviewMessageType2["ProvidersResponse"] = "providersResponse";
2283
- WebviewMessageType2["AddProvider"] = "addProvider";
2284
- WebviewMessageType2["UpdateProvider"] = "updateProvider";
2285
- WebviewMessageType2["RemoveProvider"] = "removeProvider";
2286
- WebviewMessageType2["SetDefaultProvider"] = "setDefaultProvider";
2287
- WebviewMessageType2["SetProviderEnabled"] = "setProviderEnabled";
2288
- WebviewMessageType2["SetProviderOrder"] = "setProviderOrder";
2289
- WebviewMessageType2["TestProvider"] = "testProvider";
2290
- WebviewMessageType2["ProviderTestResultMessage"] = "providerTestResult";
2291
- WebviewMessageType2["TestProviderModel"] = "testProviderModel";
2292
- WebviewMessageType2["ProviderTestModelResultMessage"] = "providerTestModelResult";
2293
- WebviewMessageType2["DefaultProviderChanged"] = "defaultProviderChanged";
2294
- WebviewMessageType2["FetchProviderModels"] = "fetchProviderModels";
2295
- WebviewMessageType2["FetchProviderModelsResult"] = "fetchProviderModelsResult";
2296
- WebviewMessageType2["GetProviderUsage"] = "getProviderUsage";
2297
- WebviewMessageType2["ProviderUsageResponse"] = "providerUsageResponse";
2298
- WebviewMessageType2["SetCacheControlEnabled"] = "setCacheControlEnabled";
2299
- WebviewMessageType2["GetByomSettings"] = "getByomSettings";
2300
- WebviewMessageType2["ByomSettingsResponse"] = "byomSettingsResponse";
2301
- WebviewMessageType2["ListSkillRepoEntries"] = "listSkillRepoEntries";
2302
- WebviewMessageType2["GetSkillRepoEntry"] = "getSkillRepoEntry";
2303
- WebviewMessageType2["InstallSkillRepoEntry"] = "installSkillRepoEntry";
2304
- WebviewMessageType2["ConvertSkillRepoEntryToSymlink"] = "convertSkillRepoEntryToSymlink";
2305
- WebviewMessageType2["UninstallSkillRepoEntry"] = "uninstallSkillRepoEntry";
2306
- WebviewMessageType2["SetSkillRepoEntryEnabled"] = "setSkillRepoEntryEnabled";
2307
- WebviewMessageType2["ListLinkedSkills"] = "listLinkedSkills";
2308
- WebviewMessageType2["UpdateSkillRepoCatalog"] = "updateSkillRepoCatalog";
2309
- WebviewMessageType2["UpdateSkillRepoInstall"] = "updateSkillRepoInstall";
2310
- WebviewMessageType2["UpdateLinkedSkills"] = "updateLinkedSkills";
2311
- WebviewMessageType2["CreateSkillRepoDraft"] = "createSkillRepoDraft";
2312
- WebviewMessageType2["CommitSkillRepoDraft"] = "commitSkillRepoDraft";
2313
- WebviewMessageType2["ListSkillRepoDrafts"] = "listSkillRepoDrafts";
2314
- WebviewMessageType2["DeleteSkillRepoDraft"] = "deleteSkillRepoDraft";
2315
- WebviewMessageType2["UpdateSkillRepoDraft"] = "updateSkillRepoDraft";
2316
- WebviewMessageType2["ListRepositories"] = "listRepositories";
2317
- WebviewMessageType2["AddRepository"] = "addRepository";
2318
- WebviewMessageType2["UpdateRepository"] = "updateRepository";
2319
- WebviewMessageType2["RemoveRepository"] = "removeRepository";
2320
- WebviewMessageType2["EnableRepository"] = "enableRepository";
2321
- WebviewMessageType2["DisableRepository"] = "disableRepository";
2322
- WebviewMessageType2["SyncRepository"] = "syncRepository";
2323
- WebviewMessageType2["SyncAllRepositories"] = "syncAllRepositories";
2324
- WebviewMessageType2["ResetParseCache"] = "resetParseCache";
2325
- WebviewMessageType2["ListCopilotPlugins"] = "listCopilotPlugins";
2326
- WebviewMessageType2["RegisterCopilotPlugin"] = "registerCopilotPlugin";
2327
- WebviewMessageType2["UnregisterCopilotPlugin"] = "unregisterCopilotPlugin";
2328
- WebviewMessageType2["SetCopilotPluginEnabled"] = "setCopilotPluginEnabled";
2329
- WebviewMessageType2["GetCopilotContentStatus"] = "getCopilotContentStatus";
2330
- WebviewMessageType2["RestoreCopilotContent"] = "restoreCopilotContent";
2331
- WebviewMessageType2["ApproveCopilotContent"] = "approveCopilotContent";
2332
- WebviewMessageType2["UpdateRepositoryList"] = "updateRepositoryList";
2333
- WebviewMessageType2["UpdateRepositoryAdd"] = "updateRepositoryAdd";
2334
- WebviewMessageType2["UpdateRepositoryUpdate"] = "updateRepositoryUpdate";
2335
- WebviewMessageType2["UpdateRepositoryRemove"] = "updateRepositoryRemove";
2336
- WebviewMessageType2["UpdateRepositoryToggle"] = "updateRepositoryToggle";
2337
- WebviewMessageType2["UpdateRepositorySyncResult"] = "updateRepositorySyncResult";
2338
- WebviewMessageType2["UpdateCopilotContentStatus"] = "updateCopilotContentStatus";
2339
- WebviewMessageType2["GetCopilotCustomizations"] = "getCopilotCustomizations";
2340
- WebviewMessageType2["SyncCopilotCustomizations"] = "syncCopilotCustomizations";
2341
- WebviewMessageType2["UpdateCopilotCustomizations"] = "updateCopilotCustomizations";
2342
- WebviewMessageType2["InstallCopilotPackage"] = "installCopilotPackage";
2343
- WebviewMessageType2["MoveCopilotPackage"] = "moveCopilotPackage";
2344
- WebviewMessageType2["UninstallCopilotPackage"] = "uninstallCopilotPackage";
2345
- WebviewMessageType2["ListCopilotSources"] = "listCopilotSources";
2346
- WebviewMessageType2["RemoveCopilotSource"] = "removeCopilotSource";
2347
- WebviewMessageType2["PreviewCopilotUpdate"] = "previewCopilotUpdate";
2348
- WebviewMessageType2["UpdateCopilotPackage"] = "updateCopilotPackage";
2349
- WebviewMessageType2["PreviewCopilotLegacy"] = "previewCopilotLegacy";
2350
- WebviewMessageType2["MigrateCopilotLegacy"] = "migrateCopilotLegacy";
2351
- WebviewMessageType2["UpdateCopilotSources"] = "updateCopilotSources";
2352
- WebviewMessageType2["UpdateCopilotUpdatePreview"] = "updateCopilotUpdatePreview";
2353
- WebviewMessageType2["UpdateCopilotLegacyPreview"] = "updateCopilotLegacyPreview";
2354
- WebviewMessageType2["GetUtilityModels"] = "getUtilityModels";
2355
- WebviewMessageType2["UtilityModelsResponse"] = "utilityModelsResponse";
2356
- WebviewMessageType2["UpdateUtilityModels"] = "updateUtilityModels";
2357
- WebviewMessageType2["GetServerProxyState"] = "getServerProxyState";
2358
- WebviewMessageType2["ServerProxyStateResponse"] = "serverProxyStateResponse";
2359
- WebviewMessageType2["SetServerProxyEnabled"] = "setServerProxyEnabled";
2360
- WebviewMessageType2["SetServerProxyAllowOverride"] = "setServerProxyAllowOverride";
2361
- WebviewMessageType2["GetCachedServerUrl"] = "getCachedServerUrl";
2362
- WebviewMessageType2["CachedServerUrlResponse"] = "cachedServerUrlResponse";
2363
- return WebviewMessageType2;
2364
- })(WebviewMessageType || {});
2365
- var GetUtilityModels = "getUtilityModels";
2366
- var UtilityModelsResponse = "utilityModelsResponse";
2367
- var UpdateUtilityModels = "updateUtilityModels";
2368
- var SetCacheControlEnabled = "setCacheControlEnabled";
2369
- var GetByomSettings = "getByomSettings";
2370
- var ByomSettingsResponse = "byomSettingsResponse";
2371
- var GetServerProxyState = "getServerProxyState";
2372
- var ServerProxyStateResponse = "serverProxyStateResponse";
2373
- var SetServerProxyEnabled = "setServerProxyEnabled";
2374
- var SetServerProxyAllowOverride = "setServerProxyAllowOverride";
2375
- var GetCachedServerUrl = "getCachedServerUrl";
2376
- var CachedServerUrlResponse = "cachedServerUrlResponse";
2377
-
2378
- // src/utils/safe-cast.ts
2126
+ //#endregion
2127
+ //#region src/messages.ts
2128
+ let WebviewMessageType = /* @__PURE__ */ function(WebviewMessageType) {
2129
+ WebviewMessageType["WebviewReady"] = "webviewReady";
2130
+ WebviewMessageType["Ready"] = "ready";
2131
+ WebviewMessageType["Log"] = "log";
2132
+ WebviewMessageType["ExecuteCommand"] = "executeCommand";
2133
+ WebviewMessageType["OpenUrl"] = "openUrl";
2134
+ WebviewMessageType["UsePrompt"] = "usePrompt";
2135
+ WebviewMessageType["UpdateAzureProfiles"] = "updateAzureProfiles";
2136
+ WebviewMessageType["UpdateAzureProfile"] = "updateAzureProfile";
2137
+ WebviewMessageType["GetApiConfig"] = "getApiConfig";
2138
+ WebviewMessageType["UpdateApiConfig"] = "updateApiConfig";
2139
+ WebviewMessageType["SaveApiConfig"] = "saveApiConfig";
2140
+ WebviewMessageType["ApiConfigSaveFailed"] = "apiConfigSaveFailed";
2141
+ WebviewMessageType["ExportApiConfig"] = "exportApiConfig";
2142
+ WebviewMessageType["ImportApiConfig"] = "importApiConfig";
2143
+ WebviewMessageType["ApiConfigImported"] = "apiConfigImported";
2144
+ WebviewMessageType["AddExternalTool"] = "addExternalTool";
2145
+ WebviewMessageType["UpdateExternalTool"] = "updateExternalTool";
2146
+ WebviewMessageType["UpdateExternalTools"] = "updateExternalTools";
2147
+ WebviewMessageType["DeleteExternalTool"] = "deleteExternalTool";
2148
+ WebviewMessageType["ReorderExternalTools"] = "reorderExternalTools";
2149
+ WebviewMessageType["UpdateNgrokStatus"] = "updateNgrokStatus";
2150
+ WebviewMessageType["UpdateServerStatus"] = "updateServerStatus";
2151
+ WebviewMessageType["UpdateOcxStatus"] = "updateOcxStatus";
2152
+ WebviewMessageType["UpdateRtkStatus"] = "updateRtkStatus";
2153
+ WebviewMessageType["UpdateCodegraphStatus"] = "updateCodegraphStatus";
2154
+ WebviewMessageType["GetAuthState"] = "getAuthState";
2155
+ WebviewMessageType["UpdateAuthState"] = "updateAuthState";
2156
+ WebviewMessageType["Login"] = "login";
2157
+ WebviewMessageType["Logout"] = "logout";
2158
+ WebviewMessageType["GetAccounts"] = "getAccounts";
2159
+ WebviewMessageType["UpdateAccounts"] = "updateAccounts";
2160
+ WebviewMessageType["SwitchAccount"] = "switchAccount";
2161
+ WebviewMessageType["GetUserProfile"] = "getUserProfile";
2162
+ WebviewMessageType["UpdateUserProfile"] = "updateUserProfile";
2163
+ WebviewMessageType["UserProfileUpdated"] = "userProfileUpdated";
2164
+ WebviewMessageType["AddExtraEmail"] = "addExtraEmail";
2165
+ WebviewMessageType["DeleteExtraEmail"] = "deleteExtraEmail";
2166
+ WebviewMessageType["GetCalendarHolidays"] = "getCalendarHolidays";
2167
+ WebviewMessageType["UpdateCalendarHolidays"] = "updateCalendarHolidays";
2168
+ WebviewMessageType["GetCalendarLeaves"] = "getCalendarLeaves";
2169
+ WebviewMessageType["UpdateCalendarLeaves"] = "updateCalendarLeaves";
2170
+ WebviewMessageType["CreateCalendarLeave"] = "createCalendarLeave";
2171
+ WebviewMessageType["DeleteCalendarLeave"] = "deleteCalendarLeave";
2172
+ WebviewMessageType["GetCalendarNotes"] = "getCalendarNotes";
2173
+ WebviewMessageType["UpdateCalendarNotes"] = "updateCalendarNotes";
2174
+ WebviewMessageType["SaveCalendarNote"] = "saveCalendarNote";
2175
+ WebviewMessageType["GetCalendarMonthSchedule"] = "getCalendarMonthSchedule";
2176
+ WebviewMessageType["GetScheduledTasks"] = "getScheduledTasks";
2177
+ WebviewMessageType["UpdateScheduledTasks"] = "updateScheduledTasks";
2178
+ WebviewMessageType["CreateScheduledTask"] = "createScheduledTask";
2179
+ WebviewMessageType["EditScheduledTask"] = "editScheduledTask";
2180
+ WebviewMessageType["DeleteScheduledTask"] = "deleteScheduledTask";
2181
+ WebviewMessageType["ToggleScheduledTask"] = "toggleScheduledTask";
2182
+ WebviewMessageType["TriggerScheduledTask"] = "triggerScheduledTask";
2183
+ WebviewMessageType["CancelTaskExecution"] = "cancelTaskExecution";
2184
+ WebviewMessageType["GetTaskExecutionLogs"] = "getTaskExecutionLogs";
2185
+ WebviewMessageType["ClearTaskExecutionLogs"] = "clearTaskExecutionLogs";
2186
+ WebviewMessageType["UpdateTaskExecutionLogs"] = "updateTaskExecutionLogs";
2187
+ WebviewMessageType["UpdateTaskExecutionState"] = "updateTaskExecutionState";
2188
+ WebviewMessageType["GetCurrentWorkspace"] = "getCurrentWorkspace";
2189
+ WebviewMessageType["UpdateCurrentWorkspace"] = "updateCurrentWorkspace";
2190
+ WebviewMessageType["GetProviders"] = "getProviders";
2191
+ WebviewMessageType["ProvidersResponse"] = "providersResponse";
2192
+ WebviewMessageType["AddProvider"] = "addProvider";
2193
+ WebviewMessageType["UpdateProvider"] = "updateProvider";
2194
+ WebviewMessageType["RemoveProvider"] = "removeProvider";
2195
+ WebviewMessageType["SetDefaultProvider"] = "setDefaultProvider";
2196
+ WebviewMessageType["SetProviderEnabled"] = "setProviderEnabled";
2197
+ WebviewMessageType["SetProviderOrder"] = "setProviderOrder";
2198
+ WebviewMessageType["TestProvider"] = "testProvider";
2199
+ WebviewMessageType["ProviderTestResultMessage"] = "providerTestResult";
2200
+ WebviewMessageType["TestProviderModel"] = "testProviderModel";
2201
+ WebviewMessageType["ProviderTestModelResultMessage"] = "providerTestModelResult";
2202
+ WebviewMessageType["DefaultProviderChanged"] = "defaultProviderChanged";
2203
+ WebviewMessageType["FetchProviderModels"] = "fetchProviderModels";
2204
+ WebviewMessageType["FetchProviderModelsResult"] = "fetchProviderModelsResult";
2205
+ WebviewMessageType["GetProviderUsage"] = "getProviderUsage";
2206
+ WebviewMessageType["ProviderUsageResponse"] = "providerUsageResponse";
2207
+ WebviewMessageType["SetCacheControlEnabled"] = "setCacheControlEnabled";
2208
+ WebviewMessageType["GetByomSettings"] = "getByomSettings";
2209
+ WebviewMessageType["ByomSettingsResponse"] = "byomSettingsResponse";
2210
+ WebviewMessageType["ListSkillRepoEntries"] = "listSkillRepoEntries";
2211
+ WebviewMessageType["GetSkillRepoEntry"] = "getSkillRepoEntry";
2212
+ WebviewMessageType["InstallSkillRepoEntry"] = "installSkillRepoEntry";
2213
+ WebviewMessageType["ConvertSkillRepoEntryToSymlink"] = "convertSkillRepoEntryToSymlink";
2214
+ WebviewMessageType["UninstallSkillRepoEntry"] = "uninstallSkillRepoEntry";
2215
+ WebviewMessageType["SetSkillRepoEntryEnabled"] = "setSkillRepoEntryEnabled";
2216
+ WebviewMessageType["ListLinkedSkills"] = "listLinkedSkills";
2217
+ WebviewMessageType["UpdateSkillRepoCatalog"] = "updateSkillRepoCatalog";
2218
+ WebviewMessageType["UpdateSkillRepoInstall"] = "updateSkillRepoInstall";
2219
+ WebviewMessageType["UpdateLinkedSkills"] = "updateLinkedSkills";
2220
+ WebviewMessageType["CreateSkillRepoDraft"] = "createSkillRepoDraft";
2221
+ WebviewMessageType["CommitSkillRepoDraft"] = "commitSkillRepoDraft";
2222
+ WebviewMessageType["ListSkillRepoDrafts"] = "listSkillRepoDrafts";
2223
+ WebviewMessageType["DeleteSkillRepoDraft"] = "deleteSkillRepoDraft";
2224
+ WebviewMessageType["UpdateSkillRepoDraft"] = "updateSkillRepoDraft";
2225
+ WebviewMessageType["ListRepositories"] = "listRepositories";
2226
+ WebviewMessageType["AddRepository"] = "addRepository";
2227
+ WebviewMessageType["UpdateRepository"] = "updateRepository";
2228
+ WebviewMessageType["RemoveRepository"] = "removeRepository";
2229
+ WebviewMessageType["EnableRepository"] = "enableRepository";
2230
+ WebviewMessageType["DisableRepository"] = "disableRepository";
2231
+ WebviewMessageType["SyncRepository"] = "syncRepository";
2232
+ WebviewMessageType["SyncAllRepositories"] = "syncAllRepositories";
2233
+ WebviewMessageType["ResetParseCache"] = "resetParseCache";
2234
+ WebviewMessageType["ListCopilotPlugins"] = "listCopilotPlugins";
2235
+ WebviewMessageType["RegisterCopilotPlugin"] = "registerCopilotPlugin";
2236
+ WebviewMessageType["UnregisterCopilotPlugin"] = "unregisterCopilotPlugin";
2237
+ WebviewMessageType["SetCopilotPluginEnabled"] = "setCopilotPluginEnabled";
2238
+ WebviewMessageType["GetCopilotContentStatus"] = "getCopilotContentStatus";
2239
+ WebviewMessageType["RestoreCopilotContent"] = "restoreCopilotContent";
2240
+ WebviewMessageType["ApproveCopilotContent"] = "approveCopilotContent";
2241
+ WebviewMessageType["UpdateRepositoryList"] = "updateRepositoryList";
2242
+ WebviewMessageType["UpdateRepositoryAdd"] = "updateRepositoryAdd";
2243
+ WebviewMessageType["UpdateRepositoryUpdate"] = "updateRepositoryUpdate";
2244
+ WebviewMessageType["UpdateRepositoryRemove"] = "updateRepositoryRemove";
2245
+ WebviewMessageType["UpdateRepositoryToggle"] = "updateRepositoryToggle";
2246
+ WebviewMessageType["UpdateRepositorySyncResult"] = "updateRepositorySyncResult";
2247
+ WebviewMessageType["UpdateCopilotContentStatus"] = "updateCopilotContentStatus";
2248
+ WebviewMessageType["GetCopilotCustomizations"] = "getCopilotCustomizations";
2249
+ WebviewMessageType["SyncCopilotCustomizations"] = "syncCopilotCustomizations";
2250
+ WebviewMessageType["UpdateCopilotCustomizations"] = "updateCopilotCustomizations";
2251
+ WebviewMessageType["InstallCopilotPackage"] = "installCopilotPackage";
2252
+ WebviewMessageType["MoveCopilotPackage"] = "moveCopilotPackage";
2253
+ WebviewMessageType["UninstallCopilotPackage"] = "uninstallCopilotPackage";
2254
+ WebviewMessageType["ListCopilotSources"] = "listCopilotSources";
2255
+ WebviewMessageType["RemoveCopilotSource"] = "removeCopilotSource";
2256
+ WebviewMessageType["PreviewCopilotUpdate"] = "previewCopilotUpdate";
2257
+ WebviewMessageType["UpdateCopilotPackage"] = "updateCopilotPackage";
2258
+ WebviewMessageType["PreviewCopilotLegacy"] = "previewCopilotLegacy";
2259
+ WebviewMessageType["MigrateCopilotLegacy"] = "migrateCopilotLegacy";
2260
+ WebviewMessageType["UpdateCopilotSources"] = "updateCopilotSources";
2261
+ WebviewMessageType["UpdateCopilotUpdatePreview"] = "updateCopilotUpdatePreview";
2262
+ WebviewMessageType["UpdateCopilotLegacyPreview"] = "updateCopilotLegacyPreview";
2263
+ WebviewMessageType["GetUtilityModels"] = "getUtilityModels";
2264
+ WebviewMessageType["UtilityModelsResponse"] = "utilityModelsResponse";
2265
+ WebviewMessageType["UpdateUtilityModels"] = "updateUtilityModels";
2266
+ WebviewMessageType["GetServerProxyState"] = "getServerProxyState";
2267
+ WebviewMessageType["ServerProxyStateResponse"] = "serverProxyStateResponse";
2268
+ WebviewMessageType["SetServerProxyEnabled"] = "setServerProxyEnabled";
2269
+ WebviewMessageType["SetServerProxyAllowOverride"] = "setServerProxyAllowOverride";
2270
+ WebviewMessageType["GetCachedServerUrl"] = "getCachedServerUrl";
2271
+ WebviewMessageType["CachedServerUrlResponse"] = "cachedServerUrlResponse";
2272
+ return WebviewMessageType;
2273
+ }({});
2274
+ /**
2275
+ * Top-level const aliases for the BYO Utility Models message-type
2276
+ * members above. Re-exported as `export const` (rather than just enum
2277
+ * members) because `@serviceme/devtools-shared` ships as CommonJS — bare
2278
+ * `import { GetUtilityModels } from "@serviceme/devtools-shared"` from an ESM
2279
+ * module resolves to `undefined` unless the binding is also exported
2280
+ * as a top-level const. The webview's vitest tests compare against
2281
+ * these by reference; without the const aliases, every
2282
+ * `c[0] === UpdateUtilityModels` check matches the mount-time
2283
+ * `vscode.post(GetUtilityModels)` call (because `undefined ===
2284
+ * undefined` is true). Keep both the enum members AND the consts in
2285
+ * sync; Task 5 (extension handler) uses the enum members, the webview
2286
+ * component + tests use the consts.
2287
+ */
2288
+ const GetUtilityModels = "getUtilityModels";
2289
+ const UtilityModelsResponse = "utilityModelsResponse";
2290
+ const UpdateUtilityModels = "updateUtilityModels";
2291
+ const SetCacheControlEnabled = "setCacheControlEnabled";
2292
+ const GetByomSettings = "getByomSettings";
2293
+ const ByomSettingsResponse = "byomSettingsResponse";
2294
+ const GetServerProxyState = "getServerProxyState";
2295
+ const ServerProxyStateResponse = "serverProxyStateResponse";
2296
+ const SetServerProxyEnabled = "setServerProxyEnabled";
2297
+ const SetServerProxyAllowOverride = "setServerProxyAllowOverride";
2298
+ const GetCachedServerUrl = "getCachedServerUrl";
2299
+ const CachedServerUrlResponse = "cachedServerUrlResponse";
2300
+ //#endregion
2301
+ //#region src/utils/safe-cast.ts
2302
+ /**
2303
+ * Environment-agnostic cast helpers.
2304
+ *
2305
+ * These centralize the handful of `as unknown as` / `JSON.parse` fallbacks
2306
+ * that used to be scattered across the codebase. They are intentionally
2307
+ * thin wrappers that preserve the exact runtime behavior of the original
2308
+ * inline casts — they exist for consistency and discoverability, not to
2309
+ * change semantics. No `protocol`-level imports are used here so the helpers
2310
+ * stay usable from any package (extension, server, webview) without pulling
2311
+ * in transport types.
2312
+ */
2313
+ /**
2314
+ * Narrow an unknown payload into a typed shape.
2315
+ *
2316
+ * Equivalent to `raw as T`. Retains the original "blind cast" semantics used
2317
+ * for scheduled-task payloads: callers own the contract and we do not validate
2318
+ * the runtime shape here. Keeping the cast in one place makes the intent
2319
+ * (and the assumption) explicit and grep-able.
2320
+ */
2379
2321
  function parsePayload(raw) {
2380
- return raw;
2322
+ return raw;
2381
2323
  }
2324
+ /**
2325
+ * Best-effort extraction of an `AbortSignal` from an inbound request object.
2326
+ *
2327
+ * The original code read `req.signal` via `req as unknown as { signal?: AbortSignal }`,
2328
+ * which would return whatever sat on `.signal` — including a non-`AbortSignal`
2329
+ * value. To avoid leaking an invalid signal into downstream `fetch`/`undici`
2330
+ * calls (where a non-`AbortSignal` signal throws), we only return the value when
2331
+ * it is a genuine `AbortSignal` instance; otherwise we return `undefined`, which
2332
+ * is the same as "no signal". In practice the request signal is always a real
2333
+ * `AbortSignal`, so behavior is unchanged for every production path.
2334
+ */
2382
2335
  function asAbortSignal(input) {
2383
- if (input !== null && typeof input === "object") {
2384
- const candidate = input.signal;
2385
- if (candidate instanceof AbortSignal) {
2386
- return candidate;
2387
- }
2388
- }
2389
- return void 0;
2336
+ if (input !== null && typeof input === "object") {
2337
+ const candidate = input.signal;
2338
+ if (candidate instanceof AbortSignal) return candidate;
2339
+ }
2390
2340
  }
2341
+ /**
2342
+ * Parse a JSON string, returning `fallback` when parsing fails.
2343
+ *
2344
+ * Equivalent to wrapping `JSON.parse(text)` in a try/catch. Used to replace
2345
+ * the previous `response.json().catch(() => ({}))` patterns (callers pair this
2346
+ * with their own `.catch` so that a body-read failure still yields the same
2347
+ * fallback as a malformed-body failure).
2348
+ */
2391
2349
  function safeJson(text, fallback) {
2392
- try {
2393
- return JSON.parse(text);
2394
- } catch {
2395
- return fallback;
2396
- }
2350
+ try {
2351
+ return JSON.parse(text);
2352
+ } catch {
2353
+ return fallback;
2354
+ }
2397
2355
  }
2398
- // Annotate the CommonJS export names for ESM import in node:
2399
- 0 && (module.exports = {
2400
- BUILTIN_PROVIDER_PRESETS,
2401
- ByomSettingsResponse,
2402
- CERTIFICATE_BUNDLE_FORMATS,
2403
- CachedServerUrlResponse,
2404
- DeviceAuthHeaders,
2405
- GIT_REMOTE_HOST_ALIASES,
2406
- GetByomSettings,
2407
- GetCachedServerUrl,
2408
- GetServerProxyState,
2409
- GetUtilityModels,
2410
- LISTABLE_PRESET_MODELS,
2411
- LogLevel,
2412
- MEDALSOFT_NUGET_PRIVATE_SOURCE,
2413
- MEDALSOFT_PRIVATE_GATEWAY_URL,
2414
- MODEL_METADATA,
2415
- NAMESPACE_ALIASES,
2416
- NAMESPACE_ALIAS_FAMILY,
2417
- PRESET_MODEL_FAMILIES,
2418
- PROVIDER_BASE_URL_PRESETS,
2419
- PROVIDER_CACHE_CONTROL_METADATA,
2420
- ServerProxyStateResponse,
2421
- SetCacheControlEnabled,
2422
- SetServerProxyAllowOverride,
2423
- SetServerProxyEnabled,
2424
- UpdateUtilityModels,
2425
- UtilityModelsResponse,
2426
- WebviewMessageType,
2427
- __internal,
2428
- asAbortSignal,
2429
- buildGitHubLocalEmail,
2430
- buildPresetModel,
2431
- checkGitHubOrgMembership,
2432
- createConsoleLogger,
2433
- createDeviceRequestSignature,
2434
- currencyForBaseUrl,
2435
- effectiveAdapterType,
2436
- fetchGitHubUser,
2437
- getBuiltinProviderPreset,
2438
- getGitHubOrgMembership,
2439
- getPresetModelDisplayName,
2440
- getProviderBaseUrlPresets,
2441
- isGitHubLocalEmail,
2442
- isProviderCacheControlAware,
2443
- isValidCanonicalSlug,
2444
- isWholePackageInstall,
2445
- listPresetModelGroups,
2446
- lookupModelMetadata,
2447
- normalizeCanonicalSlug,
2448
- normalizeErrorForLog,
2449
- normalizeGitUrl,
2450
- parsePayload,
2451
- protocolForBaseUrl,
2452
- registrationIdFromPackageId,
2453
- resolvePrimaryEmail,
2454
- safeJson,
2455
- unionProviderModelWithPreset
2456
- });
2356
+ //#endregion
2357
+ exports.BUILTIN_PROVIDER_PRESETS = BUILTIN_PROVIDER_PRESETS;
2358
+ exports.ByomSettingsResponse = ByomSettingsResponse;
2359
+ exports.CERTIFICATE_BUNDLE_FORMATS = CERTIFICATE_BUNDLE_FORMATS;
2360
+ exports.CachedServerUrlResponse = CachedServerUrlResponse;
2361
+ exports.DeviceAuthHeaders = DeviceAuthHeaders;
2362
+ exports.GIT_REMOTE_HOST_ALIASES = GIT_REMOTE_HOST_ALIASES;
2363
+ exports.GetByomSettings = GetByomSettings;
2364
+ exports.GetCachedServerUrl = GetCachedServerUrl;
2365
+ exports.GetServerProxyState = GetServerProxyState;
2366
+ exports.GetUtilityModels = GetUtilityModels;
2367
+ exports.LISTABLE_PRESET_MODELS = LISTABLE_PRESET_MODELS;
2368
+ exports.LogLevel = LogLevel;
2369
+ exports.MEDALSOFT_NUGET_PRIVATE_SOURCE = MEDALSOFT_NUGET_PRIVATE_SOURCE;
2370
+ exports.MEDALSOFT_PRIVATE_GATEWAY_URL = MEDALSOFT_PRIVATE_GATEWAY_URL;
2371
+ exports.MODEL_METADATA = MODEL_METADATA;
2372
+ exports.NAMESPACE_ALIASES = NAMESPACE_ALIASES;
2373
+ exports.NAMESPACE_ALIAS_FAMILY = NAMESPACE_ALIAS_FAMILY;
2374
+ exports.PRESET_MODEL_FAMILIES = PRESET_MODEL_FAMILIES;
2375
+ exports.PROVIDER_BASE_URL_PRESETS = PROVIDER_BASE_URL_PRESETS;
2376
+ exports.PROVIDER_CACHE_CONTROL_METADATA = PROVIDER_CACHE_CONTROL_METADATA;
2377
+ exports.ServerProxyStateResponse = ServerProxyStateResponse;
2378
+ exports.SetCacheControlEnabled = SetCacheControlEnabled;
2379
+ exports.SetServerProxyAllowOverride = SetServerProxyAllowOverride;
2380
+ exports.SetServerProxyEnabled = SetServerProxyEnabled;
2381
+ exports.UpdateUtilityModels = UpdateUtilityModels;
2382
+ exports.UtilityModelsResponse = UtilityModelsResponse;
2383
+ exports.WebviewMessageType = WebviewMessageType;
2384
+ exports.__internal = __internal;
2385
+ exports.asAbortSignal = asAbortSignal;
2386
+ exports.buildGitHubLocalEmail = buildGitHubLocalEmail;
2387
+ exports.buildPresetModel = buildPresetModel;
2388
+ exports.checkGitHubOrgMembership = checkGitHubOrgMembership;
2389
+ exports.createConsoleLogger = createConsoleLogger;
2390
+ exports.createDeviceRequestSignature = createDeviceRequestSignature;
2391
+ exports.currencyForBaseUrl = currencyForBaseUrl;
2392
+ exports.effectiveAdapterType = effectiveAdapterType;
2393
+ exports.fetchGitHubUser = fetchGitHubUser;
2394
+ exports.getBuiltinProviderPreset = getBuiltinProviderPreset;
2395
+ exports.getGitHubOrgMembership = getGitHubOrgMembership;
2396
+ exports.getPresetModelDisplayName = getPresetModelDisplayName;
2397
+ exports.getProviderBaseUrlPresets = getProviderBaseUrlPresets;
2398
+ exports.isGitHubLocalEmail = isGitHubLocalEmail;
2399
+ exports.isProviderCacheControlAware = isProviderCacheControlAware;
2400
+ exports.isValidCanonicalSlug = isValidCanonicalSlug;
2401
+ exports.isWholePackageInstall = isWholePackageInstall;
2402
+ exports.listPresetModelGroups = listPresetModelGroups;
2403
+ exports.lookupModelMetadata = lookupModelMetadata;
2404
+ exports.normalizeCanonicalSlug = normalizeCanonicalSlug;
2405
+ exports.normalizeErrorForLog = normalizeErrorForLog;
2406
+ exports.normalizeGitUrl = normalizeGitUrl;
2407
+ exports.parsePayload = parsePayload;
2408
+ exports.protocolForBaseUrl = protocolForBaseUrl;
2409
+ exports.registrationIdFromPackageId = registrationIdFromPackageId;
2410
+ exports.resolvePrimaryEmail = resolvePrimaryEmail;
2411
+ exports.safeJson = safeJson;
2412
+ exports.unionProviderModelWithPreset = unionProviderModelWithPreset;