dsh-context 0.57.0 → 0.58.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.
Files changed (3) hide show
  1. package/lib/client.js +255 -112
  2. package/lib/index.js +6 -3
  3. package/package.json +2 -2
package/lib/client.js CHANGED
@@ -354,6 +354,7 @@ window.__ModuleLoader__.load({
354
354
  "ov.kpi.ofTotal": "共 {n} 个",
355
355
  "ov.kpi.tokens": "累计消耗",
356
356
  "ov.kpi.sessionsSub": "含 {n} 个会话",
357
+ "ov.kpi.pricedSub": "已计价 {n}/{total} 个会话",
357
358
  "ov.kpi.toolSub": "工具执行 {dur}",
358
359
  "ov.kpi.wallSub": "模型调用 {n}",
359
360
  "ov.stats.empty": "统计范围内暂无 Token 用量",
@@ -734,6 +735,7 @@ window.__ModuleLoader__.load({
734
735
  "ov.kpi.ofTotal": "of {n} total",
735
736
  "ov.kpi.tokens": "Tokens Used",
736
737
  "ov.kpi.sessionsSub": "across {n} sessions",
738
+ "ov.kpi.pricedSub": "{n} of {total} sessions priced",
737
739
  "ov.kpi.toolSub": "tool runs {dur}",
738
740
  "ov.kpi.wallSub": "{n} model calls",
739
741
  "ov.stats.empty": "No billed tokens in this range",
@@ -2906,6 +2908,7 @@ window.__ModuleLoader__.load({
2906
2908
  const BAR_W = 14;
2907
2909
  const BAR_GAP = 2;
2908
2910
  const STAGGER_CAP = 20;
2911
+ const STEP_FLAG_EVERY = 5;
2909
2912
  const TURN_FILLS = ["color-mix(in srgb, var(--color-neutral-500) 12%, transparent)", "color-mix(in srgb, var(--color-neutral-500) 26%, transparent)"];
2910
2913
  const LABEL_OVERHANG = 48;
2911
2914
  const LABEL_GAP = 2;
@@ -2962,62 +2965,73 @@ window.__ModuleLoader__.load({
2962
2965
  onMouseEnter: () => {
2963
2966
  props.onHover(req.seq);
2964
2967
  },
2965
- children: [marker !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2966
- className: "lc-bar-marker",
2967
- title: "✂ " + (markerAt !== null ? markerAt + " — " : "") + eventLabel(marker),
2968
- children: "✂"
2969
- }) : null, diverge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2970
- className: "lc-bar-up animate-lc-bar-in motion-reduce:animate-none",
2971
- style: {
2972
- bottom: `${props.downPx}px`,
2973
- ...enterStyle
2974
- },
2975
- children: CATS.map((c) => {
2976
- const d = req[c.key] || 0;
2977
- if (d <= 0) return null;
2978
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2979
- "data-cat": c.key,
2980
- className: "lc-cat-seg",
2981
- style: {
2982
- height: `${Math.max(1, Math.round(d * props.deltaScale))}px`,
2983
- background: c.color
2984
- }
2985
- }, c.key);
2986
- })
2987
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2988
- className: "lc-bar-down animate-lc-bar-in motion-reduce:animate-none",
2989
- style: {
2990
- top: `${props.upPx}px`,
2991
- ...enterStyle
2992
- },
2993
- children: CATS.map((c) => {
2994
- const d = req[c.key] || 0;
2995
- if (d >= 0) return null;
2996
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2997
- "data-cat": c.key,
2998
- className: "lc-cat-seg",
2999
- style: {
3000
- height: `${Math.max(1, Math.round(-d * props.deltaScale))}px`,
3001
- background: c.color
3002
- }
3003
- }, c.key);
3004
- })
3005
- })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3006
- className: "lc-bar-stack animate-lc-bar-in motion-reduce:animate-none",
3007
- style: enterStyle,
3008
- children: CATS.map((c) => {
3009
- const v = req[c.key] || 0;
3010
- if (!v) return null;
3011
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3012
- "data-cat": c.key,
3013
- className: "lc-cat-seg",
3014
- style: {
3015
- height: `${Math.max(1, Math.round(v / props.maxTotal * CHART_H))}px`,
3016
- background: c.color
3017
- }
3018
- }, c.key);
2968
+ children: [
2969
+ props.flag !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2970
+ className: "lc-step-flag",
2971
+ "aria-hidden": "true",
2972
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2973
+ className: "lc-step-flag-label",
2974
+ children: props.flag
2975
+ })
2976
+ }) : null,
2977
+ marker !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2978
+ className: "lc-bar-marker",
2979
+ title: "✂ " + (markerAt !== null ? markerAt + " — " : "") + eventLabel(marker),
2980
+ children: "✂"
2981
+ }) : null,
2982
+ diverge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2983
+ className: "lc-bar-up animate-lc-bar-in motion-reduce:animate-none",
2984
+ style: {
2985
+ bottom: `${props.downPx}px`,
2986
+ ...enterStyle
2987
+ },
2988
+ children: CATS.map((c) => {
2989
+ const d = req[c.key] || 0;
2990
+ if (d <= 0) return null;
2991
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2992
+ "data-cat": c.key,
2993
+ className: "lc-cat-seg",
2994
+ style: {
2995
+ height: `${Math.max(1, Math.round(d * props.deltaScale))}px`,
2996
+ background: c.color
2997
+ }
2998
+ }, c.key);
2999
+ })
3000
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3001
+ className: "lc-bar-down animate-lc-bar-in motion-reduce:animate-none",
3002
+ style: {
3003
+ top: `${props.upPx}px`,
3004
+ ...enterStyle
3005
+ },
3006
+ children: CATS.map((c) => {
3007
+ const d = req[c.key] || 0;
3008
+ if (d >= 0) return null;
3009
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3010
+ "data-cat": c.key,
3011
+ className: "lc-cat-seg",
3012
+ style: {
3013
+ height: `${Math.max(1, Math.round(-d * props.deltaScale))}px`,
3014
+ background: c.color
3015
+ }
3016
+ }, c.key);
3017
+ })
3018
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3019
+ className: "lc-bar-stack animate-lc-bar-in motion-reduce:animate-none",
3020
+ style: enterStyle,
3021
+ children: CATS.map((c) => {
3022
+ const v = req[c.key] || 0;
3023
+ if (!v) return null;
3024
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3025
+ "data-cat": c.key,
3026
+ className: "lc-cat-seg",
3027
+ style: {
3028
+ height: `${Math.max(1, Math.round(v / props.maxTotal * CHART_H))}px`,
3029
+ background: c.color
3030
+ }
3031
+ }, c.key);
3032
+ })
3019
3033
  })
