dsh-token-total 1.1.3 → 1.1.5

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/README.md CHANGED
@@ -15,6 +15,7 @@ DSH(DeepSeek Harness)Web 插件:**Token 用量统计看板** + **单会话
15
15
  ### 统计看板
16
16
 
17
17
  入口在左侧边栏的插件区(与原生「插件」页并列的独立页面),点开即为看板,分上中下三部分。
18
+ ![统计看板](assets/image.png)
18
19
 
19
20
  #### 上部
20
21
 
@@ -40,6 +41,8 @@ DSH(DeepSeek Harness)Web 插件:**Token 用量统计看板** + **单会话
40
41
 
41
42
  会话输入框下方原生的「用时与速度 / Token 用量」两枚胶囊被接管并增强:
42
43
 
44
+ ![单会话统计](assets/image-1.png)
45
+
43
46
  - **Token 胶囊**显示的是**整棵会话树**(主智能体 + 全部子智能体)的合计,点开弹窗分为「主智能体 / 子智能体 / 会话合计」三行,并给出子智能体会话数。
44
47
  - 子智能体是独立持久化的会话,通过日志头的 `parentSession` 串成树;插件按需重放这些日志汇总(同一套 `(turn, step)` 去重规则,且跳过 fork 继承来的事件前缀,避免与父会话重复计数)。
45
48
  - **会话 Token 明细始终开启**:未命中缓存、缓存读 / 写、输出分别列出;底部胶囊的缓存命中率始终显示两位小数。
@@ -49,6 +52,7 @@ DSH(DeepSeek Harness)Web 插件:**Token 用量统计看板** + **单会话
49
52
  ### 会话「用量」页
50
53
 
51
54
  在每个会话原生的「对话 / 轨迹」旁新增 **用量** tab;沿用会话切换与视图导航,不另造顶部栏或输入框。
55
+ ![会话用量页](assets/image-2.png)
52
56
 
