dsh-composer-ux 0.23.4 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,58 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.24.0] — 2026-10-17(金额胶囊把**子代理**的费用算进去)
6
+
7
+ 用户 2026-10-17:「**我发现金额胶囊不计算子代理的费用**」。
8
+
9
+ **根因不在格式化,在数据源头**:子代理(`subagent` / `subagent_fork` / `subagent_control` 那些)
10
+ 跑在**自己的会话**里,它的 usage 事件落在**自己的**会话日志里 —— 父会话的折叠器看不到,
11
+ 官方 `tokenUsage` 投影同样只有本会话。于是 0.23.x 的胶囊只报父会话自己那一份,跑一堆子代理
12
+ 之后数字几乎不动,而屏幕上只是一个**偏小的数字**,不报错、不提示。
13
+
14
+ **宿主半(`src/host.ts` / `src/usage-fold.ts`)**
15
+
16
+ - **子会话从哪来**:两条来源取并集。
17
+ 1. 父会话自己的 `subagent/catalog` 事件(官方 `manager.ts` 在建立子会话时追加,事件里带
18
+ `childId` / `mode`)—— 折叠器顺手读出来,不额外读日志;
19
+ 2. **子会话 header 反查**(`parentSession` + `origin: 'subagent'`,走 `sessionQuery.listSessions()`,
20
+ 30 秒 TTL 缓存)。为什么非得有第二条:2026-10-17 在本机 `~/.dsh/sessions` 上核过 —— 211 份
21
+ 会话日志里 **35 份**是子代理子会话,而**没有任何一份**父会话日志里有 `subagent/catalog`
22
+ (那个事件是较新版本 DSH 才写的)。只认 catalog 的话,用户去看老会话,钱还是少的。
23
+ 两条来源都**只认子代理关系**:用户自己"从某会话 fork 出来的会话"在 header 里同样有
24
+ `parentSession`、但没有 `origin`,见谁都认会把用户自己开的会话算进别人的账上。
25
+ - **递归**:子代理还能派孙代理,所以按 catalog/header 一路折下去(深度 ≤ 4、会话数 ≤ 32,
26
+ 超出记 `skipped`)。活着的子会话靠 `sessions.list()` 的 `seq` 判落后,事件订阅还会继续增量喂。
27
+ - **fork 型子代理的继承前缀要剔掉**(`readSession()` 的 `inheritedEventCount`):`subagent_fork`
28
+ 的子会话日志**开头就是父会话那段历史**,整段照折等于把父会话的账再收一遍,而且金额随上下文
29
+ 大小成倍放大。判据与官方投影同一条(`event.seq < inheritedEventCount` 即继承来的)。
30
+ - **顶层字段一个都没动**:`total` / `cost` / `routes` / `tiers` 仍然是"本会话自己"。客户端拿它们
31
+ 跟官方 `tokenUsage` 投影对账(`agreesWithProjection`),一合并就永远对不上、浮层退回估算口径
32
+ (那是 0.9.1 之前的老毛病)。子代理的合计**单列**在响应新增的 `subagents` 里:
33
+ `sessions` / `live` / `missing` / `external` / `skipped` / `total` / `tiers` / `cost` / `unpriced`。
34
+ 后面四个计数是"没算进来"的四种原因(读不到日志 / 外部执行如 claude-code 本机没有会话 /
35
+ 超上限 / 单价认不出),界面必须把它们说出来 —— 否则用户只会觉得"钱怎么比预期少"。
36
+
37
+ **客户端半(`src/client/CostChipEntry.tsx` / `src/client/session-cost.ts`)**
38
+
39
+ - 胶囊那个数字与浮层「合计」= **本会话 + 子代理**(只在"本会话分列与投影对得上"时并;
40
+ 否则保持本地估算口径,不拿估出来的本会话去加算准的子代理)。
41
+ - 浮层多一行「子代理(N 个会话)」;三个分项(未缓存输入 / 缓存命中 / 输出)也并进去,
42
+ 所以"分项之和 = 合计"仍然精确成立;命中率同源。
43
+ - **峰谷两档只并 token、不并金额**:宿主没把子代理的钱按峰谷拆开,拿"token × 某档单价"凑一个
44
+ 按档金额就是编数字。浮层里写明"两档的 token 已含子代理,金额只含本会话"。
45
+ - 读不全的部分写成一句人话("另有 N 个子代理的费用没算进来:…"),子代理全未定价时那一行写
46
+ 「未定价」而不是 ¥0.00(与 route 行同口径)。没有子代理时浮层、胶囊、文案逐字节与 0.23.x 相同。
47
+
48
+ **验证**:`npm test` **33 套件 2780 条 0 失败**(新增 `test/subagent-cost.mjs` 38 条:纯逻辑 +
49
+ 真宿主半,含 fork 继承前缀、递归与上限、外部执行/读不到、老会话 header 反查、TTL 缓存);
50
+ 变异 **217 条全部咬住**(新增 `SA`–`SF` 六条);`tsc` 0 错误;`cost-panel-render` 新增第 1.2 节
51
+ 17 条渲染断言(旧夹具与旧断言一条没动)。
52
+
53
+ **未验证(如实说明)**:没有真机截图;桌面端要等用户重启后才会加载这一版(那个进程还是
54
+ 2026-10-16 19:21 起的 0.2.0-rc.2);子代理的钱只有总量、没有按峰谷拆开的金额。
55
+
56
+
5
57
  ## [0.23.4] — 2026-10-16(适配 dsh 0.2.1-rc.1:无功能变化,只做验证与版本对齐)