3020
- })]
3034
+ ]
3021
3035
  });
3022
3036
  });
3023
3037
  return function TrendChart(props) {
@@ -3371,6 +3385,7 @@ window.__ModuleLoader__.load({
3371
3385
  selected: props.selectedSeq === req.seq,
3372
3386
  hovered: props.hoveredSeq === req.seq,
3373
3387
  inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
3388
+ flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
3374
3389
  maxTotal,
3375
3390
  upPx: delta ? upPx : void 0,
3376
3391
  downPx: delta ? downPx : void 0,
@@ -5489,11 +5504,14 @@ window.__ModuleLoader__.load({
5489
5504
  * The provider-id seam between dsh request envelopes and the models.dev
5490
5505
  * registry: client/cost.ts resolves price-book branches through it, and
5491
5506
  * host/fold.ts uses the DeepSeek resolution to split the session-cost totals
5492
- * into peak/off-peak periods. Only the renames live here — an id absent
5493
- * from the table passes through verbatim.
5507
+ * into peak/off-peak periods. Only the renames live here — a dsh route key
5508
+ * that is not a registry id (user-declared gateways, `claude`/`codex`
5509
+ * subscriptions, org-prefixed catalogs) passes through and prices through
5510
+ * the model-side resolution index instead (client/cost.ts).
5494
5511
  */
5495
5512
  const MODELS_DEV_PROVIDER_IDS = {
5496
5513
  "deepseek-official": "deepseek",
5514
+ "deepseek-account": "deepseek",
5497
5515
  "kimi-coding": "moonshotai",
5498
5516
  "minimax-cn": "minimax",
5499
5517
  "zai-coding-cn": "zhipuai"
@@ -5516,6 +5534,120 @@ window.__ModuleLoader__.load({
5516
5534
  function toCurrency(usd, currency) {
5517
5535
  return currency === "cny" ? usd / USD_PER_CNY : usd;
5518
5536
  }
5537
+ /** Rate equality within the registry's float noise (sync drift prints 0.125000…003). */
5538
+ function sameRate(a, b) {
5539
+ const close = (x, y) => Math.abs(x - y) <= 1e-9 * Math.max(1, Math.abs(x));
5540
+ return close(a.hit, b.hit) && close(a.miss, b.miss) && close(a.write, b.write) && close(a.out, b.out);
5541
+ }
5542
+ /** One proven candidate rate off a built book branch, or null. */
5543
+ function rateOfEntry(value) {
5544
+ const v = value;
5545
+ if (v === null || typeof v !== "object") return null;
5546
+ const r = v;
5547
+ const num = (x) => typeof x === "number" && Number.isFinite(x);
5548
+ return num(r.hit) && num(r.miss) && num(r.write) && num(r.out) ? {
5549
+ hit: r.hit,
5550
+ miss: r.miss,
5551
+ write: r.write,
5552
+ out: r.out
5553
+ } : null;
5554
+ }
5555
+ /**
5556
+ * Build the resolution index over a proven book. `npmOf` carries each
5557
+ * provider's registry `npm` package (null when absent) — the vendor signal:
5558
+ * a provider whose package is `@ai-sdk/<its own id>` is the model's first
5559
+ * party, while mirrors and gateways ride `@ai-sdk/openai-compatible` or
5560
+ * someone else's package. Suffix tails are indexed alongside exact ids so a
5561
+ * short dsh spelling (`k3`) resolves book-wide too.
5562
+ */
5563
+ function priceIndexOf(prices, npmOf) {
5564
+ const byModel = /* @__PURE__ */ new Map();
5565
+ const push = (key, cand) => {
5566
+ const list = byModel.get(key);
5567
+ if (list === void 0) byModel.set(key, [cand]);
5568
+ else if (!list.some((c) => c.pid === cand.pid && sameRate(c.rate, cand.rate))) list.push(cand);
5569
+ };
5570
+ for (const pid of Object.keys(prices)) {
5571
+ const branch = branchOf(prices, pid);
5572
+ if (branch === null) continue;
5573
+ const npm = npmOf[pid];
5574
+ const primary = typeof npm === "string" && npm.startsWith("@ai-sdk/") && npm.slice(8) === pid;
5575
+ for (const mid of Object.keys(branch)) {
5576
+ const rate = rateOfEntry(branch[mid]);
5577
+ if (rate === null) continue;
5578
+ const lower = mid.toLowerCase();
5579
+ const cand = {
5580
+ pid,
5581
+ rate,
5582
+ primary
5583
+ };
5584
+ push(lower, cand);
5585
+ const parts = lower.split("-");
5586
+ for (let i = 1; i < parts.length; i++) push(parts.slice(i).join("-"), cand);
5587
+ }
5588
+ }
5589
+ return { byModel };
5590
+ }
5591
+ /**
5592
+ * The rate shared by a candidate group, or null: the largest equal-rate
5593
+ * group wins, a tie refuses. Agreeing mirrors are noise; disagreeing ones
5594
+ * price nothing rather than guess.
5595
+ */
5596
+ function majorityRate(cands) {
5597
+ const groups = [];
5598
+ for (const c of cands) {
5599
+ const g = groups.find((g) => sameRate(g.rate, c.rate));
5600
+ if (g === void 0) groups.push({
5601
+ rate: c.rate,
5602
+ n: 1
5603
+ });
5604
+ else g.n++;
5605
+ }
5606
+ groups.sort((a, b) => b.n - a.n);
5607
+ return groups.length > 1 && groups[0].n === groups[1].n ? null : groups[0].rate;
5608
+ }
5609
+ /**
5610
+ * Pick one candidate group's rate: the model's own vendor (unique or
5611
+ * unanimous), then a provider the model id itself names (`deepseek-v4-flash`
5612
+ * under `deepseek`), then a lone carrier. Everything else prices null.
5613
+ */
5614
+ function resolveCandidates(lower, cands, org) {
5615
+ if (org !== null) {
5616
+ const named = cands.filter((c) => c.pid === org || org.startsWith(c.pid + "-") || org.length >= 4 && org.startsWith(c.pid));
5617
+ if (named.length > 0) {
5618
+ const rate = majorityRate(named);
5619
+ if (rate !== null) return rate;
5620
+ }
5621
+ }
5622
+ const primaries = cands.filter((c) => c.primary);
5623
+ if (primaries.length > 0) {
5624
+ const rate = majorityRate(primaries);
5625
+ if (rate !== null) return rate;
5626
+ }
5627
+ const prefixed = cands.filter((c) => lower.startsWith(c.pid + "-") || lower.startsWith(c.pid + "/"));
5628
+ if (prefixed.length > 0) {
5629
+ const rate = majorityRate(prefixed);
5630
+ if (rate !== null) return rate;
5631
+ }
5632
+ return cands.length === 1 ? cands[0].rate : null;
5633
+ }
5634
+ /**
5635
+ * Resolve a model id book-wide: the full id first, then its last `/`-segment
5636
+ * (an org-prefixed dialect whose exact spelling no registry provider lists).
5637
+ * Null when every tier refuses.
5638
+ */
5639
+ function resolveRate(index, model) {
5640
+ const lower = model.toLowerCase();
5641
+ const slash = lower.lastIndexOf("/");
5642
+ const org = slash > 0 ? lower.slice(0, slash) : null;
5643
+ for (const key of slash > 0 ? [lower, lower.slice(slash + 1)] : [lower]) {
5644
+ const cands = index.byModel.get(key);
5645
+ if (cands === void 0) continue;
5646
+ const rate = resolveCandidates(key, cands, org);
5647
+ if (rate !== null) return rate;
5648
+ }
5649
+ return null;
5650
+ }
5519
5651
  /** One rate triple at the doubled peak rate (the tooltip's `peak | off` pair). */
5520
5652
  function peakOf(rate) {
5521
5653
  return {
@@ -5555,22 +5687,16 @@ window.__ModuleLoader__.load({
5555
5687
  * The book's rates for one folded (provider, model) bucket, or null when
5556
5688
  * the book cannot price it: the dsh provider id resolves through
5557
5689
  * modelsDevProviderOf (unmapped ids pass through) and prices by model id —
5558
- * exact, case-insensitive, or suffix; a provider the book does not carry
5559
- * falls back to a cross-provider scan, priced only when exactly one branch
5560
- * carries the model id.
5561
- */
5562
- function priceOf(prices, provider, model) {
5563
- if (prices === null || prices === void 0) return null;
5564
- const direct = branchOf(prices, modelsDevProviderOf(provider));
5690
+ * exact, case-insensitive, or suffix. A provider the book does not carry
5691
+ * falls back to the model-side resolution index, which names the vendor
5692
+ * from data (org segment, own-vendor SDK, id prefix, lone carrier) — never
5693
+ * from a per-model hardcode.
5694
+ */
5695
+ function priceOf(book, provider, model) {
5696
+ if (book === null || book === void 0) return null;
5697
+ const direct = branchOf(book.prices, modelsDevProviderOf(provider));
5565
5698
  if (direct !== null) return lookup(direct, model);
5566
- let found = null;
5567
- for (const models of Object.values(prices)) {
5568
- const rate = lookup(models, model);
5569
- if (rate === null) continue;
5570
- if (found !== null) return null;
5571
- found = rate;
5572
- }
5573
- return found;
5699
+ return resolveRate(book.index, model);
5574
5700
  }
5575
5701
  /**
5576
5702
  * Price the session's cumulative billed-token totals. Cache reads bill at
@@ -5581,8 +5707,8 @@ window.__ModuleLoader__.load({
5581
5707
  * Null when nothing was priced (no usage folded, no book yet, or no model
5582
5708
  * the book prices), so the cell can show a dash.
5583
5709
  */
5584
- function estimateSessionCost(usage, prices, currency) {
5585
- if (usage === null || usage === void 0 || prices === null || prices === void 0) return null;
5710
+ function estimateSessionCost(usage, book, currency) {
5711
+ if (usage === null || usage === void 0 || book === null || book === void 0) return null;
5586
5712
  let total = 0;
5587
5713
  let any = false;
5588
5714
  for (const provider of Object.keys(usage)) {
@@ -5590,7 +5716,7 @@ window.__ModuleLoader__.load({
5590
5716
  if (models === null) continue;
5591
5717
  const deepseek = isDeepSeekProvider(provider);
5592
5718
  for (const model of Object.keys(models)) {
5593
- const rate = priceOf(prices, provider, model);
5719
+ const rate = priceOf(book, provider, model);
5594
5720
  const periods = asRecord(models[model]);
5595
5721
  if (rate === null || periods === null) continue;
5596
5722
  for (const period of ["peak", "off"]) {
@@ -5662,7 +5788,7 @@ window.__ModuleLoader__.load({
5662
5788
  return formatCost(amount, currency).replace(/0+$/, "").replace(/\.$/, "");
5663
5789
  }
5664
5790
  //#endregion
5665
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/error.js
5791
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.87/node_modules/@opencode-ai/models/dist/error.js
5666
5792
  /**
5667
5793
  * The only error thrown by the models.dev client.
5668
5794
  *
@@ -5679,7 +5805,7 @@ window.__ModuleLoader__.load({
5679
5805
  }
5680
5806
  };
5681
5807
  //#endregion
5682
- //#region node_modules/.pnpm/@opencode-ai+models@0.0.72/node_modules/@opencode-ai/models/dist/client.js
5808
+ //#region node_modules/.pnpm/@opencode-ai+models@0.0.87/node_modules/@opencode-ai/models/dist/client.js
5683
5809
  /**
5684
5810
  * Creates a stateless models.dev client. Every method performs exactly one
5685
5811
  * `GET` and nothing is ever cached — callers who want caching should wrap
@@ -5696,7 +5822,11 @@ window.__ModuleLoader__.load({
5696
5822
  for (const [key, value] of new Headers(requestOptions?.headers)) headers.set(key, value);
5697
5823
  let response;
5698
5824
  try {
5699
- response = await fetch(new URL(path, base), {
5825
+ const url = new URL(path, base);
5826
+ const modelTypes = requestOptions?.modelTypes;
5827
+ if (modelTypes === "all") url.searchParams.set("type", "all");
5828
+ else if (modelTypes && modelTypes.length > 0) url.searchParams.set("type", modelTypes.join(","));
5829
+ response = await fetch(url, {
5700
5830
  method: "GET",
5701
5831
  headers,
5702
5832
  signal: requestOptions?.signal
@@ -5741,10 +5871,12 @@ window.__ModuleLoader__.load({
5741
5871
  * re-proves every field at the boundary — a non-conforming provider/model/
5742
5872
  * cost entry drops whole. The book keeps EVERY provider that prices (keyed
5743
5873
  * by the registry's own provider id), so a dsh provider id outside the
5744
- * rename table still prices by direct passthrough. One fetch per page load,
5745
- * kicked on first subscribe; a failure degrades to a visible state the cost
5746
- * cell notes, with a backed-off automatic retry — never a spinner, and
5747
- * never an unhandled rejection.
5874
+ * rename table still prices by direct passthrough, and carries each
5875
+ * provider's `npm` package into the model-side resolution index
5876
+ * (cost.ts `priceIndexOf`) so unknown provider ids price by model id alone.
5877
+ * One fetch per page load, kicked on first subscribe; a failure degrades to
5878
+ * a visible state the cost cell notes, with a backed-off automatic retry —
5879
+ * never a spinner, and never an unhandled rejection.
5748
5880
  */
5749
5881
  /** One finite non-negative registry figure, or null. */
5750
5882
  function rateOf(value) {
@@ -5752,18 +5884,23 @@ window.__ModuleLoader__.load({
5752
5884
  }
5753
5885
  /**
5754
5886
  * Extract the whole registry's per-model USD rates from a delivered
5755
- * providers payload (`/api.json`), keyed by the registry's provider ids.
5756
- * Any shape failure skips just that entry; a payload that is not a record
5757
- * at all returns null (the store treats it as a failed fetch and retries).
5887
+ * providers payload (`/api.json`), keyed by the registry's provider ids,
5888
+ * with the resolution index built over it. Any shape failure skips just
5889
+ * that entry; a payload that is not a record at all returns null (the store
5890
+ * treats it as a failed fetch and retries).
5758
5891
  */
5759
5892
  function pricesBookOf(value) {
5760
5893
  const data = asRecord(value);
5761
5894
  if (data === null || Array.isArray(data)) return null;
5762
5895
  const book = {};
5896
+ const npmOf = {};
5763
5897
  for (const providerId of Object.keys(data)) {
5764
5898
  const provider = asRecord(data[providerId]);
5765
- const modelList = provider !== null ? asRecord(provider.models) : null;
5899
+ if (provider === null) continue;
5900
+ const modelList = asRecord(provider.models);
5766
5901
  if (modelList === null) continue;
5902
+ const npm = provider.npm;
5903
+ npmOf[providerId] = typeof npm === "string" ? npm : null;
5767
5904
  const models = {};
5768
5905
  for (const id of Object.keys(modelList)) {
5769
5906
  const model = asRecord(modelList[id]);
@@ -5781,14 +5918,17 @@ window.__ModuleLoader__.load({
5781
5918
  }
5782
5919
  if (Object.keys(models).length > 0) book[providerId] = models;
5783
5920
  }
5784
- return book;
5921
+ return {
5922
+ prices: book,
5923
+ index: priceIndexOf(book, npmOf)
5924
+ };
5785
5925
  }
5786
5926
  const defaultLoader = () => make().providers();
5787
5927
  /** The retry backoff base; each consecutive failure doubles the wait, capped at 3 doublings. */
5788
5928
  const RETRY_BASE_MS = 3e4;
5789
5929
  let loader = defaultLoader;
5790
5930
  let snap = {
5791
- prices: null,
5931
+ book: null,
5792
5932
  failed: false
5793
5933
  };
5794
5934
  let inFlight = false;
@@ -5812,7 +5952,7 @@ window.__ModuleLoader__.load({
5812
5952
  const book = pricesBookOf(await loader());
5813
5953
  if (book !== null) {
5814
5954
  snap = {
5815
- prices: book,
5955
+ book,
5816
5956
  failed: false
5817
5957
  };
5818
5958
  failures = 0;
@@ -5824,7 +5964,7 @@ window.__ModuleLoader__.load({
5824
5964
  for (const fn of listeners) fn();
5825
5965
  }
5826
5966
  function kick() {
5827
- if (snap.prices !== null || inFlight || timer !== null) return;
5967
+ if (snap.book !== null || inFlight || timer !== null) return;
5828
5968
  fire();
5829
5969
  }
5830
5970
  /** The useSyncExternalStore seam: subscribing also kicks the first fetch. */
@@ -6307,7 +6447,7 @@ window.__ModuleLoader__.load({
6307
6447
  total
6308
6448
  };
6309
6449
  }
6310
- function kpisOf(rows, listed, prices, currency) {
6450
+ function kpisOf(rows, listed, book, currency) {
6311
6451
  const usage = mergeCostUsage(...rows.map((row) => row.timeline?.cost));
6312
6452
  const totals = usageTotalsOf(usage);
6313
6453
  let turns = 0;
@@ -6319,7 +6459,7 @@ window.__ModuleLoader__.load({
6319
6459
  let usageSessions = 0;
6320
6460
  for (const row of rows) {
6321
6461
  const cost = row.timeline?.cost;
6322
- if (estimateSessionCost(cost, prices, currency) !== null) costSessions++;
6462
+ if (estimateSessionCost(cost, book, currency) !== null) costSessions++;
6323
6463
  if (usageTotalsOf(cost) !== null) usageSessions++;
6324
6464
  turns += turnsOf(row.timeline);
6325
6465
  const timing = row.timeline?.timing;
@@ -6333,7 +6473,7 @@ window.__ModuleLoader__.load({
6333
6473
  listed,
6334
6474
  tokens: totals?.total ?? 0,
6335
6475
  turns,
6336
- cost: estimateSessionCost(usage, prices, currency),
6476
+ cost: estimateSessionCost(usage, book, currency),
6337
6477
  costSessions,
6338
6478
  cacheHit: totals === null ? null : cacheHitPercent(totals.cacheRead, totals.input + totals.cacheRead + totals.cacheWrite),
6339
6479
  usageSessions,
@@ -7524,7 +7664,7 @@ window.__ModuleLoader__.load({
7524
7664
  }
7525
7665
  function OverviewBody(props) {
7526
7666
  const open = (0, react.useSyncExternalStore)(overviewStore.subscribe, overviewStore.getSnapshot);
7527
- const { prices } = useModelPrices();
7667
+ const { book } = useModelPrices();
7528
7668
  const snapshot = sessionsSnapshotOf(props);
7529
7669
  const wsSnapshot = workspacesSnapshotOf(props);
7530
7670
  const [range, setRange] = (0, react.useState)("30d");
@@ -7576,7 +7716,7 @@ window.__ModuleLoader__.load({
7576
7716
  }] : counts;
7577
7717
  const visible = sortRows(group === null ? scoped : scoped.filter((row) => inGroup(row, group, groups)), sort);
7578
7718
  const paged = pageOf(visible, page);
7579
- const kpi = kpisOf(ranged, allRows.length, prices, currency);
7719
+ const kpi = kpisOf(ranged, allRows.length, book, currency);
7580
7720
  const days = aggregateDays(allRows);
7581
7721
  const openOne = (id) => {
7582
7722
  openSessionVia(ctx, id);
@@ -7681,7 +7821,10 @@ window.__ModuleLoader__.load({
7681
7821
  }),
7682
7822
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7683
7823
  className: "lc-stat-sub",
7684
- children: t("ov.kpi.sessionsSub", { n: kpi.costSessions })
7824
+ children: t("ov.kpi.pricedSub", {
7825
+ n: kpi.costSessions,
7826
+ total: kpi.sessions
7827
+ })
7685
7828
  })
7686
7829
  ]
7687
7830
  }),
@@ -7876,7 +8019,7 @@ window.__ModuleLoader__.load({
7876
8019
  children: paged.items.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OverviewCard, {
7877
8020
  row,
7878
8021
  ...groups?.[row.id] !== void 0 ? { group: groups[row.id] } : {},
7879
- costLabel: cardCostOf(row, prices, currency),
8022
+ costLabel: cardCostOf(row, book, currency),
7880
8023
  now,
7881
8024
  onOpen: openOne
7882
8025
  }, row.id))
@@ -7926,9 +8069,9 @@ window.__ModuleLoader__.load({
7926
8069
  };
7927
8070
  }
7928
8071
  /** One card's priced cost label, or the dash (no book yet, nothing billed, unpriceable model). */
7929
- function cardCostOf(row, prices, currency) {
8072
+ function cardCostOf(row, book, currency) {
7930
8073
  if (row.timeline?.cost === void 0) return "—";
7931
- const cost = estimateSessionCost(row.timeline.cost, prices, currency);
8074
+ const cost = estimateSessionCost(row.timeline.cost, book, currency);
7932
8075
  return cost === null ? "—" : formatCost(cost, currency);
7933
8076
  }
7934
8077
  //#endregion
@@ -10404,7 +10547,7 @@ window.__ModuleLoader__.load({
10404
10547
  return function PluginInfo() {
10405
10548
  const [latest, setLatest] = (0, react.useState)(null);
10406
10549
  (0, react.useEffect)(() => {
10407
- if ("0.57.0".includes("-dev")) return;
10550
+ if ("0.58.0".includes("-dev")) return;
10408
10551
  let on = true;
10409
10552
  fetchLatestVersion().then((v) => {
10410
10553
  if (on && v) setLatest(v);
@@ -10413,8 +10556,8 @@ window.__ModuleLoader__.load({
10413
10556
  on = false;
10414
10557
  };
10415
10558
  }, []);
10416
- const update = latest !== null && isNewerVersion(latest, "0.57.0") ? latest : null;
10417
- const nameText = "dsh-context (v0.57.0)";
10559
+ const update = latest !== null && isNewerVersion(latest, "0.58.0") ? latest : null;
10560
+ const nameText = "dsh-context (v0.58.0)";
10418
10561
  const nameValue = [nameText];
10419
10562
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10420
10563
  className: "lc-pi-update",
@@ -10887,8 +11030,8 @@ window.__ModuleLoader__.load({
10887
11030
  * with an off-peak bucket (DeepSeek's period-based list) shows the
10888
11031
  * peak | off-peak pair.
10889
11032
  */
10890
- function priceRowsOf(usage, prices) {
10891
- if (usage === void 0 || prices === null) return [];
11033
+ function priceRowsOf(usage, book) {
11034
+ if (usage === void 0 || book === null) return [];
10892
11035
  const rows = [];
10893
11036
  const multi = Object.keys(usage).length > 1;
10894
11037
  for (const provider of Object.keys(usage)) {
@@ -10898,7 +11041,7 @@ window.__ModuleLoader__.load({
10898
11041
  reaches here; the guard stays for the helper's own contract. */
10899
11042
  if (models === null) continue;
10900
11043
  for (const model of Object.keys(models)) {
10901
- const rate = priceOf(prices, provider, model);
11044
+ const rate = priceOf(book, provider, model);
10902
11045
  if (rate === null) continue;
10903
11046
  const periods = asRecord(models[model]);
10904
11047
  const deepseek = isDeepSeekProvider(provider);
@@ -10964,18 +11107,18 @@ window.__ModuleLoader__.load({
10964
11107
  const { t, fmt } = kit;
10965
11108
  return function StatsContext(props) {
10966
11109
  const currency = props.locale === "zh" ? "cny" : "usd";
10967
- const { prices, failed } = useModelPrices();
11110
+ const { book, failed } = useModelPrices();
10968
11111
  const subUsage = useSubagentCost(props.sessionId);
10969
11112
  const usage = mergeCostUsage(props.cost, subUsage) ?? void 0;
10970
- const cost = estimateSessionCost(usage, prices, currency);
10971
- const subCost = estimateSessionCost(subUsage, prices, currency);
11113
+ const cost = estimateSessionCost(usage, book, currency);
11114
+ const subCost = estimateSessionCost(subUsage, book, currency);
10972
11115
  const fmtRate = (usd) => formatPriceRate(toCurrency(usd, currency), currency);
10973
- const rows = priceRowsOf(usage, prices);
11116
+ const rows = priceRowsOf(usage, book);
10974
11117
  const deepseek = usage !== void 0 && Object.keys(usage).some((p) => isDeepSeekProvider(p));
10975
11118
  const subDeepseek = subUsage !== null && Object.keys(subUsage).some((p) => isDeepSeekProvider(p));
10976
11119
  const anyPair = rows.some((r) => r.offRate !== void 0);
10977
- const unpriced = rows.length === 0 && usage !== void 0 && Object.keys(usage).length > 0 && (failed || prices !== null);
10978
- const subUnpriced = subUsage !== null && priceRowsOf(subUsage, prices).length === 0 && (failed || prices !== null);
11120
+ const unpriced = rows.length === 0 && usage !== void 0 && Object.keys(usage).length > 0 && (failed || book !== null);
11121
+ const subUnpriced = subUsage !== null && priceRowsOf(subUsage, book).length === 0 && (failed || book !== null);
10979
11122
  const costTip = [
10980
11123
  t("stats.costTip") + (deepseek ? " " + t("stats.costTipDeepseek") : ""),
10981
11124
  rows.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
@@ -12021,7 +12164,7 @@ window.__ModuleLoader__.load({
12021
12164
  }
12022
12165
  //#endregion
12023
12166
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
12024
- const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity 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), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
12167
+ const css$9 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding:18px 2px 0;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity 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), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 0}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=skill] .lc-cat-seg[data-cat=skill],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
12025
12168
  const tagId$9 = "dsh-context/trendChart.css";
12026
12169
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
12027
12170
  const tag = document.createElement("style");
package/lib/index.js CHANGED
@@ -11,11 +11,14 @@ import { randomUUID } from "node:crypto";
11
11
  * The provider-id seam between dsh request envelopes and the models.dev
12
12
  * registry: client/cost.ts resolves price-book branches through it, and
13
13
  * host/fold.ts uses the DeepSeek resolution to split the session-cost totals
14
- * into peak/off-peak periods. Only the renames live here — an id absent
15
- * from the table passes through verbatim.
14
+ * into peak/off-peak periods. Only the renames live here — a dsh route key
15
+ * that is not a registry id (user-declared gateways, `claude`/`codex`
16
+ * subscriptions, org-prefixed catalogs) passes through and prices through
17
+ * the model-side resolution index instead (client/cost.ts).
16
18
  */
17
19
  const MODELS_DEV_PROVIDER_IDS = {
18
20
  "deepseek-official": "deepseek",
21
+ "deepseek-account": "deepseek",
19
22
  "kimi-coding": "moonshotai",
20
23
  "minimax-cn": "minimax",
21
24
  "zai-coding-cn": "zhipuai"
@@ -3176,7 +3179,7 @@ function createContextTimelineDefinition(config, slim) {
3176
3179
  },
3177
3180
  init: () => createTimelineState(),
3178
3181
  apply: (state, event) => applyTimeline(state, event, bounds),
3179
- stateVersion: 20
3182
+ stateVersion: 21
3180
3183
  };
3181
3184
  }
3182
3185
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.57.0",
3
+ "version": "0.58.0",
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
  "icon": "icon.svg",
6
6
  "author": "bowenliang123",
@@ -112,7 +112,7 @@
112
112
  "@deepseek-ai/dsh-settings": "0.1.5-rc.1",
113
113
  "@deepseek-ai/dsh-util-workspace-path": "0.1.5-rc.1",
114
114
  "@deepseek-ai/schemastery": "^3.18.4",
115
- "@opencode-ai/models": "^0.0.72",
115
+ "@opencode-ai/models": "^0.0.87",
116
116
  "@shikijs/langs": "^4.3.1",
117
117
  "@stylistic/eslint-plugin": "^5.10.0",
118
118
  "@tailwindcss/node": "4.3.3",