@mars-sea/dsh-commandcode-provider 0.14.2 → 0.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,44 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.14.4] - 2026-10-20
8
+
9
+ ### Changed
10
+
11
+ - **同步 2026-10-20 的目录侧漂移(CLI 产物不变,仍是 1.84.0)。** 官方 CLI 的 npm `latest` 与变更日志本轮都没有新内容(`1.84.0` 于 2026-10-16T18:29Z 发布,日志顶部仍停在它),但实时模型目录在 10-19 晚变了:默认视图(`GET /provider/v1/models`)93 → 94 个 id,`?view=client` 101 → 102。快照随默认视图投影:`KNOWN_PLANS` 93 → 94、`KNOWN_EFFORTS` 65 → 66、`KNOWN_IMAGE_MODELS` 70 → 72、`KNOWN_DEALS` 7 → 7、`KNOWN_NON_ZDR_MODELS` 25 → 25;`COMMAND_CODE_CLI_VERSION` 与运行时代码不变(纯数据同步)。审计状态 `incomplete` 是预期形态:3 个 error 全是「目录比 1.84.0 产物新」的目录侧单源行(MiniMax M3.1 Flash 两行 + 既有的 `reflection/beam:free`),不是抓取失败。
12
+
13
+ ### Added
14
+
15
+ - **MiniMax M3.1 Flash:付费 `minimax/minimax-m3.1-flash` 与限时免费 `minimax/minimax-m3.1-flash:free`。** 100 万上下文、文本 + 图像(Vision)、五档推理强度(`low`/`medium`/`high`/`xhigh`/`max`)、ZDR 覆盖、Go 起(全部套餐可见)。付费行 $0.15 输入/$0.60 输出/$0.015 缓存读(每百万 token,Go $6/GOAT $20/Pro $30 月额度);免费行 100% off,促销期为 `2026-10-19T11:00Z` → `2026-10-21T10:45Z`(北京时间 10-19 19:00 → 10-21 18:45),官方说明「限时、无每日请求上限、账户需有 $1 余额」。**免费行是 `KNOWN_DEALS` 里第一个带 `expiresAt` 的免费促销**:到期后 FREE 徽标由 `dealLabel()` 自动消失,`:free` 行仍按 id 后缀 0 价,不会把过期折扣当成现价。
16
+
17
+ - **定价页渲染表的「同一显示名多行」解析。** 上游自该模型上线起把付费行与免费行用同一个显示名 `MiniMax M3.1 Flash` 发布,`scripts/sync-model-prices.mjs` 原先「一个显示名一行」的唯一键假设因此让 `--check` 直接失败(`价格表模型名称重复,无法确定对应行:minimax m3.1 flash`)。现改为:渲染行按显示名分组,再按「免费 / 数值」形态唯一配对——同名同形态仍然拒绝生成,配到数值行后照旧逐项交叉校验三档费率与划线价。价格表随之新增一行 `minimax-m3.1-flash`(92 → 93 行)。同时给 `scripts/sync-output-limits.mjs` 的供应商映射补上小写 `minimax` 命名空间:新模型的目录 id 用的是小写前缀,缺这一项会退回聚合共识,把付费 id 的输出上限丢给运行时学习;补齐后两个 id 都落到供应商行(512000),表 78 → 80 行。
18
+
19
+ ### Removed
20
+
21
+ - **Stealth Cat Jam 预览(`stealth/cat-jam:free`)从模型表下线。** 2026-10-20 检查时两个目录视图、定价页、模型文档页与 Go 套餐页都不再服务该行;`KNOWN_PLANS`/`KNOWN_EFFORTS`/`KNOWN_IMAGE_MODELS`/`KNOWN_DEALS` 移除,`tests/adapter.test.ts` 的免费模型排序夹具改用 Glyph Cluster。**保留在 `KNOWN_NON_ZDR_MODELS`**:1.84.0 产物的 ZDR 排除集仍点名它,而该表是「artifact 排除集 ∪ 目录 `zdr:false`」的并集,删条目会让老会话的 ZDR 判定偏乐观(判据见[《决策记录》第七章第 13 条](docs/决策记录.md#七上游快照同步边界))。集合仍为 25 条。
22
+
23
+ ## [0.14.3] - 2026-10-19
24
+
25
+ ### Changed
26
+
27
+ - **发布包不再携带 sourcemap,体积减少约 16%(tarball 2.9 MB → 2.5 MB,解包 5.1 MB → 3.5 MB;文件数 18 → 16)。** `tsdown.config.ts` 的 `sourcemap: true` 会产出 `lib/index.js.map` 与 `lib/client.js.map`,而 `files` 直接收整个 `lib`,两份 map(1.63 MiB)因此进了 tarball。它们没有任何读取方:`lib/client.js` 是 WebView 直接执行的 CJS 包,`//# sourceMappingURL` 只是尾部注释;`scripts/verify-engine-load.mjs` 的审计用正则匹配 `import`/`require` **语句**,不匹配注释。本地 `npm run build` 照旧产出 map,调试能力不受影响。**用户侧无需做任何事**,运行时行为与公开导出面零变化(`lib/index.d.ts` 的 `export { ... }` 一行未改)。
28
+
29
+ - **长会话的历史分析报告更快(约 −28%~−33%)。** `buildAnalytics` 原先有两处对「事实 × 模型」的重复扫描:每模型小计对整表过滤 N 次,场景矩阵对每个(事实 × 目录模型)都查一次候选价格行(96 个模型共 480,000 次查找,查的是同一批行)。现改为先按模型分桶一次,并把定价算式收进 `cost-facts.requestCost()`、让场景循环把查价提到内层循环之外;`requestRates` 增加可选的高峰时段 memo(由调用方持有,不会活得比它的价格表更久)。两套独立测量给出同一量级:PR 作者脚本 5,000 条事实 × 96 个目录模型 42.17 ms → 30.36 ms,维护者本机用另一套合成数据复测 14.87 ms → 9.90 ms。**输出逐位不变**:分桶顺序与原 `Set` 顺序一致,排序本来就是按唯一 id 的全序(维护者另用 3,000 条确定性事实 × 96 个模型对比两版 JSON 输出,147,944 字节完全一致)。
30
+
31
+ - **图片请求里同一张图只编码一次 base64。** `readImageRequest()` 对每个(附件,编码目标)返回同一个缓存对象,但一条请求里同一张图会出现在多条历史消息中;三个 wire 编解码器原先各自按「出现次数」执行 `Buffer.from(version.data).toString('base64')`(1 MiB 载荷 0.738 ms/次)。现改为以该缓存对象为键的 `WeakMap`,保存原始 base64 与 data URL,副本生命周期跟随宿主自己的版本缓存。命中约 21 ns。
32
+
33
+ - **「查看模型用量、成本与性能分析」入口移到「账户用量」分组标题行**,紧邻「刷新」。它原先裸放在页面说明段与第一个分组之间,既无外边距也不属于任何分组,视觉上像是游离在版式之外;移到「账户用量」标题行后与服务着同一批用量/成本数据的「刷新」并列,也复用了该行既有的 `cc-spacer` 布局,不新增样式类。分析入口仍只在 Host 暴露面板接缝时出现,行为不变(切到面板的「模型分析」页签并打开面板)。
34
+
35
+ - **多账户轮换说明精简 2 处冗余用词**(中文 47 → 43 字,英文同步)。`账户操作立即生效。当前账户达到用量限额或密钥失效时,请求会按列表顺序自动切换到下一个可用账户。` → 去掉「用量」与「自动」后为:`账户操作立即生效。当前账户达到限额或密钥失效时,请求会按列表顺序切换到下一个可用账户。` 「按列表顺序切换」本身已表达自动性,`达到限额` 在账户上下文里仍是额度含意;英文侧 `usage limit` → `limit`,`switch` 原本就没有 `automatically`。**已知取舍**:同页单账户文案 `accountsHint` 仍用「用量限额」,两条说明的术语因此不完全一致。
36
+
37
+ ### Added
38
+
39
+ - **新增 `.gitattributes`:全部文本文件统一按 LF 检出与提交。** 仓库此前没有该文件,Windows 贡献者按 CRLF 检出后,sourcemap 的 `sourcesContent` 会内嵌带 CRLF 的源码,重建出的 `.map` 与提交里的不再逐字节相同,CI 的 `git diff --exit-code -- lib/` 守卫因此误报「产物过期」(2026-10-17 那次 CI 失败的根因)。实测对照:无该文件时 `core.autocrlf=true` 检出后 `src/adapter.ts` 有 3,482 行 CRLF、`lib/index.js` 有 11,817 行;加上后两者均为 0。图片与压缩包按 `binary` 排除在行尾转换之外。**本次加入时已跟踪文件 100% 是 LF,`git add --renormalize .` 未改写任何文件。**
40
+
41
+ ### Removed
42
+
43
+ - 两份 sourcemap(`lib/index.js.map`、`lib/client.js.map`)不再纳入版本控制。它们是 tsdown 产物里唯一在 CRLF 工作树上无法逐字节重建的文件,跟踪它们只会让「产物过期」守卫依赖工作树的行尾;移出 tarball 与版本控制后,该守卫在任意平台都只反映源码与产物的真实一致性。
44
+
7
45
  ## [0.14.2] - 2026-10-18
8
46
 
9
47
  ### Changed
package/lib/client.js CHANGED
@@ -3237,7 +3237,7 @@ window.__ModuleLoader__.load({
3237
3237
  cardRouteActive: "已启用",
3238
3238
  cardLoadingHint: "正在读取 Command Code 配置…",
3239
3239
  cardRegistrationHint: "此卡片由 Command Code 插件提供,正在等待设置页装配,稍后即可显示完整内容。",
3240
- accountsRotationHint: "账户操作立即生效。当前账户达到用量限额或密钥失效时,请求会按列表顺序自动切换到下一个可用账户。",
3240
+ accountsRotationHint: "账户操作立即生效。当前账户达到限额或密钥失效时,请求会按列表顺序切换到下一个可用账户。",
3241
3241
  accountModeAuto: "使用方式:自动切换(按列表顺序)",
3242
3242
  accountModePinned: "使用方式:固定使用「{name}」,不可用时仍会自动切换",
3243
3243
  accountStatusPinned: "已固定",
@@ -3463,7 +3463,7 @@ window.__ModuleLoader__.load({
3463
3463
  cardRouteActive: "Active",
3464
3464
  cardLoadingHint: "Loading the Command Code configuration…",
3465
3465
  cardRegistrationHint: "This card is contributed by the Command Code plugin and is waiting for the settings page to finish assembling.",
3466
- accountsRotationHint: "Account actions apply immediately. When the current account hits its usage limit or its key stops working, requests switch to the next usable account in list order.",
3466
+ accountsRotationHint: "Account actions apply immediately. When the current account hits its limit or its key stops working, requests switch to the next usable account in list order.",
3467
3467
  accountModeAuto: "Mode: switch automatically (list order)",
3468
3468
  accountModePinned: "Mode: pinned to \"{name}\", still switching when it is unavailable",
3469
3469
  accountStatusPinned: "Pinned",
@@ -3704,7 +3704,7 @@ window.__ModuleLoader__.load({
3704
3704
  }
3705
3705
  //#endregion
3706
3706
  //#region package.json
3707
- var version = "0.14.2";
3707
+ var version = "0.14.4";
3708
3708
  var repository = {
3709
3709
  "type": "git",
3710
3710
  "url": "git+https://github.com/Mars-Sea/dsh-commandcode-provider.git"
@@ -5554,7 +5554,7 @@ window.__ModuleLoader__.load({
5554
5554
  * each, with its status and quota inline — so one account's facts no longer
5555
5555
  * have to be cross-read across separate surfaces.
5556
5556
  */
5557
- function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, createEnrollment }) {
5557
+ function AccountsCard({ t, state, usage, login, disabled, actions, onRefresh, openAnalytics, createEnrollment }) {
5558
5558
  const [adding, setAdding] = (0, react.useState)(false);
5559
5559
  const [enrollment] = (0, react.useState)(createEnrollment);
5560
5560
  const snapshot = (0, react.useSyncExternalStore)(enrollment.store.subscribe, enrollment.store.getSnapshot);
@@ -5582,6 +5582,12 @@ window.__ModuleLoader__.load({
5582
5582
  children: t("accountsTitle")
5583
5583
  }),
5584
5584
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cc-spacer" }),
5585
+ openAnalytics ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5586
+ variant: "ghost",
5587
+ size: "sm",
5588
+ onClick: openAnalytics,
5589
+ children: t("openAnalytics")
5590
+ }) : null,
5585
5591
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5586
5592
  type: "button",
5587
5593
  className: "cc-linkButton",
@@ -5929,12 +5935,6 @@ window.__ModuleLoader__.load({
5929
5935
  className: "cc-intro",
5930
5936
  children: t("intro")
5931
5937
  }),
5932
- props.openAnalytics ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
5933
- variant: "ghost",
5934
- size: "sm",
5935
- onClick: props.openAnalytics,
5936
- children: t("openAnalytics")
5937
- }) : null,
5938
5938
  !state.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5939
5939
  className: "cc-readOnly",
5940
5940
  role: "status",
@@ -5948,6 +5948,7 @@ window.__ModuleLoader__.load({
5948
5948
  disabled,
5949
5949
  actions,
5950
5950
  onRefresh: props.refreshUsage,
5951
+ openAnalytics: props.openAnalytics,
5951
5952
  createEnrollment: props.createEnrollment
5952
5953
  }),
5953
5954
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelsCard, {
@@ -8376,10 +8377,16 @@ window.__ModuleLoader__.load({
8376
8377
  * today, so the combination becoming real is visible before it reaches a user.
8377
8378
  * This ordering is a latent choice, not a verified upstream rule.
8378
8379
  */
8379
- function requestRates(price, at, contextTokens, table) {
8380
+ function requestRates(price, at, contextTokens, table, peakHourCache) {
8380
8381
  const tier = price.contextTiers?.find((tier) => tier.maxContext === void 0 || contextTokens <= tier.maxContext);
8381
8382
  if (tier !== void 0) return tier;
8382
- return price.peak !== void 0 && at !== null && peakHour(at, table.peakHours) ? price.peak : price;
8383
+ if (price.peak === void 0 || at === null) return price;
8384
+ let peak = peakHourCache?.get(at);
8385
+ if (peak === void 0) {
8386
+ peak = peakHour(at, table.peakHours);
8387
+ peakHourCache?.set(at, peak);
8388
+ }
8389
+ return peak ? price.peak : price;
8383
8390
  }
8384
8391
  /** Cache version and wire guard: changing bands/rates must refold historical groups. */
8385
8392
  function pricingKey(table) {
package/lib/index.d.ts CHANGED
@@ -207,7 +207,13 @@ interface CommandCodeConnectionOptions {
207
207
  * text-only request never depends on the attachment seam.
208
208
  */
209
209
  type ResolveAttachments = () => AttachmentStore | undefined;
210
- /** What the rotation hook knows about the request it is rotating within. */
210
+ /**
211
+ * What the rotation hook knows about the request it is rotating within.
212
+ *
213
+ * Not exported: the only consumer is the `rotateApiKey` shape below, so the
214
+ * name never reaches the published `.d.ts` and the package's `exports` map
215
+ * leaves no deep-import path to it.
216
+ */
211
217
  interface AccountRotationContext {
212
218
  /** Every API key this request has already used, just-rejected key included. */
213
219
  tried: readonly string[];
@@ -265,13 +271,13 @@ interface CommandCodeAdapterDeps<C extends CommandCodeConnectionOptions = Comman
265
271
  */
266
272
  imageOffload?: SurfaceImagePolicy;
267
273
  }
268
- /** Account identity from `/alpha/whoami`. */
274
+ /** Account identity from `/alpha/whoami`; reached only through `CommandCodeUsageReport`. */
269
275
  interface CommandCodeAccount {
270
276
  id: string;
271
277
  name: string;
272
278
  userName: string;
273
279
  }
274
- /** Usage summary from `/alpha/usage/summary`. */
280
+ /** Usage summary from `/alpha/usage/summary`; reached only through `CommandCodeUsageReport`. */
275
281
  interface CommandCodeUsage {
276
282
  totalCount: number;
277
283
  totalCost: number;
@@ -326,7 +332,7 @@ interface CommandCodeCredits {
326
332
  resetAt: number;
327
333
  };
328
334
  }
329
- /** Subscription plan state from `/alpha/billing/subscriptions`. */
335
+ /** Subscription plan state from `/alpha/billing/subscriptions`; reached only through `CommandCodeUsageReport`. */
330
336
  interface CommandCodePlan {
331
337
  /** Raw subscription plan id (e.g. `individual-pro`); empty when unreported. */
332
338
  planId: string;
@@ -1041,8 +1047,10 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
1041
1047
  * `minimax/minimax-m3-free` is the one entry the public catalog's default view
1042
1048
  * (`/provider/v1/models`) does not serve through the picker (it is CLI/pricing-visible
1043
1049
  * and hidden from the picker); it stays listed because the CLI carries it and a Go-plan
1044
- * request can still name it. Both this row and `stealth/cat-jam:free` ARE served
1045
- * by the `?view=client` catalog, which is where the 1.82.4 verdict comes from.
1050
+ * request can still name it. At the 1.84.0 sync both this row and
1051
+ * `stealth/cat-jam:free` were also served by the `?view=client` catalog, which
1052
+ * is where the 1.82.4 verdict came from; the catalog stopped serving cat-jam on
1053
+ * 2026-10-20, so the artifact's exclusion set is now its only evidence.
1046
1054
  *
1047
1055
  * command-code@1.82.4 moved the authority for this verdict: `knownModelSupportsZdr`
1048
1056
  * no longer negates a bundled Set, it returns `resolveKnownCatalogModel({model,
@@ -1080,6 +1088,12 @@ declare const KNOWN_IMAGE_MODELS: ReadonlySet<string>;
1080
1088
  * UNION of the artifact's exclusion set and the catalog's `zdr:false` list —
1081
1089
  * neither source alone is complete, which is why this set is not simply "what
1082
1090
  * the CLI says".
1091
+ *
1092
+ * The 2026-10-20 catalog-only sync changes no membership: `stealth/cat-jam:free`
1093
+ * left both catalog views (its `zdr:false` verdict is no longer served), but the
1094
+ * 1.84.0 artifact's exclusion set still names it and this set is the union, so
1095
+ * membership stays at 25. The MiniMax M3.1 Flash launch pair is ZDR-covered
1096
+ * (`zdr:true` on both ids) and joins no set.
1083
1097
  */
1084
1098
  declare const KNOWN_NON_ZDR_MODELS: ReadonlySet<string>;
1085
1099
  /**
package/lib/index.js CHANGED
@@ -38,10 +38,30 @@ function peakHour(at, windows) {
38
38
  * today, so the combination becoming real is visible before it reaches a user.
39
39
  * This ordering is a latent choice, not a verified upstream rule.
40
40
  */
41
- function requestRates(price, at, contextTokens, table) {
41
+ function requestRates(price, at, contextTokens, table, peakHourCache) {
42
42
  const tier = price.contextTiers?.find((tier) => tier.maxContext === void 0 || contextTokens <= tier.maxContext);
43
43
  if (tier !== void 0) return tier;
44
- return price.peak !== void 0 && at !== null && peakHour(at, table.peakHours) ? price.peak : price;
44
+ if (price.peak === void 0 || at === null) return price;
45
+ let peak = peakHourCache?.get(at);
46
+ if (peak === void 0) {
47
+ peak = peakHour(at, table.peakHours);
48
+ peakHourCache?.set(at, peak);
49
+ }
50
+ return peak ? price.peak : price;
51
+ }
52
+ /**
53
+ * Price one request's token buckets against an already-resolved price row.
54
+ *
55
+ * Split out of the report's per-model lookup so a caller that prices the same
56
+ * candidate model against every fact — the analytics scenario matrix — resolves
57
+ * the price row once per model instead of once per (fact, model) pair. The
58
+ * arithmetic lives here alone so the two callers cannot drift.
59
+ */
60
+ function requestCost(tokens, price, at, table, peakHourCache) {
61
+ if (price.free === true) return 0;
62
+ const r = requestRates(price, at, tokens.uncachedInputTokens + tokens.cacheReadTokens + tokens.cacheWriteTokens, table, peakHourCache);
63
+ if (tokens.cacheWriteTokens > 0 && r.cacheWriteCost === void 0) return void 0;
64
+ return (tokens.uncachedInputTokens * r.inputCost + tokens.outputTokens * r.outputCost + tokens.cacheReadTokens * r.cacheReadCost + tokens.cacheWriteTokens * (r.cacheWriteCost ?? 0)) / 1e6;
45
65
  }
46
66
  /** Cache version and wire guard: changing bands/rates must refold historical groups. */
47
67
  function pricingKey(table) {
@@ -103,9 +123,13 @@ const PLAN_ORDER = Object.fromEntries(PLAN_TIER_ORDER.map((tier, index) => [tier
103
123
  * subscription-plan labels, deals, and hourly (peak/off-peak) pricing.
104
124
  *
105
125
  * Everything here is synced from official sources — the command-code CLI
106
- * bundle's model table (`dist/cli.mjs`, re-verified at command-code@1.84.0) and
107
- * the official plan/pricing/model docs; see the dsh-commandcode-upstream skill
108
- * for the extraction procedures. Keeping the snapshot in its own module
126
+ * bundle's model table (`dist/cli.mjs`, re-verified at command-code@1.84.0), the
127
+ * live model catalog (`GET /provider/v1/models?view=client`, the authority for
128
+ * capability fields since 1.82.4), and the official plan/pricing/model docs; see
129
+ * the dsh-commandcode-upstream skill for the extraction procedures. The
130
+ * 2026-10-20 sync is catalog-only: the CLI artifact stayed at 1.84.0 while the
131
+ * catalog added the MiniMax M3.1 Flash launch pair and dropped the Stealth Cat
132
+ * Jam preview. Keeping the snapshot in its own module
109
133
  * confines those frequent sync diffs here: src/adapter.ts holds only the stable
110
134
  * wire/runtime logic. The read helpers live here too, for the same reason.
111
135
  *
@@ -446,6 +470,20 @@ const KNOWN_EFFORTS = {
446
470
  "medium",
447
471
  "high"
448
472
  ],
473
+ "minimax/minimax-m3.1-flash": [
474
+ "low",
475
+ "medium",
476
+ "high",
477
+ "xhigh",
478
+ "max"
479
+ ],
480
+ "minimax/minimax-m3.1-flash:free": [
481
+ "low",
482
+ "medium",
483
+ "high",
484
+ "xhigh",
485
+ "max"
486
+ ],
449
487
  "gpt-6.1-sol": [
450
488
  "low",
451
489
  "medium",
@@ -475,13 +513,6 @@ const KNOWN_EFFORTS = {
475
513
  "medium",
476
514
  "high",
477
515
  "xhigh"
478
- ],
479
- "stealth/cat-jam:free": [
480
- "low",
481
- "medium",
482
- "high",
483
- "xhigh",
484
- "max"
485
516
  ]
486
517
  };
487
518
  /**
@@ -551,6 +582,8 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
551
582
  "meta/muse-spark-open",
552
583
  "nvidia/nemotron-3.5-super-vl",
553
584
  "mistral/mistral-large-4",
585
+ "minimax/minimax-m3.1-flash",
586
+ "minimax/minimax-m3.1-flash:free",
554
587
  "moonshotai/Kimi-K2.5",
555
588
  "moonshotai/Kimi-K2.6",
556
589
  "moonshotai/Kimi-K2.7-Code",
@@ -597,8 +630,10 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
597
630
  * `minimax/minimax-m3-free` is the one entry the public catalog's default view
598
631
  * (`/provider/v1/models`) does not serve through the picker (it is CLI/pricing-visible
599
632
  * and hidden from the picker); it stays listed because the CLI carries it and a Go-plan
600
- * request can still name it. Both this row and `stealth/cat-jam:free` ARE served
601
- * by the `?view=client` catalog, which is where the 1.82.4 verdict comes from.
633
+ * request can still name it. At the 1.84.0 sync both this row and
634
+ * `stealth/cat-jam:free` were also served by the `?view=client` catalog, which
635
+ * is where the 1.82.4 verdict came from; the catalog stopped serving cat-jam on
636
+ * 2026-10-20, so the artifact's exclusion set is now its only evidence.
602
637
  *
603
638
  * command-code@1.82.4 moved the authority for this verdict: `knownModelSupportsZdr`
604
639
  * no longer negates a bundled Set, it returns `resolveKnownCatalogModel({model,
@@ -636,6 +671,12 @@ const KNOWN_IMAGE_MODELS = /* @__PURE__ */ new Set([
636
671
  * UNION of the artifact's exclusion set and the catalog's `zdr:false` list —
637
672
  * neither source alone is complete, which is why this set is not simply "what
638
673
  * the CLI says".
674
+ *
675
+ * The 2026-10-20 catalog-only sync changes no membership: `stealth/cat-jam:free`
676
+ * left both catalog views (its `zdr:false` verdict is no longer served), but the
677
+ * 1.84.0 artifact's exclusion set still names it and this set is the union, so
678
+ * membership stays at 25. The MiniMax M3.1 Flash launch pair is ZDR-covered
679
+ * (`zdr:true` on both ids) and joins no set.
639
680
  */
640
681
  const KNOWN_NON_ZDR_MODELS = /* @__PURE__ */ new Set([
641
682
  "MiniMaxAI/MiniMax-M3",
@@ -832,6 +873,8 @@ const MODEL_OUTPUT_TOKEN_LIMITS = /* @__PURE__ */ new Map([
832
873
  ["meta/muse-spark-1.3", 131072],
833
874
  ["meta/muse-spark-1.3-contributor", 131072],
834
875
  ["meta/muse-wonder-1.0", 131072],
876
+ ["minimax/minimax-m3.1-flash", 512e3],
877
+ ["minimax/minimax-m3.1-flash:free", 512e3],
835
878
  ["MiniMaxAI/MiniMax-M2.5", 131072],
836
879
  ["MiniMaxAI/MiniMax-M2.7", 131072],
837
880
  ["MiniMaxAI/MiniMax-M3", 512e3],
@@ -930,6 +973,8 @@ const KNOWN_PLANS = {
930
973
  "meta/muse-spark-1.3-contributor": "go",
931
974
  "meta/muse-spark-open": "go",
932
975
  "mistral/mistral-large-4": "go",
976
+ "minimax/minimax-m3.1-flash": "go",
977
+ "minimax/minimax-m3.1-flash:free": "go",
933
978
  "moonshotai/Kimi-K2.5": "go",
934
979
  "moonshotai/Kimi-K2.6": "go",
935
980
  "moonshotai/Kimi-K2.7-Code": "go",
@@ -942,7 +987,6 @@ const KNOWN_PLANS = {
942
987
  "stepfun/Step-3.5-Flash": "go",
943
988
  "stepfun/Step-3.7-Flash": "go",
944
989
  "stepfun/Step-5-Preview": "go",
945
- "stealth/cat-jam:free": "go",
946
990
  "stealth/glyph-cluster:free": "go",
947
991
  "tencent/hy3-paid": "go",
948
992
  "tencent/hy4-preview": "go",
@@ -1129,6 +1173,11 @@ function modelVisibleForAnyAccount(modelId, accounts) {
1129
1173
  }
1130
1174
  const KNOWN_DEALS = {
1131
1175
  "MiniMaxAI/MiniMax-M3": { label: "50% off" },
1176
+ "minimax/minimax-m3.1-flash:free": {
1177
+ label: "FREE",
1178
+ free: true,
1179
+ expiresAt: "2026-10-21T10:45:00Z"
1180
+ },
1132
1181
  "xiaomi/mimo-v2.5-pro": { label: "99% off" },
1133
1182
  "xiaomi/mimo-v2.5": { label: "98% off" },
1134
1183
  "poolside/laguna-s-2.1-free": {
@@ -1139,10 +1188,6 @@ const KNOWN_DEALS = {
1139
1188
  label: "FREE",
1140
1189
  free: true
1141
1190
  },
1142
- "stealth/cat-jam:free": {
1143
- label: "FREE",
1144
- free: true
1145
- },
1146
1191
  "stealth/glyph-cluster:free": {
1147
1192
  label: "FREE",
1148
1193
  free: true
@@ -2352,6 +2397,19 @@ const MODEL_PRICE_ROWS = [
2352
2397
  pro: 57
2353
2398
  }
2354
2399
  },
2400
+ {
2401
+ id: "minimax-m3.1-flash",
2402
+ rates: [
2403
+ .15,
2404
+ .6,
2405
+ .015
2406
+ ],
2407
+ allowance: {
2408
+ go: 6,
2409
+ goat: 20,
2410
+ pro: 30
2411
+ }
2412
+ },
2355
2413
  {
2356
2414
  id: "mistral-large-4",
2357
2415
  rates: [
@@ -3257,6 +3315,7 @@ function foldRequests(session, log) {
3257
3315
  }
3258
3316
  return facts;
3259
3317
  }
3318
+ /** The zeroed accumulator `aggregate` adds into. Internal: nothing outside this module reads it. */
3260
3319
  const emptyMetrics = () => ({
3261
3320
  sessions: 0,
3262
3321
  requests: 0,
@@ -3277,17 +3336,13 @@ const emptyMetrics = () => ({
3277
3336
  decodeMs: 0,
3278
3337
  decodeTokens: 0
3279
3338
  });
3280
- function priceRequest(f, id, table, prices) {
3339
+ function priceRequest(f, id, table, prices, peakHourCache) {
3281
3340
  if (!f.tokens) return void 0;
3282
3341
  const price = prices.get(id);
3283
3342
  if (!price) return void 0;
3284
- if (price.free === true) return 0;
3285
- const context = f.tokens.uncachedInputTokens + f.tokens.cacheReadTokens + f.tokens.cacheWriteTokens;
3286
- const r = requestRates(price, f.at, context, table);
3287
- if (f.tokens.cacheWriteTokens > 0 && r.cacheWriteCost === void 0) return void 0;
3288
- return (f.tokens.uncachedInputTokens * r.inputCost + f.tokens.outputTokens * r.outputCost + f.tokens.cacheReadTokens * r.cacheReadCost + f.tokens.cacheWriteTokens * (r.cacheWriteCost ?? 0)) / 1e6;
3343
+ return requestCost(f.tokens, price, f.at, table, peakHourCache);
3289
3344
  }
3290
- function aggregate(facts, table, prices) {
3345
+ function aggregate(facts, table, prices, peakHourCache) {
3291
3346
  const m = emptyMetrics();
3292
3347
  m.sessions = new Set(facts.map((f) => f.session)).size;
3293
3348
  for (const f of facts) {
@@ -3298,7 +3353,7 @@ function aggregate(facts, table, prices) {
3298
3353
  m.usageSamples++;
3299
3354
  for (const k of COST_TOKEN_KEYS) m.tokens[k] += f.tokens[k];
3300
3355
  }
3301
- const cost = priceRequest(f, f.model, table, prices);
3356
+ const cost = priceRequest(f, f.model, table, prices, peakHourCache);
3302
3357
  if (cost !== void 0) {
3303
3358
  m.pricedRequests++;
3304
3359
  m.cost += cost;
@@ -3323,13 +3378,21 @@ function buildAnalytics(facts, query, table, catalog, now = Date.now()) {
3323
3378
  const selected = query.mode === "range" ? facts.filter((f) => f.at >= query.from && f.at < query.to) : facts;
3324
3379
  const prices = /* @__PURE__ */ new Map();
3325
3380
  for (const p of table.models) for (const id of [p.id, p.slug]) if (!prices.has(id)) prices.set(id, p);
3326
- const totals = aggregate(selected, table, prices);
3327
- const models = [...new Set(selected.map((f) => f.model))].map((id) => ({
3381
+ const peakHourCache = /* @__PURE__ */ new Map();
3382
+ const totals = aggregate(selected, table, prices, peakHourCache);
3383
+ const byModel = /* @__PURE__ */ new Map();
3384
+ for (const f of selected) {
3385
+ const bucket = byModel.get(f.model);
3386
+ if (bucket === void 0) byModel.set(f.model, [f]);
3387
+ else bucket.push(f);
3388
+ }
3389
+ const models = [...byModel].map(([id, bucket]) => ({
3328
3390
  id,
3329
- ...aggregate(selected.filter((f) => f.model === id), table, prices)
3391
+ ...aggregate(bucket, table, prices, peakHourCache)
3330
3392
  })).sort((a, b) => b.cost - a.cost || a.id.localeCompare(b.id));
3331
3393
  const scenarios = catalog.map((model) => {
3332
3394
  let cost = 0, pricedRequests = 0, excludedRequests = 0, unknownRequests = 0;
3395
+ const price = prices.get(model.id);
3333
3396
  for (const f of selected) {
3334
3397
  if (!f.tokens) {
3335
3398
  unknownRequests++;
@@ -3340,7 +3403,7 @@ function buildAnalytics(facts, query, table, catalog, now = Date.now()) {
3340
3403
  excludedRequests++;
3341
3404
  continue;
3342
3405
  }
3343
- const priced = priceRequest(f, model.id, table, prices);
3406
+ const priced = price === void 0 ? void 0 : requestCost(f.tokens, price, f.at, table, peakHourCache);
3344
3407
  if (priced === void 0) unknownRequests++;
3345
3408
  else {
3346
3409
  cost += priced;
@@ -6307,9 +6370,16 @@ const DEFAULT_API_BASE = "https://api.commandcode.ai";
6307
6370
  * under the historical name so existing importers are unaffected.
6308
6371
  */
6309
6372
  const DEFAULT_GENERATE_MAX_TOKENS = MAX_OUTPUT_TOKEN_LIMIT;
6373
+ /** Catalog fetch budget. Internal: both call sites are in this module. */
6310
6374
  const MODELS_TIMEOUT_MS = 1e4;
6311
6375
  /** How long the picker's plan-filter billing facts stay cached before refetching. */
6312
6376
  const BILLING_ACCESS_TTL_MS = 3e5;
6377
+ /**
6378
+ * How long a learned CLI-only protocol preference stays cached per account.
6379
+ * After this TTL a request may probe Provider API again, so an upgraded Go
6380
+ * account can recover without a restart.
6381
+ */
6382
+ const PROTOCOL_CACHE_TTL_MS = 9e5;
6313
6383
  /** The entry-plan tier weight (`individual-go` in KNOWN_SUBSCRIPTION_PLANS). */
6314
6384
  const GO_TIER_WEIGHT = 0;
6315
6385
  /** Hard cap on account rotations within one request (one attempt per distinct key). */
@@ -6960,6 +7030,31 @@ function priceRequestImage(block, model, imageCapable) {
6960
7030
  };
6961
7031
  }
6962
7032
  /**
7033
+ * Base64 payloads keyed by the cached request version itself.
7034
+ *
7035
+ * `readImageRequest()` returns one codec object per (attachment, encoder
7036
+ * target) and `requestImages()` reuses that same object for every occurrence in
7037
+ * one generation, but a history that carries an image also replays it in every
7038
+ * later message. Encoding per occurrence therefore re-base64s the same
7039
+ * multi-megabyte buffer once per replay — the single most expensive
7040
+ * per-occurrence step of an image request. Keying on the object makes an
7041
+ * occurrence a map hit, and the WeakMap ties the copy's lifetime to the
7042
+ * harness's own version cache rather than to this plugin.
7043
+ */
7044
+ const encodedRequestImages = /* @__PURE__ */ new WeakMap();
7045
+ function encodedRequestImage(version) {
7046
+ let encoded = encodedRequestImages.get(version);
7047
+ if (encoded === void 0) {
7048
+ const base64 = Buffer.from(version.data).toString("base64");
7049
+ encoded = {
7050
+ base64,
7051
+ dataUrl: `data:${version.mediaType};base64,${base64}`
7052
+ };
7053
+ encodedRequestImages.set(version, encoded);
7054
+ }
7055
+ return encoded;
7056
+ }
7057
+ /**
6963
7058
  * Convert one image reference to the final `/alpha/generate` wire format.
6964
7059
  * The official CLI accepts a base64 source internally, then toWireMessages()
6965
7060
  * sends a data URL plus mimeType. Use the request version's media type because
@@ -6969,7 +7064,7 @@ async function imageToCommandCode(ref, readImage) {
6969
7064
  const version = await readImage(ref);
6970
7065
  return {
6971
7066
  type: "image",
6972
- image: `data:${version.mediaType};base64,${Buffer.from(version.data).toString("base64")}`,
7067
+ image: encodedRequestImage(version).dataUrl,
6973
7068
  mimeType: version.mediaType
6974
7069
  };
6975
7070
  }
@@ -7102,10 +7197,9 @@ async function messagesToCC(messages, readImage) {
7102
7197
  * `{ type: 'image_url', image_url: { url: 'data:...;base64,...' } }`.
7103
7198
  */
7104
7199
  async function imageToOpenAI(ref, readImage) {
7105
- const version = await readImage(ref);
7106
7200
  return {
7107
7201
  type: "image_url",
7108
- image_url: { url: `data:${version.mediaType};base64,${Buffer.from(version.data).toString("base64")}` }
7202
+ image_url: { url: encodedRequestImage(await readImage(ref)).dataUrl }
7109
7203
  };
7110
7204
  }
7111
7205
  /**
@@ -7172,7 +7266,7 @@ async function imageToMessages(ref, readImage) {
7172
7266
  source: {
7173
7267
  type: "base64",
7174
7268
  media_type: version.mediaType,
7175
- data: Buffer.from(version.data).toString("base64")
7269
+ data: encodedRequestImage(version).base64
7176
7270
  }
7177
7271
  };
7178
7272
  }
@@ -8174,7 +8268,7 @@ var CommandCodeAdapter = class CommandCodeAdapter extends LlmAdapter {
8174
8268
  /** Cached protocol decision for a key, or undefined when expired/unknown. */
8175
8269
  cachedProtocol(apiBase, apiKey) {
8176
8270
  const hit = this.protocolCache.get(this.accountCacheKey(apiBase, apiKey));
8177
- if (hit === void 0 || Date.now() - hit.at >= 9e5) return void 0;
8271
+ if (hit === void 0 || Date.now() - hit.at >= PROTOCOL_CACHE_TTL_MS) return void 0;
8178
8272
  return hit.protocol;
8179
8273
  }
8180
8274
  rememberProtocol(apiBase, apiKey, protocol) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mars-sea/dsh-commandcode-provider",
3
- "version": "0.14.2",
3
+ "version": "0.14.4",
4
4
  "description": "Unofficial DeepSeek Harness LLM provider plugin for Command Code, ported from pi-commandcode-provider (MIT). Registers the 'commandcode' provider route with a Models-page card and live model catalog.",
5
5
  "icon": "./icon.svg",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "files": [
41
41
  "lib",
42
+ "!lib/**/*.map",
42
43
  "assets",
43
44
  "icon.svg",
44
45
  "locale",