53
57
  - **两张实时卡**(形制参考 [magpie](https://github.com/yetone/magpie))并排位于子代理/模型明细之下、请求记录之上,回答「提示词里现在有什么」与「整场会话到底花了什么」两个不同的问题:
54
58
  - 左卡「**当前上下文**」:`已用 / 模型窗口 tokens` 大字附占用百分比,标题行右侧是当前供应商 · 模型;下方一条**整条即模型窗口**的堆叠条——有颜色的部分是已用的七类(按类别占比分色),尾部是还没用上的余量,所以条本身就是占用率;再下面按统一图例列出系统提示词、用户消息、技能注入、工具结果、工具定义、注入内容、助手消息各自的占比与估算 token(输出不占窗口,故不列)。鼠标悬浮可读精确 token 数与占上下文比例;模型窗口未知时只写已用数。
@@ -56,7 +60,7 @@ DSH(DeepSeek Harness)Web 插件:**Token 用量统计看板** + **单会话
56
60
  - 两张卡都由会话明细轮询驱动(每 1.5 秒一次),不额外起计时器;旧宿主只提供其中一份数据时,另一张卡显示空态而非整组消失,两份都缺时整组不渲染。窄视口下自动上下堆叠。
57
61
 
58
62
  - 浅色页面两行统计卡:第一行请求数量(附失败数)、总 tokens(附精确值)、缓存命中率(附命中 token)、token 速度(附平均首 token);第二行输入、输出、缓存读取、缓存写入四个 token 桶。不显示费用。
59
- - 子代理明细使用每个子代理的**真实名称**(取自子代理标签,缺失时回退到会话标题、工作目录或会话 id)。
63
+ - 子代理明细与请求记录使用每个子代理的**真实名称**:优先子代理自身标签,再取最新非空会话标题;旧 CLI 记录缺少标题时,读取本次会话树中实际父会话的 `subagent/catalog` 任务标签,最后才回退到日志头名称、工作目录或会话 id。只使用各日志的自有事件,不把继承的父标题/目录当作自身名称,不改写历史日志,也不改变 Token 统计。若直接打开旧子会话、实际父会话不在本次读取树中,则保留原有回退名称。
60
64
  - 中部为等宽、等高的 **子代理明细** 与 **按模型明细**;窄视口自动堆叠。使用固定列布局而非横向滚动,长名称省略并可悬浮查看全名,token 数同样可悬浮查看精确值。按模型明细的名称使用模型选择器的**显示名称**(路由 id 保留在悬浮提示中,无法解析时回退为 id);当多行显示名称相同(忽略大小写与分隔符)时,在名称下方用灰色小字标明各自供应商以便区分。
61
65
  - 请求记录按**轮**折叠(压缩调用归入独立分组),**默认全部折叠**;展开某一轮可查看该轮每一次请求的时间、模型、tokens、命中率、状态与入 / 出 / 读 / 写明细,支持单组展开和全部展开 / 收起。失败尝试、实际重试、压缩调用纳入统计;仅继承的父会话历史不重复计算。
62
66
  - 页面每 1.5 秒串行读取当前会话明细,切换或离开 tab 会取消请求和计时器。统计含本会话及所有后代的自有历史日志,缺失的首 token / 速度显示 `—`,读取错误保留上次成功结果。
package/lib/client.js CHANGED
@@ -31531,7 +31531,8 @@ window.__ModuleLoader__.load({
31531
31531
  self,
31532
31532
  subagents: tree?.subagents ?? { ...ZERO },
31533
31533
  total: tree === null ? self : combinedTotals(tree, self),
31534
- subagentSessions: tree?.subagentSessions ?? 0
31534
+ subagentSessions: tree?.subagentSessions ?? 0,
31535
+ ...tree?.coverage ? { coverage: tree.coverage } : {}
31535
31536
  };
31536
31537
  }
31537
31538
  function hasAnyTokens(totals) {
@@ -31901,9 +31902,11 @@ window.__ModuleLoader__.load({
31901
31902
  }
31902
31903
  function UsagePill({ usage, tree, t, dialog, dock }) {
31903
31904
  const { open, setOpen, rootRef, panelRef, pos } = useStatDialog(dialog);
31904
- const billed = sessionUsageForDisplay(usage, tree?.tree ?? null).total;
31905
+ const display = sessionUsageForDisplay(usage, tree?.tree ?? null);
31906
+ const billed = display.total;
31905
31907
  const total = billedTotal(billed);
31906
- const totalText = t("message.turnUsage.count", { count: formatTokensCompact(total) });
31908
+ const incomplete = display.coverage?.complete === false;
31909
+ const totalText = (incomplete ? "≥ " : "") + t("message.turnUsage.count", { count: formatTokensCompact(total) });
31907
31910
  const cacheHit = cacheHitPercent(billed);
31908
31911
  const cacheHitText = cacheHit !== null ? t("stats.cacheHit", { percent: cacheHit }) : null;
31909
31912
  const exactCount = (value) => t("message.turnUsage.count", { count: formatTokens(value) });
@@ -31916,7 +31919,8 @@ window.__ModuleLoader__.load({
31916
31919
  className: StatsLineEnhanced_module_default.pill,
31917
31920
  "aria-haspopup": "dialog",
31918
31921
  "aria-expanded": open,
31919
- "aria-label": cacheHitText === null ? totalText : totalText + " · " + cacheHitText,
31922
+ "aria-label": (incomplete ? t("stats.partial") + " · " : "") + (cacheHitText === null ? totalText : totalText + " · " + cacheHitText),
31923
+ title: incomplete ? t("stats.incomplete", { count: display.coverage.unreadableSessions.length }) : void 0,
31920
31924
  onClick: () => {
31921
31925
  setOpen(!open);
31922
31926
  },
@@ -31943,13 +31947,18 @@ window.__ModuleLoader__.load({
31943
31947
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DatabaseIcon, {}), t("stats.dialog.usageTitle")]
31944
31948
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
31945
31949
  className: StatsLineEnhanced_module_default.dialogTitleValue,
31946
- children: exactCount(total)
31950
+ children: (incomplete ? "≥ " : "") + exactCount(total)
31947
31951
  })]
31948
31952
  }),
31949
31953
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
31950
31954
  className: StatsLineEnhanced_module_default.dialogTitleRule,
31951
31955
  "aria-hidden": true
31952
31956
  }),
31957
+ incomplete && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
31958
+ role: "status",
31959
+ "data-session-usage-partial": true,
31960
+ children: t("stats.incomplete", { count: display.coverage.unreadableSessions.length })
31961
+ }),
31953
31962
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
31954
31963
  className: StatsLineEnhanced_module_default.dialogDetails,
31955
31964
  "data-session-stats-usage": true,