6
58
 
7
59
  用户 2026-10-16:「给网页版 dsh 更新最新版,并适配插件」。上游最新 tag 是 **`dsh-v0.2.1-rc.1`**
package/README.en.md CHANGED
@@ -188,7 +188,7 @@ edited in settings; the JSON output contract is always appended and cannot be ov
188
188
  | Read-only tool fence (including symlinks) / every cap / out-of-bounds rejection | `node test/optimize-tools.mjs` (66 checks against a **real temporary directory**) |
189
189
  | Tool-loop message shape / round cap / exceptions must degrade | same file, section 6 (a fake `llm.stream` drives the whole loop; `role:'tool'` and `toolCallId` are asserted) |
190
190
  | Tool path: off by default, fires only when enabled, **prose after lookups falls back**, no cwd ⇒ no tool at all | `test/quick-commands.mjs` 5g (13 checks) |
191
- | Mutation guards really bite, and **the *named* test must be the one going red** | `npm run test:mutations`: **137/137 bite** |
191
+ | Mutation guards really bite, and **the *named* test must be the one going red** | `npm run test:mutations`: **217/217 bite** |
192
192
  | 0.13.2: `✕` collapses (result kept) + a two-click 丢弃 | `node test/panel-split-render.mjs` §6, mutations EE–EI |
193
193
  | Panel split + pinned result-box buttons (0.13.1) | `node test/panel-split-render.mjs` (20 checks, real `react-dom/server` render) |
194
194
  | Release gates | `npm run gates` (tag target / packed file list / docs drift) |
package/README.md CHANGED
@@ -45,7 +45,7 @@ DeepSeek Harness Web 输入体验增强插件:
45
45
  - **OpenCode 请求头**:给 OpenCode 的模型请求自动附加 `x-opencode-session`(详见下节)。
46
46
  - **默认终端**:Windows 上把模型用的终端工具从 PowerShell 换成 Git Bash(三档在标题行;详见下节)。
47
47
  - **统计行**:输入框下方那行「缓存命中 xx%」按三位小数显示(0.7.0 新增,**唯一默认开的栏**,只有一个开关;详见下节)。
48
- - **金额**:输入框下方一颗极简金额胶囊(本会话费用估算,按 DeepSeek 官方刊例价;点开看三分项明细)。**这一栏没有卡级开关**(胶囊常驻显示),卡内是**按模型改高峰/空闲两档单价**的编辑器(0.9.1 新增;留空=沿用官方价,可自加任意模型名;详见下节)。
48
+ - **金额**:输入框下方一颗极简金额胶囊(本会话费用估算,按 DeepSeek 官方刊例价;点开看三分项明细)。**0.24.0 起把子代理(含孙代理)花的钱也并进来** —— 那个数字是「本会话 + 子代理」的合计。**这一栏没有卡级开关**(胶囊常驻显示),卡内是**按模型改高峰/空闲两档单价**的编辑器(0.9.1 新增;留空=沿用官方价,可自加任意模型名;详见下节)。
49
49
  - **重启 DSH**:卡片抬头右端(GitHub 链接左边)那枚按钮,两步确认后原地重启(详见「维护:重启 DSH」)。
