dsh-context 0.54.0 → 0.54.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.
package/lib/client.js CHANGED
@@ -56,6 +56,8 @@ window.__ModuleLoader__.load({
56
56
  "timing.callTimes": "{n}次",
57
57
  "timing.toolTimes": "{n}次",
58
58
  "timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
59
+ "timing.tps": "{tps} tok/s",
60
+ "timing.tpsTip": "输出速度(TPS):累计解码输出 tokens ÷ 累计解码时长,与 DeepSeek Harness 会话统计同口径。",
59
61
  "tokens.title": "Token 统计",
60
62
  "tokens.total": "总用量",
61
63
  "tokens.output": "输出",
@@ -320,6 +322,7 @@ window.__ModuleLoader__.load({
320
322
  "ov.title": "上下文洞察",
321
323
  "ov.unavailable": "会话列表暂不可用",
322
324
  "ov.range.label": "统计范围",
325
+ "ov.range.24h": "近 24 小时",
323
326
  "ov.range.7d": "近 7 天",
324
327
  "ov.range.30d": "近 30 天",
325
328
  "ov.range.all": "全部",
@@ -330,7 +333,6 @@ window.__ModuleLoader__.load({
330
333
  "ov.kpi.toolSub": "工具执行 {dur}",
331
334
  "ov.kpi.wallSub": "模型调用 {n}",
332
335
  "balance.title": "DeepSeek 余额",
333
- "balance.tip.total": "总余额",
334
336
  "balance.tip.granted": "赠送余额",
335
337
  "balance.tip.toppedUp": "充值余额",
336
338
  "ov.heat.title": "活跃度",
@@ -426,6 +428,8 @@ window.__ModuleLoader__.load({
426
428
  "timing.callTimes": "{n} calls",
427
429
  "timing.toolTimes": "{n} runs",
428
430
  "timing.empty": "No timing data yet — durations appear as the conversation runs",
431
+ "timing.tps": "{tps} tok/s",
432
+ "timing.tpsTip": "Output speed (TPS): summed decoded output tokens over summed decode time — the same pairing as the harness session stats.",
429
433
  "tokens.title": "Token Stats",
430
434
  "tokens.total": "Total",
431
435
  "tokens.output": "Output",
@@ -690,6 +694,7 @@ window.__ModuleLoader__.load({
690
694
  "ov.title": "Context Insights",
691
695
  "ov.unavailable": "The session list is unavailable",
692
696
  "ov.range.label": "Range",
697
+ "ov.range.24h": "Last 24 hours",
693
698
  "ov.range.7d": "Last 7 days",
694
699
  "ov.range.30d": "Last 30 days",
695
700
  "ov.range.all": "All",
@@ -700,7 +705,6 @@ window.__ModuleLoader__.load({
700
705
  "ov.kpi.toolSub": "tool runs {dur}",
701
706
  "ov.kpi.wallSub": "{n} model calls",
702
707
  "balance.title": "DeepSeek balance",
703
- "balance.tip.total": "Total balance",
704
708
  "balance.tip.granted": "Granted balance",
705
709
  "balance.tip.toppedUp": "Topped-up balance",
706
710
  "ov.heat.title": "Activity",
@@ -5978,6 +5982,7 @@ window.__ModuleLoader__.load({
5978
5982
  }
5979
5983
  /** The range window's start instant (epoch ms), or null for "all". */
5980
5984
  function rangeStartOf(range, now) {
5985
+ if (range === "24h") return now - 864e5;
5981
5986
  if (range === "7d") return now - 6048e5;
5982
5987
  if (range === "30d") return now - 2592e6;
5983
5988
  return null;
@@ -6209,8 +6214,8 @@ window.__ModuleLoader__.load({
6209
6214
  //#endregion
6210
6215
  //#region src/client/balance.ts
6211
6216
  const BALANCE_ROUTE = "/api/dsh-context/balance";
6212
- /** How long a settled answer (including a `null`) serves identical reads. */
6213
- const TTL_MS$1 = 6e4;
6217
+ /** Where the last live figure is remembered across opens, per browser. */
6218
+ const STORAGE_KEY = "dsh-context:platform-balance";
6214
6219
  /** One platform amount ('110.00', or an already-numeric producer variant), or null. */
6215
6220
  function amountOf(value) {
6216
6221
  const n = typeof value === "string" ? Number(value) : value;
@@ -6260,45 +6265,97 @@ window.__ModuleLoader__.load({
6260
6265
  return balance.balances.find((entry) => entry.currency === want) ?? balance.balances[0];
6261
6266
  }
6262
6267
  let cached$1 = null;
6268
+ /** The route read in flight, so a second open joins it instead of starting one. */
6269
+ let refreshing = null;
6270
+ /**
6271
+ * The landing callbacks of every open riding the in-flight read — all
6272
+ * notified when it lands, so an open that JOINS mid-flight (a fast
6273
+ * close-reopen, a second capsule) settles on the live figure too, exactly
6274
+ * as this function's contract promises (issue #82). Cleared when the read
6275
+ * settles: the next open starts a fresh read and a fresh set.
6276
+ */
6277
+ const landingCallbacks = /* @__PURE__ */ new Set();
6278
+ /** The storage this module reads and writes; swapped in tests. */
6279
+ let storage;
6280
+ function storageFn() {
6281
+ if (storage !== void 0) return storage;
6282
+ try {
6283
+ const found = globalThis.localStorage;
6284
+ storage = found === null || found === void 0 ? null : found;
6285
+ } catch {
6286
+ storage = null;
6287
+ }
6288
+ return storage;
6289
+ }
6290
+ /** The stored figure, or `null` when absent, unreadable, or no longer a valid balance. */
6291
+ function storedBalance() {
6292
+ try {
6293
+ const raw = storageFn()?.getItem(STORAGE_KEY);
6294
+ return raw === null || raw === void 0 ? null : platformBalanceOf(JSON.parse(raw));
6295
+ } catch {
6296
+ return null;
6297
+ }
6298
+ }
6299
+ function rememberBalance(value) {
6300
+ try {
6301
+ storageFn()?.setItem(STORAGE_KEY, JSON.stringify(value));
6302
+ } catch {}
6303
+ }
6304
+ /** One route POST, narrowed through `platformBalanceOf`. Never rejects. */
6305
+ async function readRoute() {
6306
+ try {
6307
+ const response = await fetch(BALANCE_ROUTE, {
6308
+ method: "POST",
6309
+ headers: { "content-type": "application/json" }
6310
+ });
6311
+ if (!response.ok) return null;
6312
+ const r = asRecord(await response.json());
6313
+ if (r === null || r.ok !== true || r.value === null || r.value === void 0) return null;
6314
+ return platformBalanceOf(r.value);
6315
+ } catch {
6316
+ return null;
6317
+ }
6318
+ }
6263
6319
  /**
6264
- * The capsule's read: one route POST, narrowed through
6265
- * `platformBalanceOf`, deduplicated in flight, TTL-cached. Never rejects.
6266
- */
6267
- function fetchPlatformBalance() {
6268
- if (cached$1 !== null && Date.now() - cached$1.at < TTL_MS$1) return cached$1.promise;
6269
- const at = Date.now();
6270
- const promise = (async () => {
6271
- try {
6272
- const response = await fetch(BALANCE_ROUTE, {
6273
- method: "POST",
6274
- headers: { "content-type": "application/json" }
6275
- });
6276
- if (!response.ok) return null;
6277
- const r = asRecord(await response.json());
6278
- if (r === null || r.ok !== true || r.value === null || r.value === void 0) return null;
6279
- return platformBalanceOf(r.value);
6280
- } catch {
6281
- return null;
6282
- }
6283
- })();
6284
- cached$1 = {
6285
- at,
6286
- promise
6287
- };
6288
- return promise;
6320
+ * One route read per open, with `onRefresh` once it lands on a figure: the
6321
+ * capsule paints the remembered total immediately and settles on the live
6322
+ * one. `refreshing` is assigned before anything awaits, so an open arriving
6323
+ * while a read is still in flight joins it rather than starting a second one;
6324
+ * its callback joins the landing set, so the read's landing reports to EVERY
6325
+ * open riding it — the joiner included (issue #82). A failed or absent read
6326
+ * notifies nobody and keeps serving the remembered figure, so an offline open
6327
+ * never trades a number for a blank pill; `null` (whether pending, absent, or
6328
+ * failed) means nothing to show yet.
6329
+ */
6330
+ function readPlatformBalance(onRefresh) {
6331
+ landingCallbacks.add(onRefresh);
6332
+ if (refreshing === null) refreshing = (async () => {
6333
+ const value = await readRoute();
6334
+ if (value === null) return;
6335
+ cached$1 = value;
6336
+ rememberBalance(value);
6337
+ for (const waiter of landingCallbacks) waiter(value);
6338
+ })().finally(() => {
6339
+ refreshing = null;
6340
+ landingCallbacks.clear();
6341
+ });
6342
+ return cached$1 ?? storedBalance();
6289
6343
  }
6290
6344
  //#endregion
6291
6345
  //#region src/client/components/balanceCapsule.tsx
6292
6346
  /**
6293
6347
  * BalanceCapsule — the DeepSeek open-platform balance pill in the Context
6294
6348
  * Dashboard's header (the plugin's one account-level figure, beside the
6295
- * session-level ones). Mounted only by the dashboard header; it reads the
6296
- * plugin's balance route once per open through client/balance.ts and renders
6297
- * NOTHING while the read is pending, absent, or failed — the pill exists
6298
- * only when there is a live figure to show. The tooltip carries the
6299
- * breakdown (total / granted / topped-up) in the entry's own currency; the
6300
- * entry follows the active locale (zh → CNY) with the account's first
6301
- * currency as the fallback.
6349
+ * session-level ones). Clicking it opens the platform's usage console in a
6350
+ * new tab. Mounted only by the dashboard header; it paints the figure the
6351
+ * previous open remembered while client/balance.ts revalidates it in the
6352
+ * background, and renders NOTHING when nothing is remembered and the route
6353
+ * answers nothing — the pill exists only when there is a figure to show. The
6354
+ * non-zero parts of the breakdown (topped-up / granted) ride the harness
6355
+ * Tooltip primitive — immediate on hover, where a native `title` lags — in
6356
+ * the entry's own currency (the pill itself carries the total); the entry
6357
+ * follows the active locale (zh → CNY) with the account's first currency as
6358
+ * the fallback.
6302
6359
  */
6303
6360
  /** The symbol prefix for the currencies the platform serves; others show their code. */
6304
6361
  function symbolOf(currency) {
@@ -6306,31 +6363,25 @@ window.__ModuleLoader__.load({
6306
6363
  if (currency === "USD") return "$";
6307
6364
  return currency + " ";
6308
6365
  }
6366
+ /** The platform console page behind the capsule's click — usage and top-ups. */
6367
+ const USAGE_URL = "https://platform.deepseek.com/usage";
6309
6368
  function makeBalanceCapsule(ctx, kit) {
6310
6369
  const { t } = kit;
6311
6370
  return function BalanceCapsule() {
6312
- const [balance, setBalance] = (0, react.useState)(null);
6313
- (0, react.useEffect)(() => {
6314
- let on = true;
6315
- fetchPlatformBalance().then((v) => {
6316
- if (on) setBalance(v);
6317
- });
6318
- return () => {
6319
- on = false;
6320
- };
6321
- }, []);
6371
+ const [balance, setBalance] = (0, react.useState)(() => readPlatformBalance((v) => {
6372
+ setBalance(v);
6373
+ }));
6322
6374
  const locale = ctx.locale;
6323
6375
  const entry = balanceEntryOf(balance, (typeof locale.getLocale === "function" ? locale.getLocale().active : "en") === "zh" ? "cny" : "usd");
6324
6376
  if (entry === null) return null;
6325
6377
  const money = (amount) => symbolOf(entry.currency) + amount.toFixed(2);
6326
- const tip = [
6327
- t("balance.tip.total") + ": " + money(entry.total),
6328
- t("balance.tip.granted") + ": " + money(entry.granted),
6329
- t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)
6330
- ].join("\n");
6331
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
6378
+ const tipLines = [...entry.toppedUp > 0 ? [t("balance.tip.toppedUp") + ": " + money(entry.toppedUp)] : [], ...entry.granted > 0 ? [t("balance.tip.granted") + ": " + money(entry.granted)] : []];
6379
+ const pill = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
6332
6380
  className: "lc-ov-balance",
6333
- title: tip,
6381
+ "aria-label": tipLines.length > 0 ? tipLines.join("\n") : void 0,
6382
+ href: USAGE_URL,
6383
+ target: "_blank",
6384
+ rel: "noreferrer",
6334
6385
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
6335
6386
  className: "lc-ov-balance-label",
6336
6387
  children: t("balance.title")
@@ -6339,6 +6390,11 @@ window.__ModuleLoader__.load({
6339
6390
  children: money(entry.total)
6340
6391
  })]
6341
6392
  });
6393
+ return tipLines.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
6394
+ label: tipLines.join("\n"),
6395
+ side: "bottom",
6396
+ children: pill
6397
+ }) : pill;
6342
6398
  };
6343
6399
  }
6344
6400
  //#endregion
@@ -6790,6 +6846,7 @@ window.__ModuleLoader__.load({
6790
6846
  * closes the panel.
6791
6847
  */
6792
6848
  const RANGES = [
6849
+ "24h",
6793
6850
  "7d",
6794
6851
  "30d",
6795
6852
  "all"
@@ -9639,7 +9696,7 @@ window.__ModuleLoader__.load({
9639
9696
  return function PluginInfo() {
9640
9697
  const [latest, setLatest] = (0, react.useState)(null);
9641
9698
  (0, react.useEffect)(() => {
9642
- if ("0.54.0".includes("-dev")) return;
9699
+ if ("0.54.2".includes("-dev")) return;
9643
9700
  let on = true;
9644
9701
  fetchLatestVersion().then((v) => {
9645
9702
  if (on && v) setLatest(v);
@@ -9648,8 +9705,8 @@ window.__ModuleLoader__.load({
9648
9705
  on = false;
9649
9706
  };
9650
9707
  }, []);
9651
- const update = latest !== null && isNewerVersion(latest, "0.54.0") ? latest : null;
9652
- const nameText = "dsh-context (v0.54.0)";
9708
+ const update = latest !== null && isNewerVersion(latest, "0.54.2") ? latest : null;
9709
+ const nameText = "dsh-context (v0.54.2)";
9653
9710
  const nameValue = [nameText];
9654
9711
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9655
9712
  className: "lc-pi-update",
@@ -10380,6 +10437,14 @@ window.__ModuleLoader__.load({
10380
10437
  tools: "var(--color-teal-500)",
10381
10438
  other: "var(--color-slate-400)"
10382
10439
  };
10440
+ /**
10441
+ * The decode-throughput figure, formatted exactly as the harness's own
10442
+ * session-stats seat formats it: whole tokens from ten up, one decimal below.
10443
+ */
10444
+ function formatTokensPerSecond(tps) {
10445
+ const clamped = Math.max(0, tps);
10446
+ return clamped >= 10 ? String(Math.round(clamped)) : String(Math.round(clamped * 10) / 10);
10447
+ }
10383
10448
  function makeStatsTiming(kit, Donut) {
10384
10449
  const { t, fmt, fmtDuration, fmtShare } = kit;
10385
10450
  const SliceList = makeSliceList(kit);
@@ -10387,6 +10452,7 @@ window.__ModuleLoader__.load({
10387
10452
  const [hoverKey, setHoverKey] = (0, react.useState)(null);
10388
10453
  const timing = props.timing;
10389
10454
  const wall = timing !== null && Number.isFinite(timing.wallMs) && timing.wallMs > 0 ? timing.wallMs : 0;
10455
+ const tps = timing !== null && typeof timing.speedMs === "number" && Number.isFinite(timing.speedMs) && timing.speedMs > 0 && typeof timing.speedTokens === "number" && Number.isFinite(timing.speedTokens) && timing.speedTokens >= 0 ? formatTokensPerSecond(timing.speedTokens / (timing.speedMs / 1e3)) : null;
10390
10456
  let segments = [];
10391
10457
  let rows = [];
10392
10458
  if (timing !== null && (wall > 0 || timing.calls > 0 || timing.toolCalls > 0)) {
@@ -10495,12 +10561,16 @@ window.__ModuleLoader__.load({
10495
10561
  }
10496
10562
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10497
10563
  className: "lc-card lc-col-stats lc-col-donut flex-1 min-w-[min(360px,100%)]",
10498
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10564
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10499
10565
  className: "lc-card-title",
10500
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10566
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10501
10567
  className: "lc-card-title-text",
10502
10568
  children: t("timing.title")
10503
- })
10569
+ }), tps !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10570
+ className: "lc-timing-tps",
10571
+ title: t("timing.tpsTip"),
10572
+ children: t("timing.tps", { tps })
10573
+ })]
10504
10574
  }), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10505
10575
  className: "lc-empty",
10506
10576
  children: t("timing.empty")
@@ -11429,7 +11499,7 @@ window.__ModuleLoader__.load({
11429
11499
  }
11430
11500
  //#endregion
11431
11501
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
11432
- const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
11502
+ const css$13 = ".lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-timing-tps{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);font-variant-numeric:tabular-nums;cursor:default;border-radius:999px;flex:none;margin-left:auto;padding:1px 8px;font-size:12px;line-height:18px}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:color-mix(in srgb, var(--color-neutral-500) 18%, transparent)}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-hint{text-decoration:none}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}";
11433
11503
  const tagId$13 = "dsh-context/stats.css";
11434
11504
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
11435
11505
  const tag = document.createElement("style");
@@ -11572,7 +11642,7 @@ window.__ModuleLoader__.load({
11572
11642
  }
11573
11643
  //#endregion
11574
11644
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/overview.css.mjs
11575
- const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);cursor:default;border-radius:999px;flex:none;align-items:center;gap:6px;max-width:100%;padding:2px 10px;font-size:12px;line-height:18px;display:inline-flex}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
11645
+ const css = ".lc-ov-entry{box-sizing:border-box;width:calc(100% + 4px);height:42px;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:8px;margin:0 -2px;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.lc-ov-entry:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-ov-entry-rail{border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.lc-ov-entry-icon{flex:none}.lc-ov-entry-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;overflow:hidden}.lc-ov-backdrop{z-index:200;background:var(--dsw-alias-bg-mask-1,#00000073);justify-content:center;align-items:center;display:flex;position:fixed;inset:0;container:lc-ov/inline-size}.lc-ov-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);width:min(1120px,100vw - 32px);max-height:min(86vh,900px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;flex-direction:column;padding:16px 18px 18px;font-size:13px;display:flex;overflow:hidden}.lc-ov-head{flex-wrap:wrap;flex:none;align-items:center;gap:10px;margin-bottom:14px;display:flex}.lc-ov-head-icon{flex:none}.lc-ov-title{white-space:nowrap;font-size:15px;font-weight:600}.lc-ov-balance{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);max-width:100%;color:inherit;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 10px;font-size:12px;line-height:18px;text-decoration:none;display:inline-flex}.lc-ov-balance:hover .lc-ov-balance-value{text-decoration:underline}.lc-ov-balance-label{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-ov-balance-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-head .lc-modal-close{flex:none;margin-left:0}.lc-ov-body{flex:auto;grid-template-columns:minmax(0,3fr) minmax(0,7fr);gap:10px;min-height:0;display:grid}.lc-ov-left{scrollbar-gutter:stable;flex-direction:column;gap:10px;min-height:0;padding-right:8px;display:flex;overflow-y:auto}.lc-ov-right{scrollbar-gutter:stable;min-height:0;padding-right:8px;overflow-y:auto}@container lc-ov (width<=932px){.lc-ov-card{overflow-y:auto}.lc-ov-body{display:block}.lc-ov-left{margin-bottom:12px}.lc-ov-left,.lc-ov-right{min-height:auto;padding-right:0;overflow:visible}.lc-ov-heat-card{flex:none}}.lc-ov-left>.lc-card{box-sizing:border-box;min-width:0;margin-bottom:0}.lc-ov-kpis{flex:none;grid-template-rows:repeat(3,1fr);grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.lc-ov-kpi{align-items:center;padding:10px 8px}.lc-ov-kpi .lc-stat-value{font-size:20px}.lc-ov-heat-card{flex-direction:column;flex:1 0 auto;display:flex}.lc-heat{flex:1;justify-content:center;align-items:center;gap:4px;display:flex}.lc-heat-wds,.lc-heat-col{padding-top:13px}.lc-heat-wds{flex-direction:column;gap:3px;display:flex}.lc-heat-wd{width:10px;height:20px;color:var(--dsw-alias-label-secondary);text-align:center;flex:none;justify-content:center;align-items:center;font-size:9px;display:flex}.lc-heat-cols{gap:3px;display:flex}.lc-heat-col{flex-direction:column;gap:3px;display:flex;position:relative}.lc-heat-mon{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:9px;line-height:13px;position:absolute;top:0;left:0}.lc-heat-cell{box-sizing:border-box;background:var(--dsw-alias-bg-layer-2);cursor:default;border:0;border-radius:20%;width:20px;height:20px;padding:0}button.lc-heat-cell{cursor:pointer}button.lc-heat-cell:hover{box-shadow:0 0 0 1.5px color-mix(in srgb, var(--color-blue-500) 70%, transparent)}.lc-heat-future{background:0 0}.lc-heat-0{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent)}.lc-heat-1{background:color-mix(in srgb, var(--color-blue-500) 28%, transparent)}.lc-heat-2{background:color-mix(in srgb, var(--color-blue-500) 50%, transparent)}.lc-heat-3{background:color-mix(in srgb, var(--color-blue-500) 72%, transparent)}.lc-heat-4{background:color-mix(in srgb, var(--color-blue-500) 92%, transparent)}.lc-heat-on{box-shadow:0 0 0 1.5px var(--color-blue-500)}.lc-ov-list-head{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.lc-ov-list-title{white-space:nowrap;font-weight:600}.lc-ov-list-count{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;background:var(--dsw-alias-bg-layer-2);border-radius:8px;padding:1px 7px;font-size:11px}.lc-ov-day-chip{border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);color:var(--color-blue-500);background:color-mix(in srgb, var(--color-blue-500) 10%, transparent);cursor:pointer;white-space:nowrap;border-radius:999px;padding:1px 10px;font-family:inherit;font-size:11px;line-height:1.4}.lc-ov-search{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);min-width:110px;font:inherit;color:var(--dsw-alias-label-primary);transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;outline:none;flex:150px;padding:4px 10px;font-size:12px}.lc-ov-search:focus{border-color:var(--dsw-alias-label-secondary)}.lc-ov-search::placeholder{color:var(--dsw-alias-label-secondary)}.lc-ov-groups{flex-wrap:wrap;gap:6px;margin:-2px 0 10px;display:flex}.lc-ov-chip{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;text-overflow:ellipsis;max-width:220px;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:999px;align-items:baseline;gap:5px;padding:2px 10px;font-size:11px;line-height:1.5;display:inline-flex;overflow:hidden}.lc-ov-chip:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}.lc-ov-chip-on,.lc-ov-chip-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border-color:#0000}.lc-ov-chip-n{font-variant-numeric:tabular-nums;opacity:.65;font-size:10px}.lc-ov-chip-on .lc-ov-chip-n{opacity:.85}.lc-ov-grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px;display:grid}.lc-ov-session{text-align:left;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;font:inherit;color:inherit;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;display:flex}.lc-ov-session:hover{border-color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}.lc-ov-session-current{border-color:color-mix(in srgb, var(--color-blue-500) 65%, transparent)}.lc-ov-session-head{align-items:flex-start;gap:6px;min-width:0;display:flex}.lc-ov-running{background:var(--color-green-500);border-radius:50%;flex:none;width:7px;height:7px}.lc-ov-session-title{-webkit-line-clamp:2;overflow-wrap:anywhere;-webkit-box-orient:vertical;flex:auto;min-width:0;font-weight:600;display:-webkit-box;overflow:hidden}.lc-ov-current{color:var(--color-blue-500);border:1px solid color-mix(in srgb, var(--color-blue-500) 45%, transparent);border-radius:4px;flex:none;padding:0 4px;font-size:10px;line-height:1.5}.lc-ov-session-times{flex-direction:column;flex:none;align-items:flex-end;gap:1px;display:flex}.lc-ov-session-time{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:11px}.lc-ov-session-created{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:10px;line-height:1.4}.lc-ov-session-crumb{min-width:0;color:var(--dsw-alias-label-secondary);white-space:nowrap;align-items:center;gap:5px;font-size:11px;display:flex;overflow:hidden}.lc-ov-crumb-group{text-overflow:ellipsis;flex:none;max-width:55%;overflow:hidden}.lc-ov-crumb-sep{opacity:.55;flex:none}.lc-ov-crumb-project{text-overflow:ellipsis;min-width:0;font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 16%, transparent);border-radius:999px;flex:0 auto;padding:0 8px;font-size:10px;line-height:1.6;overflow:hidden}.lc-ov-session-body{align-items:center;gap:14px;min-width:0;display:flex}.lc-ov-mini-stats{flex-direction:column;flex:auto;gap:3px;min-width:0;display:flex}.lc-ov-mini-stat{justify-content:space-between;align-items:baseline;gap:10px;font-size:11px;display:flex}.lc-ov-mini-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none}.lc-ov-mini-value{font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:600}.lc-ov-session-empty{color:var(--dsw-alias-label-secondary);padding:6px 0;font-size:11px;line-height:1.5}.lc-ov-session-preview{border-top:1px solid color-mix(in srgb, var(--dsw-alias-border-l2) 60%, transparent);align-items:baseline;gap:6px;min-width:0;margin-top:auto;padding-top:6px;display:flex}.lc-ov-session-preview-label{color:var(--dsw-alias-label-secondary);flex:none;font-size:10px}.lc-ov-session-preview-text{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-primary);flex:0 auto;font-size:11px;line-height:1.5;overflow:hidden}.lc-ov-pager{justify-content:center;align-items:center;gap:10px;margin-top:12px;display:flex}.lc-ov-pager-btn{border:1px solid var(--dsw-alias-border-l2);width:26px;height:22px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border-radius:6px;flex:none;justify-content:center;align-items:center;line-height:1;display:flex}.lc-ov-pager-btn:hover:enabled{border-color:var(--dsw-alias-label-secondary)}.lc-ov-pager-btn:disabled{opacity:.4;cursor:default}.lc-ov-pager-n{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}";
11576
11646
  const tagId = "dsh-context/overview.css";
11577
11647
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
11578
11648
  const tag = document.createElement("style");
package/lib/index.d.ts CHANGED
@@ -638,6 +638,17 @@ interface TimingTotals {
638
638
  textMs?: number;
639
639
  /** Tool-call-argument decode slice of `genMs`. */
640
640
  toolArgMs?: number;
641
+ /**
642
+ * The decode-throughput seat, paired exactly as the harness's own
643
+ * session-stats fold pairs them: `speedTokens` sums provider-reported
644
+ * output tokens and `speedMs` the first-token → assistant-message
645
+ * windows, over the calls that carried BOTH a first-token stamp and a
646
+ * usage report — a subset of `genMs`, which counts every token-stamped
647
+ * call regardless of usage. Additive-optional: cached rows written before
648
+ * the seat existed lack them, and the card falls back to no chip.
649
+ */
650
+ speedTokens?: number;
651
+ speedMs?: number;
641
652
  /** Completed model calls (assistant messages folded). */
642
653
  calls: number;
643
654
  /** Summed per-call durations of completed tool calls. */
@@ -800,7 +811,9 @@ interface ContextHeaders {
800
811
  interface PlatformBalanceEntry {
801
812
  /** The ISO code the platform reported (`CNY` / `USD`). */
802
813
  currency: string;
803
- /** Total available funds: the granted and topped-up balances summed. */
814
+ /** Total available funds: `granted` + `toppedUp`. Derived here rather than read
815
+ * from the platform's own `total_balance`, which rounds independently of its
816
+ * parts and can land a cent away from what the breakdown beside it shows. */
804
817
  total: number;
805
818
  /** The not-expired granted (gift) balance. */
806
819
  granted: number;
package/lib/index.js CHANGED
@@ -1430,11 +1430,12 @@ function applyTimeline(state, event, bounds) {
1430
1430
  };
1431
1431
  if (data && typeof data.turn === "number") record.turn = data.turn;
1432
1432
  if (data && typeof data.step === "number") record.step = data.step;
1433
+ let output = null;
1433
1434
  if (usage !== null && typeof usage === "object") {
1434
1435
  const input = tokenCountOf(usage.inputTokens);
1435
1436
  const cacheRead = tokenCountOf(usage.cacheReadTokens);
1436
1437
  const cacheWrite = tokenCountOf(usage.cacheWriteTokens);
1437
- const output = tokenCountOf(usage.outputTokens);
1438
+ output = tokenCountOf(usage.outputTokens);
1438
1439
  if (input !== null || cacheRead !== null || cacheWrite !== null || output !== null) {
1439
1440
  record.prompt = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
1440
1441
  if (cacheRead !== null) record.cacheRead = cacheRead;
@@ -1456,6 +1457,10 @@ function applyTimeline(state, event, bounds) {
1456
1457
  if (firstToken !== void 0) {
1457
1458
  timing.ttftMs += durOf(stepStart.time, firstToken);
1458
1459
  timing.genMs += durOf(firstToken, event.time);
1460
+ if (output !== null) {
1461
+ timing.speedMs = (timing.speedMs ?? 0) + durOf(firstToken, event.time);
1462
+ timing.speedTokens = (timing.speedTokens ?? 0) + output;
1463
+ }
1459
1464
  if (stepStart.decode !== void 0) {
1460
1465
  const decode = { ...stepStart.decode };
1461
1466
  if (stepStart.block !== void 0) decode[stepStart.block.kind] += durOf(stepStart.block.since, event.time);
@@ -2066,9 +2071,6 @@ const DEEPSEEK_SETTINGS_NS = "llm-deepseek";
2066
2071
  const DEFAULT_API_KEY_ENV = "DEEPSEEK_API_KEY";
2067
2072
  /** The platform's public API root (llm-deepseek's PUBLIC_BASE_URL default). */
2068
2073
  const PUBLIC_BASE_URL = "https://api.deepseek.com";
2069
- /** A settled read serves identical reads for this long; failures decay faster. */
2070
- const CACHE_TTL_MS = 3e5;
2071
- const CACHE_FAIL_TTL_MS = 6e4;
2072
2074
  /** One platform read's whole budget — never worth blocking the route longer. */
2073
2075
  const FETCH_TIMEOUT_MS = 1e4;
2074
2076
  /** Narrow an unknown value to a string-keyed record, or null. */
@@ -2109,9 +2111,12 @@ async function resolveFacts(ctx) {
2109
2111
  }
2110
2112
  /**
2111
2113
  * Narrow the platform's payload to the wire value (the boundary rigor every
2112
- * parser owes untrusted input): each entry's currency and three amounts are
2113
- * re-proved, an entry failing the shape drops whole, and a payload with no
2114
- * valid entry is no balance at all.
2114
+ * parser owes untrusted input): each entry's currency and parts are re-proved
2115
+ * and the total is the two summed — the platform's own `total_balance` rounds
2116
+ * independently of them, so it can land a cent away from what the breakdown
2117
+ * beside it adds up to, and the viewer's arithmetic is the authority. An entry
2118
+ * failing the shape drops whole, and a payload with no valid entry is no
2119
+ * balance at all.
2115
2120
  */
2116
2121
  function balanceOfPayload(value) {
2117
2122
  const data = asRecord$1(value);
@@ -2122,13 +2127,12 @@ function balanceOfPayload(value) {
2122
2127
  const entry = asRecord$1(info);
2123
2128
  if (entry === null) continue;
2124
2129
  const currency = entry.currency;
2125
- const total = amountOf(entry.total_balance);
2126
2130
  const granted = amountOf(entry.granted_balance);
2127
2131
  const toppedUp = amountOf(entry.topped_up_balance);
2128
- if (typeof currency !== "string" || currency === "" || total === null || granted === null || toppedUp === null) continue;
2132
+ if (typeof currency !== "string" || currency === "" || granted === null || toppedUp === null) continue;
2129
2133
  balances.push({
2130
2134
  currency,
2131
- total,
2135
+ total: granted + toppedUp,
2132
2136
  granted,
2133
2137
  toppedUp
2134
2138
  });
@@ -2143,18 +2147,17 @@ function balanceOfPayload(value) {
2143
2147
  /** The outbound platform fetch through the real global fetch. */
2144
2148
  const defaultFetcher = (url, init) => fetch(url, init);
2145
2149
  let fetcher = defaultFetcher;
2146
- let cache = null;
2147
2150
  let inFlight = null;
2148
2151
  /**
2149
- * One platform read for these facts, shared across identical concurrent
2150
- * reads and remembered briefly: successes for CACHE_TTL_MS, failures for
2151
- * CACHE_FAIL_TTL_MS (a blip must not pin the capsule away for the full
2152
- * window). Every failure path — transport, timeout, non-ok status, malformed
2153
- * payload — resolves `null`; the route never throws into the transport.
2152
+ * One platform read for these facts, shared by the identical reads already in
2153
+ * flight. Each read that finds none goes to the platform: the account's
2154
+ * balance moves with every request the harness bills, so a remembered figure
2155
+ * would disagree with the console page the capsule links to. The client's own
2156
+ * open-time read is the rate limit. Every failure path — transport, timeout,
2157
+ * non-ok status, malformed payload — resolves `null`; the route never throws
2158
+ * into the transport.
2154
2159
  */
2155
2160
  async function readBalance(facts) {
2156
- const key = facts.baseUrl + "\0" + facts.apiKey;
2157
- if (cache !== null && cache.key === key && Date.now() - cache.at < (cache.value === null ? CACHE_FAIL_TTL_MS : CACHE_TTL_MS)) return cache.value;
2158
2161
  if (inFlight !== null) return inFlight;
2159
2162
  inFlight = (async () => {
2160
2163
  let value = null;
@@ -2168,11 +2171,6 @@ async function readBalance(facts) {
2168
2171
  });
2169
2172
  if (response.ok) value = balanceOfPayload(await response.json());
2170
2173
  } catch {}
2171
- cache = {
2172
- key,
2173
- at: Date.now(),
2174
- value
2175
- };
2176
2174
  return value;
2177
2175
  })();
2178
2176
  const settled = inFlight;
@@ -2891,6 +2889,8 @@ const timingTotalsSchema = z.object({
2891
2889
  reasoningMs: z.number().nonnegative().optional(),
2892
2890
  textMs: z.number().nonnegative().optional(),
2893
2891
  toolArgMs: z.number().nonnegative().optional(),
2892
+ speedTokens: z.number().nonnegative().optional(),
2893
+ speedMs: z.number().nonnegative().optional(),
2894
2894
  calls: z.number().int().nonnegative(),
2895
2895
  toolsMs: z.number().nonnegative(),
2896
2896
  toolCalls: z.number().int().nonnegative(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.54.0",
3
+ "version": "0.54.2",
4
4
  "description": "A DeepSeek Harness plugin for context insight and management, with context dashboard and context command, for understanding how the context is made of, and how it evolves.",
5
5
  "author": "bowenliang123",
6
6
  "repository": {