@chenlongapps/opencode-token-usage 0.4.8 → 0.4.10

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/dist/usage.d.ts CHANGED
@@ -143,9 +143,10 @@ export declare function contextUsage(messages: readonly UsageMessage[], limit?:
143
143
  export declare function formatTokens(value: number): string;
144
144
  /** Native sub-minute precision; longer durations keep compact, readable units. */
145
145
  export declare function formatDuration(milliseconds: number, compact?: boolean): string;
146
- export declare function formatRunTime(runtime?: RuntimeSummary, compact?: boolean): string;
146
+ /** Hide confirmed zero totals, but preserve unavailable and stale timing. */
147
+ export declare function formatRunTime(runtime?: RuntimeSummary, compact?: boolean): string | undefined;
147
148
  /** Preserve session-local time on narrow terminals without adding composer height. */
148
- export declare function fitUsageSummary(fields: readonly (readonly [string, string])[], width: number, stale?: boolean): string;
149
+ export declare function fitUsageSummary(fields: readonly (readonly [string, string | undefined])[], width: number, stale?: boolean): string;
149
150
  export declare const formatCost: (value: number) => string;
150
151
  export declare function formatEstimatedCost(cost: number, status: CostStatus): string | undefined;
151
152
  /** Per-million rates need more precision than a rounded total cost. */
package/dist/usage.js CHANGED
@@ -209,27 +209,35 @@ export function formatDuration(milliseconds, compact = false) {
209
209
  return compact ? `${minutes}m${rest}s` : `${minutes}m ${String(rest).padStart(2, "0")}s`;
210
210
  return `${rest}s`;
211
211
  }
212
+ /** Hide confirmed zero totals, but preserve unavailable and stale timing. */
212
213
  export function formatRunTime(runtime, compact = false) {
213
214
  if (runtime?.milliseconds === undefined)
214
215
  return "—";
215
- const time = formatDuration(runtime.milliseconds, compact);
216
+ const estimated = runtime.estimatedMilliseconds !== undefined;
217
+ const milliseconds = runtime.estimatedMilliseconds ?? runtime.milliseconds;
218
+ if (runtime.status === "ready" && milliseconds === 0)
219
+ return undefined;
220
+ const duration = formatDuration(milliseconds, compact);
221
+ const time = estimated && duration !== "—" ? `~${duration}` : duration;
216
222
  return runtime.status === "stale" ? `${time} · ${compact ? "stale" : "Not updated"}` : time;
217
223
  }
218
224
  /** Preserve session-local time on narrow terminals without adding composer height. */
219
225
  export function fitUsageSummary(fields, width, stale = false) {
220
- let visible = [...fields];
226
+ let visible = fields.filter((field) => field[1] !== undefined);
221
227
  const suffix = stale ? " · Not updated" : "";
222
- const render = (title) => `${title} · ${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${suffix}`;
228
+ const render = (title) => `${[title, ...visible.map(([label, value]) => `${label} ${value}`)].join(" · ")}${suffix}`;
223
229
  for (const label of ["TPS", "Context", "Cost", "Total"]) {
224
230
  if (render("Token Usage").length <= width)
225
231
  return render("Token Usage");
226
- visible = visible.filter(([name]) => name !== label);
232
+ // Keep a useful metric even when the zero-time field is hidden.
233
+ if (visible.length > 1)
234
+ visible = visible.filter(([name]) => name !== label);
227
235
  }
228
236
  if (render("Token Usage").length <= width)
229
237
  return render("Token Usage");
230
238
  if (render("Usage").length <= width)
231
239
  return render("Usage");
232
- // Remove the title before clipping any value; time is the final retained field.
240
+ // Remove the title before clipping any value; retain time when it is present.
233
241
  return `${visible.map(([label, value]) => `${label} ${value}`).join(" · ")}${stale ? " · stale" : ""}`.slice(0, Math.max(0, width));
234
242
  }
235
243
  export const formatCost = (value) => {
@@ -335,7 +343,8 @@ export function usageRows(summary, context, performance, runtime) {
335
343
  if (performance?.ttft !== undefined && Number.isFinite(performance.ttft)) {
336
344
  rows.push(["TTFT", `${(Math.max(0, performance.ttft) / 1_000).toFixed(1)}s`]);
337
345
  }
338
- if (runtime)
339
- rows.push(["Run Time", formatRunTime(runtime)]);
346
+ const time = runtime && formatRunTime(runtime);
347
+ if (time !== undefined)
348
+ rows.push(["Run Time", time]);
340
349
  return rows;
341
350
  }
@@ -1,5 +1,185 @@
1
1
  # 验证记录
2
2
 
3
+ ## 2026-10-14 · 首轮 Run Time 起点重置与重连归零(未发布)
4
+
5
+ 无 idle 消息并不一定是旧历史:新会话的第一轮在执行结束前也没有该标记。此前每次 synthetic/steer 输入都被当成 legacy 轮次边界,工具延续时丢失首轮之前已经过去的时间;重新读取历史或重建视图可以重新暴露该少算。匿名固定夹具复现原先 `~1m 56s` 与正确 `~18m 57s` 的差异,不依赖屏幕操作。
6
+
7
+ `src/runtime.ts` 接收既有会话读取的原生时间元数据:有效 `time.created` 且尚未设置 `time.idle` 时,首轮使用首个 user/synthetic 输入,即使尚未产生第一条 idle 消息。工具、retry 与 steer 保持同轮,完成页脚替换重叠时长;首次 idle 前后的 native 小计一致。已有原生 idle 时间(包括零)或元数据未知的无标记旧历史仍使用最近输入。会话创建时间只用于区分边界语义,不代替缺失的输入时间、不会按会话年龄计时。`src/source.ts` 保留原有 `session.get()` 返回的时间字段,控制器复用它,没有新增请求、周期读取或持久化。
8
+
9
+ 另修复 `src/controller.ts` 的重连窗口:历史读取可以早于可选 clock RPC 返回,不能把同一仍保留的开放轮次的可信 `~` 值替换成原生零小计。只有起点一致、此前 assistant 仍在历史中且不含当前轮的历史小计未变时保留冻结显示;等待可信锚点期间停止 ticker。完成/中断、回退删除及替换历史仍使用新历史的权威数值,允许合法向下校正,不使用最大值锁掩盖差异。
10
+
11
+ 已核对 V2 插件/CLI/Client/API 文档、SDK 2.0.24 的 `SessionInfo.time`,以及版本固定的宿主 [`2.0.9 projector`](https://github.com/anomalyco/opencode/blob/v2.0.9/packages/core/src/session/projector.ts)、[`2.0.28 projector`](https://github.com/anomalyco/opencode/blob/v2.0.28/packages/core/src/session/projector.ts) 与 [`2.0.28 info`](https://github.com/anomalyco/opencode/blob/v2.0.28/packages/core/src/session/info.ts):新建/分叉不继承 `time_idle`,原生执行结束记录它;未设置时 API 不输出 `time.idle`。2.0.28 的 `turnDuration`/`inputIndex`、分叉副本 ID、完成 compaction 与升序历史语义已复核;首轮的元数据修正避免其首次 idle 前临时误用 legacy 回退。
12
+
13
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.9、SDK 2.0.24、OpenTUI 0.5.14、真实 OpenCode 2.0.28。此前 2.0.11/2.0.22/2.0.24/2.0.26 的验证记录保留,不宣称这些宿主本次全部重跑。
14
+
15
+ | 检查 | 结果 |
16
+ | --- | --- |
17
+ | `npm run typecheck` | 通过 |
18
+ | `npm test` | 247 项通过,新增 13 项首轮、恢复及适配器回归测试 |
19
+ | `npm run build` | 通过 |
20
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
21
+ | `npm run test:smoke` | 真实 OpenCode 2.0.28 打包/隔离安装的完整检查通过,含首轮延续场景 |
22
+
23
+ 自动化测试覆盖多次 synthetic 投递造成的约 17 分钟少算、首次 idle 前后完成结算、首轮 steer/retry 页脚替换、无 marker 的分叉、旧历史最近输入、缺失/无效消息时间、同一视图刷新、sidebar/详情同时重建、暂停回调后一次补齐两分钟、慢校时期间冻结及恢复、新监视器的一次 clock 读取,以及历史替换/小计变化不能继承旧冻结值。暂停、重连和慢 RPC 由注入的事件、单调时钟与可控 Promise 验证,不是实际断网或操作系统暂停。
24
+
25
+ 新增真实宿主首轮场景:本地模拟提供商先返回 subagent 工具调用,两个原生 synthetic inbox 项在同一轮投递,继续输出保持门控等待。确认消息中没有 idle、原生 `time.idle` 也未设置;打包算法与侧边栏均从最初输入计时,完成之前 native 小计保持零。可见耗时为投递前 `~4.4s`、投递后 `~7.2s`、新 TUI 恢复 `~8.7s`、终端失焦/恢复 `~9.2s`、紧凑/详细 `/usage` `~9.4s`,各实时值与服务端 clock 减原始输入相符。结束后两个 TUI 均恢复原生 9,564ms;原有上下文、定价、TPS/TTFT、Responses、子代理、分叉及 compaction 的完整检查也通过。模拟提供商只在本地,不调用付费模型、不修改现有 OpenCode 配置。
26
+
27
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-DIvmzk`。`13-first-turn-*.txt`/`.ansi` 保存首轮各入口,`result.json` 的 `firstTurnRuntime` 保存最初输入、投递项、实测参考与显示值及最终 native 时长。2.0.28 已加入实际验证宿主记录;Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
28
+
29
+ 用户报告的 **macOS 物理多屏切换** 尚未实机复验:上述终端焦点事件、新 TUI 恢复和模拟重连不代表已确认物理切屏是原因,也不代表已验证 App Nap 或睡眠/唤醒。中英文 README、产品约定与路线图已同步;版本、SDK 与价格快照未变,未升级版本、提交、推送或发布。
30
+
31
+ ## 2026-10-11 · 性能监视器异常流回收(未发布)
32
+
33
+ `PerformanceMonitor` 在 `server.connected` 后重置近期 TPS 窗口,并通过 V2 `session.active()` 对账全部已跟踪的未结束流,包括离屏会话。未结束流静默 60 秒后进行共享活动检查,每次请求覆盖全部未结束流;确认 inactive 后保留 60 秒,再释放流、去重事件集合和 TTFT 观察。没有未结束流时停止活动查询,增量及 2 秒 TPS 窗口过期仍使用内存,不读取历史。状态请求有 5 秒超时;失败保留并重试,请求身份及逐流 revision 防止迟到响应覆盖新活动、重连及销毁。
34
+
35
+ 历史 tokens/`time.streamed` 只表示提供商已交付,不能证明工具已完成,不再单独触发 TTFT/流状态回收。有效历史 `time.completed`、step/execution 结束或后继 step 可以结算,重复结束不延长保留期;旧结束/重试不能影响新 step,重试可撤销此前失败的回收。回收与静默检查都用本地单调时钟。服务端仍 running 的长工具等待、首 token 前等待和重试没有硬性寿命上限。
36
+
37
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.9、SDK 2.0.24、OpenTUI 0.5.14。已复核 V2 插件、CLI 插件与官方 OpenAPI 文档,以及已安装 SDK 的 `session.active()` Promise 解包形状、execution/step/retry/tool 事件;活动集合覆盖本进程的前台执行,缺席才表示 inactive,读取异常不能代替空集合。
38
+
39
+ | 检查 | 结果 |
40
+ | --- | --- |
41
+ | `npm run typecheck` | 通过 |
42
+ | `npm test` | 234 项通过,新增 29 项生命周期、控制器及适配器回归测试 |
43
+ | `npm run build` | 通过 |
44
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
45
+ | `npm run test:smoke` | 真实 OpenCode 2.0.26 打包/隔离安装的完整复验通过,包含新增异常流场景 |
46
+
47
+ 自动化测试覆盖无视图/无结束事件/无后续历史的自主回收、首 token 缺失、session 结束兜底、连续 step 替代、长工具等待及 streamed-only 历史、长重试、跨树重连、交错会话的共享查询、失败/超时重试、新 delta/工具进度/retry/execution 活动与慢查询竞态、重复重连及取消失效、旧状态响应、重复结束不续期、单调时钟、删除/销毁与空监视器停止查询。关闭全部控制器后回收离屏流,再打开对应会话不会恢复过期 TTFT。
48
+
49
+ 首轮真实 OpenCode 2.0.26 的原有整套检查通过,新增场景因测试要求父会话必须有实时 TTFT 而超时。事件记录确认宿主可以直接交付完整工具参数、没有 `session.tool.input.delta`,父会话因此没有可测实时 TTFT;其流状态仍应保留。测试已改为同时确认父/子流存在,并保留实际可测的子会话 TTFT,不伪造父会话样本。首轮记录:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-nvGQ95`。
50
+
51
+ 完整复验使用真实宿主执行 subagent 工具、由本地模拟提供商暂缓子会话输出;独立的打包监视器没有挂载视图,也没有接收历史对账。测试过滤其 step/execution 结束事件,注入与宿主相同的无 `created` 重连握手,并将该监视器的单调时钟加速至 1 小时、累计 25 小时。两次真实活动查询都包含父/子会话,两条流一直保留,子会话 TTFT 始终为 418ms;结束后第三次活动集合为空,监视器回收全部流与观察并停止定时任务。此处验证的是加速年龄及注入的事件丢失/重连路径,不是实际等待 25 小时或切断真实网络连接。
52
+
53
+ 通过记录:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-WOFN5d`。`result.json` 的 `performanceLifecycle` 保存会话、90,000,000ms 加速年龄、可测 TTFT 与三次真实活动集合;`stream-events.json` 保存包括被过滤结束事件在内的宿主事件。原有侧边栏、详情、子代理、耗时、TPS 突发/过期、分叉和压缩检查全部通过。中英文 README、产品约定及路线图已同步;插件版本仍为 0.4.9,未升级版本、提交、推送或发布。
54
+
55
+ ## 2026-10-11 · 构建清理与打包文件一致性
56
+
57
+ `scripts/build.mjs` 在 TypeScript 编译前递归清理 `dist`,目录不存在时也可构建;`npm pack` 的既有 `prepack` 钩子复用该流程。`scripts/smoke.mjs` 的打包断言改为从当前 `src` 推导完整 `.js`/`.d.ts` 清单,精确比对包内 `dist` 文件,统一检测残留、缺失以及未清理的 JSX 中间产物。
58
+
59
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.9。本次执行本地构建与打包专项校验。
60
+
61
+ | 检查 | 结果 |
62
+ | --- | --- |
63
+ | `npm run typecheck`、`npm test` | 通过,205 项测试全部通过 |
64
+ | `npm run build` | 通过,原有孤儿 `dist/popover.js`/`dist/popover.d.ts` 已清理 |
65
+ | 实际 `npm pack --json` | 预置孤儿 JS、声明及嵌套残留后,`prepack` 全部清理;16 个源码模块对应 32 个 `dist` 文件,tarball 共 41 个文件,归档实际清单与 npm 清单一致 |
66
+ | 重复打包 | 连续两次完整 `prepack`/打包的 SHA-512 integrity 一致 |
67
+ | `node --check scripts/smoke.mjs` | 通过 |
68
+
69
+ 打包验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-pack-EJkcwj/chenlongapps-opencode-token-usage-0.4.9.tgz`。
70
+
71
+ ## 2026-10-10 · 移除 Run Time 运行状态动画(未发布)
72
+
73
+ 侧边栏、子代理摘要/完整浮窗以及 `/usage` 紧凑/详细模式直接显示耗时,不再附加 `Running.`/`Running..`/`Running...`。移除 `src/running.ts`、动画专用计时器及其 5 项测试,清理仅供动画使用的会话 ID 传递;各入口的原生状态仍用于控制 `~` 实时估算,共享 500ms 耗时 ticker 与原生累计算法未改动。零值隐藏、不可用/未更新标记及面板末行顺序保持不变。
74
+
75
+ 新增子代理摘要回归测试覆盖实时估算、未更新/不可用值与窄终端;smoke 改为检查耗时行不含运行提示、没有新增量时估算仍推进、完成/中断后恢复原生值,并断言打包产物不包含旧动画模块。中英文 README 与产品约定已同步,旧动画验证记录保留为历史记录。
76
+
77
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26;沿用 V2 CLI 插件 API。
78
+
79
+ | 检查 | 结果 |
80
+ | --- | --- |
81
+ | `npm run typecheck` | 通过 |
82
+ | `npm test` | 205 项通过:移除 5 项动画测试,新增 1 项摘要回归测试 |
83
+ | `npm run build` | 通过;已清理本地旧动画构建产物 |
84
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
85
+ | `npm run test:smoke` | 真实 OpenCode 2.0.26 打包/隔离安装的完整检查通过 |
86
+
87
+ 真实 TUI 在侧边栏、两种 `/usage`、子代理摘要/完整浮窗及 48 列子代理视图中均不再显示运行提示,并在没有新流式增量时继续推进 `~` 耗时。同一耗时行在观察期间位置不变;完成/中断后恢复不带 `~` 的原生累计值,空闲父会话不随子代理增长。零值隐藏以及原有标题点击、上下文、定价、TPS/TTFT、Responses 突发/静默、分叉与压缩检查全部通过,没有调用真实付费模型或修改现有 OpenCode 配置。
88
+
89
+ 验证产物:`/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/token-usage-smoke-kuK7vo`,其中 `10-running-*-start.txt`/`advanced.txt` 保存各入口没有运行提示且耗时继续增长的画面,`result.json` 保存原生耗时、宿主版本与不含旧动画模块的打包文件列表。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
90
+
91
+ 插件版本、SDK、价格快照未变;未升级版本、提交、推送或发布。
92
+
93
+ ## 2026-10-10 · 实时 TPS 突发增量保护(未发布)
94
+
95
+ `src/performance.ts` 合并 100ms 内的可观察增量,以每个近期窗口的首批增量作为计数基线,不把基线字节计入其时间戳之后的输出;有效跨度至少为 500ms,窗口仍为约 2 秒。EWMA 按经过时间计算权重,最多每 100ms 推进一次,与 delta 条数和视图读取次数解耦。超过窗口的服务端事件间隔或本地静默会重建基线并清除旧平滑值,不使用模型专属倍率、TPS 硬上限或隐藏 reasoning 猜测。
96
+
97
+ 共享性能监视器的超时任务同时负责近期窗口与已有 TTFT 保留期清理:连续 2 秒没有新 delta 后,使实时估算过期并通知视图,回退到已准备的历史 TPS,没有历史值则隐藏该行。静默年龄只使用本地单调时钟,不拿客户端 epoch 减服务端时间;超时和增量均不读取历史或调用 API,也不改变原生 Run Time。TTFT 仍取 step 开始到首个原始增量,完成后的全树 assistant `Σ(Output + Reasoning) ÷ Σ(time.streamed − time.created)` 口径不变。读取失败、切换会话、多视图共享和子代理范围沿用现有控制器。
98
+
99
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14。已复核 V2 插件/CLI 文档与版本固定的 OpenCode 2.0.26 Responses 适配器和事件总线:事件 `created` 是宿主发布时间,done-only 摘要可回退为 delta,并非内部 token 生成时钟。
100
+
101
+ | 检查 | 结果 |
102
+ | --- | --- |
103
+ | `npm run typecheck` | 通过 |
104
+ | `npm test` | 209 项通过,新增 12 项近期 TPS、突发、时钟、过期及共享控制器回归测试 |
105
+ | `npm run build` | 通过 |
106
+ | `node --check scripts/smoke.mjs`、`node --check test/fixtures/responses.mjs`、`git diff --check` | 通过 |
107
+ | `npm run test:smoke` | 真实 OpenCode 2.0.26 打包/隔离安装的完整检查通过,包含新增 Responses SSE 场景 |
108
+
109
+ 自动化测试覆盖 10ms 摘要突发、同时间戳与碎片化基线、长暂停后恢复、500ms 边界、乱序/重复/空增量、UTF-8、时间加权平滑、有效高速不限幅、客户端/远程 epoch 偏差、无事件过期、并发独立过期、删除/销毁清理、TTFT 保留及多视图不读取历史。中英文 README 已同步说明首次测速延迟、静默回退及实测/估算口径差异。
110
+
111
+ 新增本地 Responses SSE 模拟提供商,包含摘要 delta 突发、第二段 done-only 摘要、间隔 600ms 的文本和静默等待;宿主仍为真实 OpenCode,不调用付费模型或修改现有配置。首轮事件记录中,宿主将摘要交付为相隔 5ms 的 800/400 字节 delta,TUI 正确只显示 TTFT、不显示 TPS;失败的是标题误匹配断言,而非插件指标。限定为数值指标行后,完整复验通过:摘要突发不显示 TPS,后续文本显示 `~11.3 tok/s`,静默后隐藏 TPS、仍保留 `TTFT 0.3s`,完成后按服务端上报的 Output 200 与 Reasoning 800 恢复无 `~` 的历史吞吐。原有侧边栏标题、详情、窄终端、子代理、原生耗时、定价、分叉和压缩检查全部通过;不宣称真实 OpenAI 服务已验证。
112
+
113
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-ZKisPX`,其中 `11-responses-burst.txt`、`11-responses-live.txt`、`11-responses-quiet.txt`、`11-responses-completed.txt` 保存四个阶段,`stream-events.json` 保存真实宿主事件,`result.json` 记录 Responses 实时与历史 TPS 及原有全套结果。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
114
+
115
+ 插件版本、SDK、价格快照未变;未升级版本、提交、推送或发布。
116
+
117
+ ## 2026-10-10 · 点击侧边栏标题查看详情(未发布)
118
+
119
+ 侧边栏 `Token Usage` 标题的左键单击复用 `/usage` 的 `UsageDialog` 与统一打开函数,默认紧凑模式,保留 `d` 切换、居中 large 弹窗、滚动及 Escape/esc 关闭。按标题所属的被查看会话打开,不改变统计口径或子代理摘要入口;仅弹窗挂载期间保留原有独立详情控制器,关闭沿用现有取消读取、订阅与 ticker 清理。
120
+
121
+ 新增 `src/click.ts`:匹配左键按下/同一位置松开,拖动取消,选中文字不触发;只有有效点击才阻止鼠标事件冒泡。OpenTUI 的文本普通点击也可能带 `isDragging: true`,因此不把该标记直接当作实际拖动。其他统计行未绑定打开事件,保留文字选择。
122
+
123
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26;已复核 V2 插件/CLI 文档及已安装 OpenTUI 的鼠标选择语义。
124
+
125
+ | 检查 | 结果 |
126
+ | --- | --- |
127
+ | `npm run typecheck` | 通过 |
128
+ | `npm test` | 197 项通过,新增 5 项点击、防误触与选择事件回归测试 |
129
+ | `npm run build` | 通过,包含 click 模块与类型声明 |
130
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
131
+ | `npm run test:smoke` | 真实 2.0.26 打包/隔离安装的完整检查通过 |
132
+
133
+ 真实终端新增断言覆盖空会话与已有用量、标题中键/右键、标题拖选、普通统计行不打开、切换会话后的正确身份、默认紧凑与 `d` 详细模式、Escape/点击 esc 关闭及重复打开后恢复默认模式。检查消息数量与模拟提供商请求数量均不增加;运行中的标题入口继续显示、推进并结算实时耗时。原有窄终端、子代理、全树累计、逐消息计价、未知上下文上限、分叉及已完成压缩检查全部通过。
134
+
135
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-PAneuj`,其中 `01-sidebar-usage-dialog.txt`、`06-sidebar-usage-dialog.txt` 保存标题入口的空/有用量弹窗,`10-running-usage-*` 保存运行中的详情与三帧动画,`result.json` 记录真实宿主版本、打包文件与最终统计。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。
136
+
137
+ 中英文 README 已同步入口与操作说明,并明确新入口仅在 2.0.26 完成集成验证。插件版本仍为 0.4.8,SDK 与 OpenTUI 依赖未变;未升级版本、提交、推送或发布。
138
+
139
+ ## 2026-10-10 · 当前已耗时实时估算(未发布)
140
+
141
+ 运行中 `Run Time`/`Time` 用 `~` 显示截至当前的累计耗时估算,首轮非零估算也可见,不预测最终或剩余时间。`src/runtime.ts` 从同一次完整历史扫描准备当前轮起点及已计入的重叠时长;显示值用当前轮估算替换同轮已有时长,`milliseconds` 仍只保存原生已完成累计。工具、重试及前台等待继续计时;terminal 回答/执行结束先冻结显示,刷新后以原生完成时间结算并移除 `~`,允许向下校正。读取失败保留冻结的估算及未更新标记,未知历史时间仍显示不可用。
142
+
143
+ 新增 `src/runtime-monitor.ts`,以服务端时间锚点和本地单调时钟推进共享的 500ms ticker;时钟 tick 不扫描历史、不读取 API。启动/重连通过现有 `opencode-token-usage.context` RPC 的只读 `clock` 方法取得时间,不保存数据、不读 MCP、不进入模型调用链路;RPC 不可用或慢响应时,可用带时间戳的实时事件建立锚点,不阻塞实测统计。无可信锚点时保留原生累计值,不用客户端 epoch 时间猜测远程时间。各入口使用同一监视器,关闭最后一个估算视图后取消 ticker;会话切换隔离旧结果,空闲父会话不继承子代理的活动。
144
+
145
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包仍为 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。已复核 V2 插件/CLI/RPC 文档及已安装 SDK;实际 2.0.26 的 synthetic `server.connected` 握手缺少 `created`。首轮集成因此暴露了新开安静子代理视图没有锚点的问题,已用可选只读 RPC 修复,并加入实际握手形状的回归夹具。
146
+
147
+ | 检查 | 结果 |
148
+ | --- | --- |
149
+ | `npm run typecheck` | 通过 |
150
+ | `npm test` | 192 项通过,新增 24 项估算、时钟、控制器及显示回归测试 |
151
+ | `npm run build` | 通过,包含 runtime monitor 与类型声明 |
152
+ | `node --check scripts/smoke.mjs`、`git diff --check` | 通过 |
153
+ | `npm run test:smoke` | 真实 2.0.26 打包/隔离安装的完整检查通过 |
154
+
155
+ 自动化测试覆盖首轮、同轮 steer/retry 重叠替换、工具等待、历史空闲、legacy/无输入/分叉起点、terminal 完成后的迟到快照、失败冻结与恢复、无锚点降级、远程时间与客户端 epoch 偏差、共享 ticker 清理、可选 RPC 的慢响应/失败/取消/迟到结果及只读性。时钟偏差与读取失败由固定夹具验证,没有宣称在真实远程宿主注入这些故障。真实 TUI 在侧边栏、两种 `/usage`、子代理摘要/完整浮窗及 48 列视图分别捕获三个 Running 动画帧并确认无新流式增量时估算仍增长;验证首轮显示、切换/新开正在运行的会话、正常完成和中断后的精确结算、空闲父会话不随子代理增长,以及原有分叉、压缩、定价、上下文和 TPS/TTFT 检查。
156
+
157
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-FxP0I5`,其中 `02-switched-streaming.txt` 与 `04-streaming.txt` 保存首轮实时值,`10-running-*` 保存各入口实时值及三帧动画,`result.json` 保存最终原生累计值。Node 22 的既有 OpenTUI 引擎警告与 MockTimers 实验警告保留。本次未升级版本、提交、推送或发布。
158
+
159
+ ## 2026-10-10 · Run Time 运行状态动画
160
+
161
+ 可见的 `Run Time`/`Time` 值按当前查看会话的原生 `context.data.session.status(sessionID)` 附加运行状态。新增 `src/running.ts`,每 500ms 循环 `Running.`/`Running..`/`Running...`,用空格补齐三个点的位置,避免右对齐数值及摘要后续字段移动。动画只更新显示信号,不读取消息历史、不估算未完成耗时,不把子代理活动状态归给空闲的父会话。零值隐藏、不可用/未更新标记以及 Run Time 末行顺序保持不变;进入空闲、隐藏时间项、关闭视图或切换会话时清理原计时器。
162
+
163
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。
164
+
165
+ 已通过 `npm run typecheck`、168 项 `npm test`(新增 5 项动画循环、固定宽度、清理/重启与格式兼容回归测试)、`npm run build`、smoke 脚本语法检查、`git diff --check` 和完整 `npm run test:smoke`。真实打包/隔离安装在侧边栏、`/usage` 两种模式、子代理摘要/完整浮窗与 48 列子代理视图中分别捕获三个动画帧,确认行位置、累计数值及 Running 起始列不移动,生成中累计值不增长。中断/完成后移除运行提示并恢复已完成原生耗时;打开已经运行的子代理能恢复状态,空闲父会话不继承子代理的运行标记。零值隐藏及原有全套集成断言均通过。
166
+
167
+ Node 22 的既有 OpenTUI 引擎警告保留。本次未升级版本、提交、推送或发布。
168
+
169
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-4anV5A`,其中 `10-running-*-1-dot.txt`/`2-dot.txt`/`3-dot.txt` 保存各入口的三个帧,`result.json` 保存最终原生耗时及原有集成结果。
170
+
171
+ ## 2026-10-10 · 隐藏零耗时项
172
+
173
+ 正常状态下确认累计原生耗时为零时,侧边栏、子代理摘要/完整浮窗以及 `/usage` 紧凑/详细模式均隐藏整个 `Run Time`/`Time` 项,不留空行或空字段。非零毫秒值、未知值 `—` 与 `Not updated`/`stale` 提示保留。耗时统计与刷新逻辑不变;窄终端没有 Time 时仍保留可用指标,而不是只剩标题。
174
+
175
+ 环境:macOS、Node.js v22.23.2、npm 10.9.8,插件包 0.4.8、SDK 2.0.24、OpenTUI 0.5.14,真实宿主 OpenCode 2.0.26。
176
+
177
+ 已通过 `npm run typecheck`、163 项 `npm test`、`npm run build` 和完整 `npm run test:smoke`。新增 3 项显示回归测试,覆盖零值隐藏、非零毫秒、无效/未知时间、未更新的零值仍带提示、其他面板行不变,以及窄终端摘要的字段和分隔符。真实打包/隔离安装检查覆盖空侧边栏、两种 `/usage` 模式、空子代理摘要/浮窗与 48 列视图;生成中继续隐藏零值,完成后恢复非零耗时,原有全套集成断言均通过。
178
+
179
+ Node 22 的既有 OpenTUI 引擎警告保留。本次未升级版本、提交、推送或发布。
180
+
181
+ 验证产物:`/private/var/folders/rw/bmx6c8hd737brl55m0_ff_b80000gn/T/opencode/token-usage-smoke-sC9gcQ`,其中 `01-empty-usage-dialog.txt` 与 `01-empty-child-narrow.txt` 保存零耗时隐藏后的界面。
182
+
3
183
  ## 2026-10-09 · Run Time 移至面板末行
4
184
 
5
185
  将侧边栏与子代理完整浮窗的行序调整为 TPS → TTFT → Run Time;任一性能指标缺失时,Run Time 仍为最后一行。保留原有分组空行,统计口径与子代理单行摘要不变,`/usage` 继续在 Session 区域末行显示耗时。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenlongapps/opencode-token-usage",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "Session-tree token usage, live performance, context window and estimated cost for OpenCode 2",
5
5
  "keywords": [
6
6
  "opencode",