50
50
  2. **快捷指令按钮**:输入框工具行里、「展开」按钮左侧的胶囊按钮,点开是常备提示词清单 + 「优化提示词」。
51
51
  3. **键位生效**(仅主聊天输入框):默认值 = 现状(Enter 发送、Shift+Enter 换行、Ctrl+Enter 加速提交),改动即时生效并持久保存。
@@ -496,6 +496,7 @@ Windows 上 DSH 给模型的终端工具是 **PowerShell**(工具名 `pwsh`)
496
496
  - **为什么金额必须自己算**:DSH 送到浏览器的 `tokenUsage` 投影**只有 token 桶**,全库没有一处把"钱"送到客户端;`llm-pi-ai` 里那个 `cost` 只活在 provider 内部,而且用户自定义的路由(profile 里手写的 provider)拿到的是 `NO_COST`(全 0)。所以费用只能由本插件按刊例价算。
497
497
  - **数据全部来自官方、不新增采集**:token 桶读 `tokenUsage` 投影(与官方统计行**同一份**),模型读 `modelSelection` 投影(来自 `request/header` 事件)。会话中途换过模型时按**最后一次请求**的模型计价(近似,浮层里写明)。
498
498
  - **按 route 分列与"逐笔准时"的取数**(点开才去问宿主半;折叠态那颗胶囊也走同一条路由):面板里列出每条 route(provider + model + 高峰/空闲)的 token 与花费,**金额由宿主半按档算好**(用户覆盖价一并读自设置),所以"各行加起来等于总额""三项相加等于合计"都精确成立。逐请求归因只能宿主半做,走只读路由 `GET /composer-ux/usage?sessionId=…`(`src/host.ts` 注册):首次(或发现 `seq` 落后)用官方 `sessionQuery.readSession(sessionId)` 完整读一次,之后靠 `session/event` 订阅**增量**喂折叠缓存(`createUsageCache`,纯逻辑在 [src/usage-fold.ts](src/usage-fold.ts))—— 所以胶囊取价是 O(1),不必反复重读整份日志。**播种窗口**(读日志期间追加的事件)会先缓冲、读完按 `seq` 补上:不这么做的话水位会跳过快照里那些中间事件,金额**永久少算**(`feed()` 的 seq 幂等,补也补不进去)。**事件形状按本版官方 `SessionEventMap` 的真实声明**(`assistant/attempt` 的 `stream` 里取最后一条 usage 块,或 `assistant/message` 的 `data.usage`;**本版没有 `assistant/chunk` 事件**),口径对齐官方 `token-meter` 的 `usage-projection`(同一步替换、`llm/retry-started` 重开替换槽)。`test/usage-fold.mjs`(89 项)逐条钉住差分、归属、重试、脏数据、逐笔分档与增量缓存;分列出不来时面板会显示"读了 N 条事件、M 条 usage、来源哪条路",一眼就能定位。
499
+ - **子代理的钱(0.24.0)**:子代理跑在**自己的**会话里,usage 在自己的日志里 —— 父会话的折叠器与官方 `tokenUsage` 投影都看不到它(用户 2026-10-17 报的「金额胶囊不计算子代理的费用」就是这个)。现在宿主半会把**整棵子代理树**折进来:子会话 id 有两条来源(父会话日志里的 `subagent/catalog` 事件 + 子会话 header 里 `origin: subagent` 的反查,后者覆盖那些父会话日志里没有 catalog 的老会话),递归到深度 4 / 32 个会话;**fork 型子代理的继承前缀要剔掉**(`readSession()` 的 `inheritedEventCount`,否则等于把父会话的账再收一遍);读不到 / 外部执行(claude-code 那类,本机没有会话)/ 超上限 / 单价认不出 四种「没算进来」的原因分开计数,浮层用人话说出来。顶层 `total`/`cost`/`routes` 仍是**本会话自己**(客户端拿它与官方投影对账,一合并就永远对不上),子代理的合计单列在响应的 `subagents` 里、由客户端相加;峰谷两档**只并 token 不并金额**(宿主没按档拆子代理的钱,不编数字)。`test/subagent-cost.mjs`(38 项)把 fork 前缀、递归上限、外部执行、老会话反查、TTL 缓存逐条钉住。
499
500
  - **折叠态那个数字怎么来的**:客户端投影只有累计 token 桶、**没有任何时间信息**,所以"逐笔准时"的数字只能从宿主半拿(`src/client/session-cost.ts`):最短 600ms 间隔、期间有变化排一次**尾随**请求(保证流结束后的数字是准的)、同一时刻只有一个请求在飞、旧响应按序号丢弃。宿主半那份还没到手、或会话日志与投影对不上时,退回本地的"按当前档位估算"并在面板里说明是哪种口径 —— 胶囊从不空着。