@@ -31982,7 +31991,8 @@ window.__ModuleLoader__.load({
31982
31991
  const stats = (0, react.useMemo)(() => projected ?? deriveStats(settledNodes ?? []), [projected, settledNodes]);
31983
31992
  const tokenScale = (0, react.useMemo)(() => measuredTokenScale(settledNodes ?? []) ?? 1, [settledNodes]);
31984
31993
  const tree = useSessionUsage(sessionId, usageSignature(usage, stats.steps));
31985
- const hasTokens = hasAnyTokens(sessionUsageForDisplay(usage, tree.tree).total);
31994
+ const display = sessionUsageForDisplay(usage, tree.tree);
31995
+ const hasTokens = hasAnyTokens(display.total) || display.coverage?.complete === false;
31986
31996
  if (stats.steps === 0 && !hasTokens) return null;
31987
31997
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
31988
31998
  ref: markDockContainer,
@@ -32048,7 +32058,8 @@ window.__ModuleLoader__.load({
32048
32058
  const StatsUsageEntry = (0, react.memo)(function StatsUsageEntry({ useProjection, t, sessionId }) {
32049
32059
  const usage = useProjection("tokenUsage");
32050
32060
  const tree = useSessionUsage(sessionId, usageSignature(usage, usage === void 0 ? 0 : 1));
32051
- if (!hasAnyTokens(sessionUsageForDisplay(usage, tree.tree).total)) return null;
32061
+ const display = sessionUsageForDisplay(usage, tree.tree);
32062
+ if (!(hasAnyTokens(display.total) || display.coverage?.complete === false)) return null;
32052
32063
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UsagePill, {
32053
32064
  usage,
32054
32065
  tree,
@@ -32420,6 +32431,8 @@ window.__ModuleLoader__.load({
32420
32431
  "stats.cacheMiss": "Cache miss",
32421
32432
  "stats.dialog.title": "Time & speed",
32422
32433
  "stats.dialog.usageTitle": "Token usage",
32434
+ "stats.partial": "Partial usage",
32435
+ "stats.incomplete": "{count} subagent session(s) could not be read; totals are incomplete.",
32423
32436
  "stats.dialog.llmTime": "LLM time",
32424
32437
  "stats.dialog.toolTime": "Tool time",
32425
32438
  "stats.dialog.ttft": "Mean TTFT",
@@ -32508,6 +32521,8 @@ window.__ModuleLoader__.load({
32508
32521
  "stats.cacheMiss": "未命中缓存",
32509
32522
  "stats.dialog.title": "用时与速度",
32510
32523
  "stats.dialog.usageTitle": "Token 用量",
32524
+ "stats.partial": "部分用量",
32525
+ "stats.incomplete": "有 {count} 个子会话暂不可读取,总计尚不完整。",
32511
32526
  "stats.dialog.llmTime": "LLM 用时",
32512
32527
  "stats.dialog.toolTime": "工具用时",
32513
32528
  "stats.dialog.ttft": "首 token 平均",
@@ -32596,6 +32611,8 @@ window.__ModuleLoader__.load({
32596
32611
  "stats.cacheMiss": "未命中快取",
32597
32612
  "stats.dialog.title": "用時與速度",
32598
32613
  "stats.dialog.usageTitle": "Token 用量",
32614
+ "stats.partial": "部分用量",
32615
+ "stats.incomplete": "有 {count} 個子會話暫不可讀取,總計尚不完整。",
32599
32616
  "stats.dialog.llmTime": "LLM 用時",
32600
32617
  "stats.dialog.toolTime": "工具用時",
32601
32618
  "stats.dialog.ttft": "首 token 平均",
@@ -33844,6 +33861,15 @@ window.__ModuleLoader__.load({
33844
33861
  className: SessionUsageView_module_default.scope,
33845
33862
  children: t("scope")
33846
33863
  }),
33864
+ detail.coverage?.complete === false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
33865
+ className: SessionUsageView_module_default.error,
33866
+ role: "status",
33867
+ "data-session-usage-partial": true,
33868
+ children: [t("partialCoverage", { count: String(detail.coverage.unreadableSessions.length) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
33869
+ title: detail.coverage.unreadableSessions.map((issue) => issue.sessionId).join("\n"),
33870
+ children: t("partialTotals")
33871
+ })]
33872
+ }),
33847
33873
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Metrics, {
33848
33874
  detail,
33849
33875
  t
@@ -33972,6 +33998,8 @@ window.__ModuleLoader__.load({
33972
33998
  noModels: "暂无模型用量",
33973
33999
  scope: "本会话及所有子代理 · 历史日志累计",
33974
34000
  note: "统计包含本会话与全部后代的独立请求、重试和压缩调用,不重复计算继承历史。首 token 与速度仅使用已记录的时间信息,缺失时显示 —。",
34001
+ partialCoverage: "有 {count} 个子会话暂不可读取。",
34002
+ partialTotals: "当前仅显示已读取用量,未将缺失用量视为零。",
33975
34003
  failedLoad: "用量读取失败,已保留上次结果。",
33976
34004
  retry: "重新读取",
33977
34005
  completed: "已完成",
@@ -34080,6 +34108,8 @@ window.__ModuleLoader__.load({
34080
34108
  noModels: "No model usage yet",
34081
34109
  scope: "This session and all subagents · Historical log totals",
34082
34110
  note: "Includes independent requests, retries and compaction calls from this session and all descendants, without counting inherited history twice. Timing uses recorded timestamps only; missing values appear as —.",
34111
+ partialCoverage: "{count} subagent session(s) could not be read.",
34112
+ partialTotals: "Only readable usage is shown; missing usage is not treated as zero.",
34083
34113
  failedLoad: "Could not read usage. The last successful result is preserved.",
34084
34114
  retry: "Retry",
34085
34115
  completed: "Completed",
@@ -34188,6 +34218,8 @@ window.__ModuleLoader__.load({
34188
34218
  noModels: "暫無模型用量",
34189
34219
  scope: "本會話及所有子代理 · 歷史日誌累計",
34190
34220
  note: "統計包含本會話與全部後代的獨立請求、重試和壓縮呼叫,不重複計算繼承歷史。首 token 與速度僅使用已記錄的時間資訊,缺失時顯示 —。",
34221
+ partialCoverage: "有 {count} 個子會話暫不可讀取。",
34222
+ partialTotals: "目前僅顯示已讀取用量,未將缺失用量視為零。",
34191
34223
  failedLoad: "用量讀取失敗,已保留上次結果。",
34192
34224
  retry: "重新讀取",
34193
34225
  completed: "已完成",