dsh-context 0.41.2 → 0.41.3

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
@@ -5122,7 +5122,7 @@ window.__ModuleLoader__.load({
5122
5122
  return function PluginInfo() {
5123
5123
  const [latest, setLatest] = React.useState(null);
5124
5124
  React.useEffect(() => {
5125
- if ("0.41.2".includes("-dev")) return;
5125
+ if ("0.41.3".includes("-dev")) return;
5126
5126
  let on = true;
5127
5127
  fetchLatestVersion().then((v) => {
5128
5128
  if (on && v) setLatest(v);
@@ -5131,8 +5131,8 @@ window.__ModuleLoader__.load({
5131
5131
  on = false;
5132
5132
  };
5133
5133
  }, []);
5134
- const update = latest !== null && isNewerVersion(latest, "0.41.2") ? latest : null;
5135
- const nameText = "dsh-context (v0.41.2)";
5134
+ const update = latest !== null && isNewerVersion(latest, "0.41.3") ? latest : null;
5135
+ const nameText = "dsh-context (v0.41.3)";
5136
5136
  const nameValue = [nameText];
5137
5137
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
5138
5138
  key: "update",
package/lib/index.js CHANGED
@@ -921,6 +921,25 @@ function applySurface(st, ev, type, data, message) {
921
921
  return node;
922
922
  }
923
923
  /**
924
+ * One provider-reported usage bucket as a billed count, or null when the
925
+ * field carries no readable number. Accepts finite numbers and numeric
926
+ * strings; fractions round (some gateways report fractional counts) and
927
+ * negatives clamp to 0 — a mis-accounting gateway that reports
928
+ * `cached_tokens > prompt_tokens` drives the disjoint uncached-input figure
929
+ * below zero, and one raw figure in the state would fail the wire and state
930
+ * schemas' `.int().nonnegative()` gates on EVERY later delivery, permanently
931
+ * freezing the projection feed for the session (issue #44). NaN, infinities,
932
+ * and non-numeric values read as absent.
933
+ */
934
+ function tokenCountOf(value) {
935
+ if (typeof value === "number") return Number.isFinite(value) ? Math.max(0, Math.round(value)) : null;
936
+ if (typeof value === "string" && value.trim() !== "") {
937
+ const parsed = Number(value);
938
+ return Number.isFinite(parsed) ? Math.max(0, Math.round(parsed)) : null;
939
+ }
940
+ return null;
941
+ }
942
+ /**
924
943
  * The DeepSeek V4 model family a model name prices as — matched on the NAME
925
944
  * alone (provider-agnostic: official API, proxies, OpenRouter spellings like
926
945
  * `deepseek/deepseek-v4-flash` all land here). Null for any other model:
@@ -949,7 +968,9 @@ function isPeakUtc(time) {
949
968
  /**
950
969
  * Fold one billed request into the session-cost totals, cloning along the
951
970
  * mutated path only (the untouched branch stays shared with the persisted
952
- * previous state — the apply contract never mutates it in place).
971
+ * previous state — the apply contract never mutates it in place). The
972
+ * buckets arrive sanitized ({@link BilledUsage}), so the totals stay at the
973
+ * schemas' non-negative safe integers no matter what the provider reported.
953
974
  */
954
975
  function accumulateCost(st, time, usage) {
955
976
  const family = costFamilyOf(st.model);
@@ -965,13 +986,10 @@ function accumulateCost(st, time, usage) {
965
986
  };
966
987
  const nextFam = { ...fam };
967
988
  nextFam[period] = {
968
- /* v8 ignore next 1 -- accumulateCost only runs under the
969
- `typeof usage.inputTokens === 'number'` guard; the fallback is
970
- defensive against a hand-built UsageLike. */
971
- uncached: b.uncached + (usage.inputTokens ?? 0),
972
- cacheRead: b.cacheRead + (usage.cacheReadTokens ?? 0),
973
- cacheWrite: b.cacheWrite + (usage.cacheWriteTokens ?? 0),
974
- output: b.output + (usage.outputTokens ?? 0)
989
+ uncached: b.uncached + usage.input,
990
+ cacheRead: b.cacheRead + usage.cacheRead,
991
+ cacheWrite: b.cacheWrite + usage.cacheWrite,
992
+ output: b.output + usage.output
975
993
  };
976
994
  const next = { ...prev };
977
995
  next[family] = nextFam;
@@ -1194,11 +1212,22 @@ function applyTimeline(state, event, bounds) {
1194
1212
  };
1195
1213
  if (data && typeof data.turn === "number") record.turn = data.turn;
1196
1214
  if (data && typeof data.step === "number") record.step = data.step;
1197
- if (usage && typeof usage.inputTokens === "number") {
1198
- record.prompt = usage.inputTokens + (usage.cacheReadTokens || 0) + (usage.cacheWriteTokens || 0);
1199
- if (typeof usage.cacheReadTokens === "number") record.cacheRead = usage.cacheReadTokens;
1200
- if (typeof usage.outputTokens === "number") record.output = usage.outputTokens;
1201
- accumulateCost(s, event.time, usage);
1215
+ if (usage !== null && typeof usage === "object") {
1216
+ const input = tokenCountOf(usage.inputTokens);
1217
+ const cacheRead = tokenCountOf(usage.cacheReadTokens);
1218
+ const cacheWrite = tokenCountOf(usage.cacheWriteTokens);
1219
+ const output = tokenCountOf(usage.outputTokens);
1220
+ if (input !== null || cacheRead !== null || cacheWrite !== null || output !== null) {
1221
+ record.prompt = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
1222
+ if (cacheRead !== null) record.cacheRead = cacheRead;
1223
+ if (output !== null) record.output = output;
1224
+ accumulateCost(s, event.time, {
1225
+ input: input ?? 0,
1226
+ cacheRead: cacheRead ?? 0,
1227
+ cacheWrite: cacheWrite ?? 0,
1228
+ output: output ?? 0
1229
+ });
1230
+ }
1202
1231
  }
1203
1232
  s.requests.push(record);
1204
1233
  const timing = ensureTiming(s);
@@ -1533,7 +1562,7 @@ function createContextTimelineDefinition(config) {
1533
1562
  },
1534
1563
  init: () => createTimelineState(),
1535
1564
  apply: (state, event) => applyTimeline(state, event, bounds),
1536
- stateVersion: 12
1565
+ stateVersion: 13
1537
1566
  };
1538
1567
  }
1539
1568
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.41.2",
3
+ "version": "0.41.3",
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": {