500
501
  - **命中率与旁边那行必然是同一个数**:面板只在"分列与投影**四个桶全等**"(`agreesWithProjection` 是精确比较)时才把分列当权威,此时分列算出的命中率与官方胶囊逐位相同;否则退回投影口径。**两个坑**:客户端投影的未缓存输入叫 `uncachedInputTokens`、日志里叫 `inputTokens` —— 第一版在 `billedInputTokens()` 里认对了名字,却在**调用处**传了 `inputTokens`,于是分母丢掉整块未缓存输入、命中率恒 100%(旁边官方胶囊 98.206%)。现在调用处也走官方键名,`test/client-registration.mjs` 同时钉住"调用处键名"与"与输入框下面那一行同一套函数"。
501
502
  - **计价按 `(provider, model)`**(0.10.0 起):DeepSeek 路由(provider 名含 `deepseek` 或模型名以 `deepseek` 开头)走官方价目表与历史档;其它 provider 走同步来的第三方价目,认不出就写"未定价"。所以用 OpenCode(Zen)之类的接口跑 DeepSeek 模型,用量仍按 **DeepSeek 官方价**进这个金额;跑非 DeepSeek 模型则按 models.dev 的价估算。代价如浮层所写:Zen 可能有自己的加价/订阅,这个数字不等于你付给 Zen 的钱。
@@ -720,9 +721,9 @@ node build.mjs # 产出 lib/index.js + lib/client.js
720
721
  # ⚠️ 宿主半会**内联** schemastery / cosmokit:
721
722
  # 优先用 DSH 检出里的 vendor 副本,检出不在时退到 node_modules
722
723
  # 里同版本的 npm 包(两者逐字节相同)——CI 上走的就是退路
723
- npm test # 32 个套件;当前 2726 passed, 0 failed(2026-10-09 实测;CI 三平台同样全绿)
724
+ npm test # 33 个套件;当前 2780 passed, 0 failed(2026-10-17 实测;CI 三平台同样全绿)
724
725
  # 走 scripts/run-tests.mjs:顺带把"多少套件/多少条"记进 test/.last-run.json
725
- npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(211 条,须单独跑)
726
+ npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(217 条,须单独跑)
726
727
  # 同样记录结果,供下面的文档门禁核对
727
728
  npm run gates # 发版门禁三条一起跑:tag 指向 / 包内容 / 文档数字
728
729
  npm run check:tag # ① tag 名里的版本 == 该 tag 所指提交里的 package.json 版本
package/dsh-plugin.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifestVersion": "0.15",
4
4
  "id": "dsh-composer-ux.input-experience",
5
5
  "name": "dsh-composer-ux",
6
- "version": "0.23.4",
6
+ "version": "0.24.0",
7
7
  "license": "MIT",
8
8
  "facets": {
9
9
  "host": {
package/lib/client.js CHANGED
@@ -7275,13 +7275,45 @@ function CostChipEntry({ useLive, sessionId, useProjection }) {
7275
7275
  { miss: 0, hit: 0, out: 0 }
7276
7276
  );
7277
7277
  const useBreakdown = routeRows.length > 0 && foldAgrees && foldTotal !== null;
7278
+ const subRaw = useBreakdown ? data?.subagents : void 0;
7279
+ const subSessions = num(subRaw?.sessions);
7280
+ const sub = subSessions > 0 ? subRaw : void 0;
7281
+ const subMissing = num(sub?.missing);
7282
+ const subExternal = num(sub?.external);
7283
+ const subSkipped = num(sub?.skipped);
7284
+ const subUnpriced = num(sub?.unpriced);
7285
+ const subCost = num(sub?.cost?.total);
7286
+ const subTokens = tokenTotal(sub?.total);
7287
+ const subParts = {
7288
+ miss: num(sub?.total?.inputTokens),
7289
+ hit: num(sub?.total?.cacheReadTokens),
7290
+ write: num(sub?.total?.cacheWriteTokens),
7291
+ out: num(sub?.total?.outputTokens)
7292
+ };
7293
+ const peakTierTokens = peakTier.tokens + (peakTier.tokens > 0 ? tokenTotal(sub?.tiers?.peak) : 0);
7294
+ const offTierTokens = offTier.tokens + (offTier.tokens > 0 ? tokenTotal(sub?.tiers?.offPeak) : 0);
7295
+ const subTierMerged = peakTierTokens > peakTier.tokens || offTierTokens > offTier.tokens;
7296
+ const subGapParts = [];
7297
+ if (subMissing > 0) subGapParts.push(`${subMissing} \u4E2A\u8BFB\u4E0D\u5230\u4F1A\u8BDD\u65E5\u5FD7\uFF08\u5DF2\u5F52\u6863\uFF0C\u6216\u672C\u673A\u6CA1\u6709\u5B83\u7684\u65E5\u5FD7\uFF09`);
7298
+ if (subExternal > 0) subGapParts.push(`${subExternal} \u4E2A\u662F\u5916\u90E8\u6267\u884C\uFF08\u672C\u673A\u6CA1\u6709\u8FD9\u4E2A\u4F1A\u8BDD\uFF0C\u7528\u91CF\u62FF\u4E0D\u5230\uFF09`);
7299
+ if (subSkipped > 0) subGapParts.push(`${subSkipped} \u4E2A\u8D85\u51FA\u5355\u6B21\u8BFB\u53D6\u4E0A\u9650`);
7300
+ if (subUnpriced > 0) subGapParts.push(`${subUnpriced} \u884C\u6CA1\u8BA4\u51FA\u4EF7\uFF0C\u6309 0 \u8BA1`);
7301
+ const subUnaccounted = subMissing + subExternal + subSkipped;
7302
+ const subGapText = subGapParts.length === 0 ? null : subUnaccounted > 0 ? `\u53E6\u6709 ${subUnaccounted} \u4E2A\u5B50\u4EE3\u7406\u7684\u8D39\u7528\u6CA1\u7B97\u8FDB\u6765\uFF1A${subGapParts.join("\u3001")}` : `\u5B50\u4EE3\u7406\u91CC\u6709\u8D39\u7528\u6CA1\u7B97\u8FDB\u6765\uFF1A${subGapParts.join("\u3001")}`;
7278
7303
  const view = useBreakdown && foldTotal !== null ? {
7279
- miss: foldTotal.inputTokens,
7280
- hit: foldTotal.cacheReadTokens,
7281
- write: foldTotal.cacheWriteTokens,
7282
- out: foldTotal.outputTokens,
7283
- parts: { miss: num(data?.cost?.miss), hit: num(data?.cost?.hit), out: num(data?.cost?.out) },
7284
- total: num(data?.cost?.total)
7304
+ // 子代理的四桶并进展示桶(`sub` 为 undefined 时每一项都是 0,即原样):
7305
+ // 这样"分项之和 = 合计 = 全额"与命中率都是同一份量,见文件头 0.24.0 第三条。
7306
+ miss: foldTotal.inputTokens + subParts.miss,
7307
+ hit: foldTotal.cacheReadTokens + subParts.hit,
7308
+ write: foldTotal.cacheWriteTokens + subParts.write,
7309
+ out: foldTotal.outputTokens + subParts.out,
7310
+ // ⚠️ 三个分项**也要**并钱:只并桶不并钱,"未缓存输入 + 缓存命中 + 输出 = 合计"当场破。
7311
+ parts: {
7312
+ miss: num(data?.cost?.miss) + num(sub?.cost?.miss),
7313
+ hit: num(data?.cost?.hit) + num(sub?.cost?.hit),
7314
+ out: num(data?.cost?.out) + num(sub?.cost?.out)
7315
+ },
7316
+ total: num(data?.cost?.total) + subCost
7285
7317
  } : {
7286
7318
  miss: buckets.miss,
7287
7319
  hit: buckets.hit,
@@ -7310,7 +7342,7 @@ function CostChipEntry({ useLive, sessionId, useProjection }) {
7310
7342
  type: "button",
7311
7343
  "data-composer-ux-cost": "",
7312
7344
  "aria-expanded": open,
7313
- title: (useBreakdown ? "\u672C\u4F1A\u8BDD\u8D39\u7528\uFF08\u6309\u6BCF\u7B14\u7528\u91CF\u53D1\u751F\u7684\u65F6\u95F4\u8BA1\u4EF7\uFF09" : "\u672C\u4F1A\u8BDD\u8D39\u7528\uFF08\u4F30\u7B97\uFF09") + ` \xB7 ${peakText}`,
7345
+ title: (useBreakdown ? sub !== void 0 ? "\u672C\u4F1A\u8BDD\u8D39\u7528\uFF08\u542B\u5B50\u4EE3\u7406\uFF0C\u6309\u6BCF\u7B14\u7528\u91CF\u53D1\u751F\u7684\u65F6\u95F4\u8BA1\u4EF7\uFF09" : "\u672C\u4F1A\u8BDD\u8D39\u7528\uFF08\u6309\u6BCF\u7B14\u7528\u91CF\u53D1\u751F\u7684\u65F6\u95F4\u8BA1\u4EF7\uFF09" : "\u672C\u4F1A\u8BDD\u8D39\u7528\uFF08\u4F30\u7B97\uFF09") + ` \xB7 ${peakText}`,
7314
7346
  onClick: toggle,
7315
7347
  onMouseEnter: () => setHover(true),
7316
7348
  onMouseLeave: () => setHover(false),
@@ -7396,15 +7428,22 @@ function CostChipEntry({ useLive, sessionId, useProjection }) {
7396
7428
  `${formatTokens(route.tokens)} \xB7 ${route.unpriced ? "\u672A\u5B9A\u4EF7" : formatMoney(route.cost)}`,
7397
7429
  `${route.provider}|${route.model}|${String(route.peak)}|${route.era}`
7398
7430
  )),
7431
+ sub !== void 0 ? detail(
7432
+ `\u5B50\u4EE3\u7406\uFF08${subSessions} \u4E2A\u4F1A\u8BDD\uFF09`,
7433
+ `${formatTokens(subTokens)} \xB7 ${subUnpriced > 0 ? "\u672A\u5B9A\u4EF7" : formatMoney(subCost)}`,
7434
+ "__subagents__"
7435
+ ) : null,
7399
7436
  detail("\u5408\u8BA1", `${formatTokens(view.miss + view.hit + view.write + view.out)} \xB7 ${formatMoney(view.total)}`, "__total__"),
7400
- peakTier.tokens > 0 ? detail("\u9AD8\u5CF0\u6863", `${formatTokens(peakTier.tokens)} \xB7 ${formatMoney(peakTier.cost)}`, "__peak__") : null,
7401
- offTier.tokens > 0 ? detail("\u7A7A\u95F2\u6863", `${formatTokens(offTier.tokens)} \xB7 ${formatMoney(offTier.cost)}`, "__off__") : null,
7437
+ peakTier.tokens > 0 ? detail("\u9AD8\u5CF0\u6863", `${formatTokens(peakTierTokens)} \xB7 ${formatMoney(peakTier.cost)}`, "__peak__") : null,
7438
+ offTier.tokens > 0 ? detail("\u7A7A\u95F2\u6863", `${formatTokens(offTierTokens)} \xB7 ${formatMoney(offTier.cost)}`, "__off__") : null,
7402
7439
  otherTier.tokens > 0 ? detail("\u5176\u4ED6\u8DEF\u7531\uFF08\u65E0\u5CF0\u8C37\uFF09", `${formatTokens(otherTier.tokens)} \xB7 ${formatMoney(otherTier.cost)}`, "__other__") : null,
7440
+ subTierMerged ? note("\u9AD8\u5CF0 / \u7A7A\u95F2\u4E24\u6863\u7684 token \u5DF2\u542B\u5B50\u4EE3\u7406\uFF0C\u91D1\u989D\u53EA\u542B\u672C\u4F1A\u8BDD\uFF08\u5BBF\u4E3B\u534A\u6CA1\u628A\u5B50\u4EE3\u7406\u7684\u94B1\u6309\u5CF0\u8C37\u62C6\u5F00\uFF0C\u6240\u4EE5\u4E0D\u62FF token \u4E58\u5355\u4EF7\u7F16\u4E00\u4E2A\u6309\u6863\u91D1\u989D\u51FA\u6765\uFF09\u3002", "__sub_tier__") : null,
7403
7441
  detail("\u672A\u7F13\u5B58\u8F93\u5165", `${formatTokens(view.miss)} \xB7 ${formatMoney(view.parts.miss)}`, "__miss__"),
7404
7442
  detail("\u7F13\u5B58\u547D\u4E2D", `${formatTokens(view.hit)} \xB7 ${formatMoney(view.parts.hit)}`, "__hit__"),
7405
7443
  detail("\u8F93\u51FA", `${formatTokens(view.out)} \xB7 ${formatMoney(view.parts.out)}`, "__out__"),
7406
7444
  builtinCount > 0 ? note(`\u6709 ${builtinCount} \u884C\u7528\u7684\u662F\u5185\u7F6E\u5FEB\u7167\u4EF7\uFF08models.dev \u5FEB\u7167 ${PROVIDER_PRICES_SNAPSHOT_AT}\uFF09\u2014\u2014\u60F3\u6362\u6210\u6700\u65B0\u4EF7\uFF0C\u53BB\u8BBE\u7F6E\u9875\u300C\u91D1\u989D\u300D\u70B9\u4E00\u6B21\u300C\u540C\u6B65\u7B2C\u4E09\u65B9\u4EF7\u76EE\u300D\u3002`, "__builtin__") : null,
7407
- unpricedCount > 0 ? note(`\u6709 ${unpricedCount} \u884C\u300C\u672A\u5B9A\u4EF7\u300D\uFF1A\u90A3\u662F\u975E DeepSeek \u6A21\u578B\uFF0C\u540C\u6B65\u4EF7\u76EE\u4E0E\u5185\u7F6E\u5FEB\u7167\u91CC\u90FD\u6CA1\u6709\u5B83\uFF0C\u6240\u4EE5\u90A3\u90E8\u5206\u6309 0 \u8BA1\u3002\u53BB\u8BBE\u7F6E\u9875\u300C\u91D1\u989D\u300D\u5237\u65B0\u4E00\u6B21\u300C\u540C\u6B65\u7B2C\u4E09\u65B9\u4EF7\u76EE\u300D\uFF0C\u6216\u7ED9\u90A3\u884C\u76F4\u63A5\u586B\u4E2A\u4EF7\u3002`, "__unpriced__") : null
7445
+ unpricedCount > 0 ? note(`\u6709 ${unpricedCount} \u884C\u300C\u672A\u5B9A\u4EF7\u300D\uFF1A\u90A3\u662F\u975E DeepSeek \u6A21\u578B\uFF0C\u540C\u6B65\u4EF7\u76EE\u4E0E\u5185\u7F6E\u5FEB\u7167\u91CC\u90FD\u6CA1\u6709\u5B83\uFF0C\u6240\u4EE5\u90A3\u90E8\u5206\u6309 0 \u8BA1\u3002\u53BB\u8BBE\u7F6E\u9875\u300C\u91D1\u989D\u300D\u5237\u65B0\u4E00\u6B21\u300C\u540C\u6B65\u7B2C\u4E09\u65B9\u4EF7\u76EE\u300D\uFF0C\u6216\u7ED9\u90A3\u884C\u76F4\u63A5\u586B\u4E2A\u4EF7\u3002`, "__unpriced__") : null,
7446
+ subGapText !== null ? note(subGapText, "__sub_gap__") : null
7408
7447
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
7409
7448
  detail("\u672A\u7F13\u5B58\u8F93\u5165", `${formatTokens(buckets.miss)} \xB7 ${formatMoney(estimateParts.miss)}`),
7410
7449
  detail("\u7F13\u5B58\u547D\u4E2D", `${formatTokens(buckets.hit)} \xB7 ${formatMoney(estimateParts.hit)}`),