dsh-context 0.52.0 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,13 +59,22 @@ Open any session and click the **Context / 上下文** tab:
59
59
 
60
60
  The headline occupancy and composition read the **same official token-meter projections as the chat composer's context ring** (`contextPressure` / `contextBreakdown`), so the figures always match what the ring tells you.
61
61
 
62
- ### 🧱 Current Context — who ate the budget
62
+ ### Context Stats
63
+
64
+ #### Token Stats and Timing Stats
65
+ ![Token_Stats_and_Timing_Stats](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/token-stats-and-timing-stats.png)
66
+
67
+ #### Context Stats
68
+ ![Context_Stats](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/context-stats.png)
69
+
70
+
71
+ ### 🧱 Current Context — what's in the context window now
63
72
 
64
73
  ![Current Context card](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/current-context.png)
65
74
 
66
75
  A six-color stacked bar against the model's full window (hatching = free headroom): system prompt, tool schemas, user messages, injected context, assistant replies, tool results — each with its ≈token figure and share. When a conversation starts degrading, this is where you see *which part* is responsible.
67
76
 
68
- ### 📈 Context Trend — every request, sized and explained
77
+ ### 📈 Context Trend — how the context grew and evolved by turn or steps
69
78
 
70
79
  ![Context Trend with the step brief](https://raw.githubusercontent.com/bowenliang123/dsh-context/main/docs/context-trend.png)
71
80
 
package/lib/client.js CHANGED
@@ -211,7 +211,7 @@ window.__ModuleLoader__.load({
211
211
  "gate.minimum": "最低要求",
212
212
  "gate.orNewer": "及更新的版本",
213
213
  "gate.ok": "知道了",
214
- "footer": "估算口径:与 dsh 内置 tokenMeter 相同的固定密度启发式(约 4 字符 ≈ 1 token);「实际」为供应商上报用量。",
214
+ "footer": "Token 数量统计:「估算」与 DeepSeek Harness 内置 tokenMeter 相同的固定密度启发式(约 4 字符 ≈ 1 token);「实际」为供应商上报用量。",
215
215
  "tip.step": "第 {t} 轮 · 第 {s} 步 (共 {n} 步)",
216
216
  "tip.turn": "第 {t} 轮 · 共 {n} 步",
217
217
  "tip.turn1": "第 {t} 轮 · 共 1 步",
@@ -237,6 +237,7 @@ window.__ModuleLoader__.load({
237
237
  "node.nonText": "(非文本消息)",
238
238
  "node.snapshot": "快照: ",
239
239
  "node.skillTag": "技能 · {name}",
240
+ "cmd.label": "上下文",
240
241
  "cmd.section": "上下文",
241
242
  "cmd.desc": "查看当前上下文构成,浏览各步骤组成",
242
243
  "cmd.close": "关闭",
@@ -514,7 +515,7 @@ window.__ModuleLoader__.load({
514
515
  "gate.minimum": "Minimum required",
515
516
  "gate.orNewer": "or newer",
516
517
  "gate.ok": "Got it",
517
- "footer": "Estimate: same fixed-density heuristic as dsh’s built-in tokenMeter (~4 chars ≈ 1 token); “actual” is provider-reported usage.",
518
+ "footer": "Token amount: “Estimated” uses the same fixed-density heuristic as DeepSeek Harness’s built-in tokenMeter (~4 chars ≈ 1 token); “Actual” is provider-reported usage.",
518
519
  "tip.step": "Turn {t} · Step {s} of {n}",
519
520
  "tip.turn": "Turn {t} · {n} steps",
520
521
  "tip.turn1": "Turn {t} · 1 step",
@@ -540,6 +541,7 @@ window.__ModuleLoader__.load({
540
541
  "node.nonText": "(non-text message)",
541
542
  "node.snapshot": "Snapshot: ",
542
543
  "node.skillTag": "Skill · {name}",
544
+ "cmd.label": "Context",
543
545
  "cmd.section": "Context",
544
546
  "cmd.desc": "View current context makeup, browse per-step composition",
545
547
  "cmd.close": "Close",
@@ -615,6 +617,58 @@ window.__ModuleLoader__.load({
615
617
  "attach.tokensTip": "Estimated token cost via DeepSeek's official image-size conversion (384-token cap per image)"
616
618
  };
617
619
  //#endregion
620
+ //#region src/client/icon.tsx
621
+ /** The sheet's strokes, in paint order, as `[path, fill]` pairs. */
622
+ const SHEET_PATHS = [
623
+ ["M899.984 19.873h-3.452c-26.123 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.173 47.298 47.296 47.298h3.452c26.119 0 47.297-21.171 47.297-47.298V67.169c0-26.124-21.177-47.296-47.297-47.296z", "#4A5699"],
624
+ ["M132.643 19.873h-3.449c-26.12 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.177 47.298 47.296 47.298h3.449c26.123 0 47.299-21.171 47.299-47.298V67.169c0-26.124-21.176-47.296-47.299-47.296z", "#C45FA0"],
625
+ ["M899.463 19.873H129.194c-26.12 0-47.296 21.172-47.296 47.296v3.377c0 26.12 21.177 47.299 47.296 47.299h770.269c26.123 0 47.296-21.179 47.296-47.299v-3.377c0-26.124-21.173-47.296-47.296-47.296z", "#6277BA"],
626
+ ["M899.463 905.006H129.194c-26.12 0-47.296 21.17-47.296 47.29v3.381c0 26.127 21.177 47.298 47.296 47.298h770.269c26.123 0 47.296-21.171 47.296-47.298v-3.381c0-26.12-21.173-47.29-47.296-47.29z", "#C45FA0"],
627
+ ["M717.962 543.153H542.047c-26.121 0-47.298 21.175-47.298 47.297v3.724c0 26.123 21.177 47.293 47.298 47.293h175.915c26.121 0 47.297-21.17 47.297-47.293v-3.724c0-26.122-21.176-47.297-47.297-47.297z", "#E5594F"],
628
+ ["M689.268 198.849H513.355c-26.122 0-47.298 21.175-47.298 47.297v3.722c0 26.12 21.176 47.297 47.298 47.297h175.912c26.122 0 47.298-21.177 47.298-47.297v-3.722c0-26.122-21.175-47.297-47.297-47.297z", "#F0D043"],
629
+ ["M757.789 353.081H261.17c-26.121 0-47.297 21.172-47.297 47.296v3.377c0 26.121 21.177 47.299 47.297 47.299h496.619c26.121 0 47.296-21.178 47.296-47.299v-3.377c0-26.125-21.175-47.296-47.296-47.296z", "#E5594F"],
630
+ ["M762.638 726.225h-496.62c-26.12 0-47.294 21.18-47.294 47.301v3.377c0 26.12 21.174 47.3 47.294 47.3h496.62c26.122 0 47.296-21.18 47.296-47.3v-3.377c0-26.122-21.174-47.301-47.296-47.301z", "#6277BA"],
631
+ ["M355.734 543.328H281.41c-26.122 0-47.297 21.17-47.297 47.293v3.378c0 26.118 21.175 47.297 47.297 47.297h74.324c26.123 0 47.296-21.179 47.296-47.297v-3.378c0-26.123-21.174-47.293-47.296-47.293z", "#F39A2B"],
632
+ ["M334.85 248.006m-48.986 0a48.986 48.986 0 1 0 97.972 0 48.986 48.986 0 1 0-97.972 0Z", "#F39A2B"]
633
+ ];
634
+ /** The colourful document sheet at the requested square edge. */
635
+ function ContextIcon({ size = 20, className }) {
636
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
637
+ width: size,
638
+ height: size,
639
+ viewBox: "0 0 1024 1024",
640
+ className,
641
+ "aria-hidden": "true",
642
+ xmlns: "http://www.w3.org/2000/svg",
643
+ children: SHEET_PATHS.map(([d, fill]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
644
+ d,
645
+ fill
646
+ }, d))
647
+ });
648
+ }
649
+ /**
650
+ * The tab chip's title seat (`sidebar.right.pane.tab.title`): the emblem before
651
+ * the label, so the chip reads as the files chip does. The label comes from the
652
+ * plugin's own bound translate — read at render, so the chip follows the active
653
+ * locale — rather than the tab-information hook, which a foreign or
654
+ * not-yet-committed tab record can throw on. It carries a trailing gutter
655
+ * (`.lc-title-label`) so the active chip's fade lands past the text, never on
656
+ * the last glyphs.
657
+ * @param t - the plugin-namespace translate bound in `apply`.
658
+ * @returns the title component to register under the tab type's id.
659
+ */
660
+ function makeContextTabTitle(t) {
661
+ return function ContextTabTitle() {
662
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
663
+ size: 16,
664
+ className: "lc-title-icon"
665
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
666
+ className: "lc-title-label",
667
+ children: t("tab")
668
+ })] });
669
+ };
670
+ }
671
+ //#endregion
618
672
  //#region src/client/modalStore.ts
619
673
  const stores$1 = /* @__PURE__ */ new Map();
620
674
  function modalStoreOf(sessionId) {
@@ -677,6 +731,8 @@ window.__ModuleLoader__.load({
677
731
  if (query !== "" && !COMMAND.startsWith(query)) return Promise.resolve([]);
678
732
  return Promise.resolve([{
679
733
  name: COMMAND,
734
+ label: kit.t("cmd.label"),
735
+ icon: ContextIcon,
680
736
  section: kit.t("cmd.section"),
681
737
  description: kit.t("cmd.desc")
682
738
  }]);
@@ -4114,6 +4170,14 @@ window.__ModuleLoader__.load({
4114
4170
  for (const n of asm.nodes) (m[n.cat] ??= []).push(n);
4115
4171
  return m;
4116
4172
  }
4173
+ /**
4174
+ * The producer identity a fold-stamped injection node carries
4175
+ * (shared/types.ts SurfaceNode.name): '' when absent or drift-typed — the
4176
+ * delivered payload is untrusted, so rows re-prove it before rendering.
4177
+ */
4178
+ function nodeNameOf(n) {
4179
+ return typeof n.name === "string" ? n.name : "";
4180
+ }
4117
4181
  function countOf(asm, byCat, c) {
4118
4182
  if (c === "system") return asm.system !== null ? 1 : 0;
4119
4183
  if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
@@ -4422,12 +4486,13 @@ window.__ModuleLoader__.load({
4422
4486
  const rowErr = (n.cat === "tool" || n.cat === "skill") && toolErrOf(n, conv).err;
4423
4487
  let tag = null;
4424
4488
  let preview = nodeText(n);
4489
+ const id = nodeNameOf(n);
4425
4490
  if (n.cat === "tool") {
4426
4491
  tag = n.tool ?? "?";
4427
4492
  preview = callSummaryOf(conv) ?? t("node.toolResult");
4428
4493
  } else if (n.cat === "skill") {
4429
4494
  tag = n.skill !== void 0 ? t("node.skillTag", { name: n.skill }) : t("form." + (n.form || "context"));
4430
- preview = (n.text !== void 0 && n.text !== "" ? n.text : null) ?? callSummaryOf(conv) ?? preview;
4495
+ preview = (n.skill === void 0 && id !== "" ? id : null) ?? (n.text !== void 0 && n.text !== "" ? n.text : null) ?? callSummaryOf(conv) ?? preview;
4431
4496
  } else if (n.cat === "assistant" && Array.isArray(n.calls) && n.calls.length > 0) {
4432
4497
  tag = n.calls.join(" › ");
4433
4498
  preview = (n.text !== void 0 && n.text !== "" ? n.text : null) ?? blockSummaryOf(conv) ?? t("node.empty");
@@ -4437,7 +4502,8 @@ window.__ModuleLoader__.load({
4437
4502
  if (imgCount > 0 && openElem !== `n${n.seq}`) tag = t("attach.image") + (imgCount > 1 ? " ×" + String(imgCount) : "");
4438
4503
  } else if (n.cat === "inject") {
4439
4504
  tag = t("form." + (n.form || "context"));
4440
- if (n.text !== void 0 && n.text !== "") preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
4505
+ if (id !== "") preview = id;
4506
+ else if (n.text !== void 0 && n.text !== "") preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
4441
4507
  }
4442
4508
  return {
4443
4509
  n,
@@ -4448,7 +4514,7 @@ window.__ModuleLoader__.load({
4448
4514
  };
4449
4515
  });
4450
4516
  const q = rowQuery.trim().toLowerCase();
4451
- const shown = q === "" ? rows : rows.filter((r) => (r.tag ?? "").toLowerCase().includes(q) || r.preview.toLowerCase().includes(q));
4517
+ const shown = q === "" ? rows : rows.filter((r) => (r.tag ?? "").toLowerCase().includes(q) || r.preview.toLowerCase().includes(q) || typeof r.n.text === "string" && r.n.text.toLowerCase().includes(q));
4452
4518
  const rowctl = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowToolbar, {
4453
4519
  value: rowQuery,
4454
4520
  placeholder: t("browser.search." + c),
@@ -4591,7 +4657,7 @@ window.__ModuleLoader__.load({
4591
4657
  className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty"),
4592
4658
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
4593
4659
  type: "button",
4594
- className: "lc-br-cat-row hover:bg-(--dsw-alias-interactive-bg-hover)" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
4660
+ className: "lc-br-cat-row hover:bg-(--dsw-alias-interactive-bg-hover)" + (open ? " lc-br-cat-open" : "") + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
4595
4661
  /* v8 ignore start -- the handlers exist only when linked,
4596
4662
  and linked already requires onHoverKey defined (above). */
4597
4663
  onMouseEnter: linked ? () => {
@@ -7533,7 +7599,7 @@ window.__ModuleLoader__.load({
7533
7599
  return function PluginInfo() {
7534
7600
  const [latest, setLatest] = (0, react.useState)(null);
7535
7601
  (0, react.useEffect)(() => {
7536
- if ("0.52.0".includes("-dev")) return;
7602
+ if ("0.52.1".includes("-dev")) return;
7537
7603
  let on = true;
7538
7604
  fetchLatestVersion().then((v) => {
7539
7605
  if (on && v) setLatest(v);
@@ -7542,8 +7608,8 @@ window.__ModuleLoader__.load({
7542
7608
  on = false;
7543
7609
  };
7544
7610
  }, []);
7545
- const update = latest !== null && isNewerVersion(latest, "0.52.0") ? latest : null;
7546
- const nameText = "dsh-context (v0.52.0)";
7611
+ const update = latest !== null && isNewerVersion(latest, "0.52.1") ? latest : null;
7612
+ const nameText = "dsh-context (v0.52.1)";
7547
7613
  const nameValue = [nameText];
7548
7614
  if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
7549
7615
  className: "lc-pi-update",
@@ -9382,58 +9448,6 @@ window.__ModuleLoader__.load({
9382
9448
  };
9383
9449
  }
9384
9450
  //#endregion
9385
- //#region src/client/icon.tsx
9386
- /** The sheet's strokes, in paint order, as `[path, fill]` pairs. */
9387
- const SHEET_PATHS = [
9388
- ["M899.984 19.873h-3.452c-26.123 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.173 47.298 47.296 47.298h3.452c26.119 0 47.297-21.171 47.297-47.298V67.169c0-26.124-21.177-47.296-47.297-47.296z", "#4A5699"],
9389
- ["M132.643 19.873h-3.449c-26.12 0-47.296 21.172-47.296 47.296v888.508c0 26.127 21.177 47.298 47.296 47.298h3.449c26.123 0 47.299-21.171 47.299-47.298V67.169c0-26.124-21.176-47.296-47.299-47.296z", "#C45FA0"],
9390
- ["M899.463 19.873H129.194c-26.12 0-47.296 21.172-47.296 47.296v3.377c0 26.12 21.177 47.299 47.296 47.299h770.269c26.123 0 47.296-21.179 47.296-47.299v-3.377c0-26.124-21.173-47.296-47.296-47.296z", "#6277BA"],
9391
- ["M899.463 905.006H129.194c-26.12 0-47.296 21.17-47.296 47.29v3.381c0 26.127 21.177 47.298 47.296 47.298h770.269c26.123 0 47.296-21.171 47.296-47.298v-3.381c0-26.12-21.173-47.29-47.296-47.29z", "#C45FA0"],
9392
- ["M717.962 543.153H542.047c-26.121 0-47.298 21.175-47.298 47.297v3.724c0 26.123 21.177 47.293 47.298 47.293h175.915c26.121 0 47.297-21.17 47.297-47.293v-3.724c0-26.122-21.176-47.297-47.297-47.297z", "#E5594F"],
9393
- ["M689.268 198.849H513.355c-26.122 0-47.298 21.175-47.298 47.297v3.722c0 26.12 21.176 47.297 47.298 47.297h175.912c26.122 0 47.298-21.177 47.298-47.297v-3.722c0-26.122-21.175-47.297-47.297-47.297z", "#F0D043"],
9394
- ["M757.789 353.081H261.17c-26.121 0-47.297 21.172-47.297 47.296v3.377c0 26.121 21.177 47.299 47.297 47.299h496.619c26.121 0 47.296-21.178 47.296-47.299v-3.377c0-26.125-21.175-47.296-47.296-47.296z", "#E5594F"],
9395
- ["M762.638 726.225h-496.62c-26.12 0-47.294 21.18-47.294 47.301v3.377c0 26.12 21.174 47.3 47.294 47.3h496.62c26.122 0 47.296-21.18 47.296-47.3v-3.377c0-26.122-21.174-47.301-47.296-47.301z", "#6277BA"],
9396
- ["M355.734 543.328H281.41c-26.122 0-47.297 21.17-47.297 47.293v3.378c0 26.118 21.175 47.297 47.297 47.297h74.324c26.123 0 47.296-21.179 47.296-47.297v-3.378c0-26.123-21.174-47.293-47.296-47.293z", "#F39A2B"],
9397
- ["M334.85 248.006m-48.986 0a48.986 48.986 0 1 0 97.972 0 48.986 48.986 0 1 0-97.972 0Z", "#F39A2B"]
9398
- ];
9399
- /** The colourful document sheet at the requested square edge. */
9400
- function ContextIcon({ size = 20, className }) {
9401
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
9402
- width: size,
9403
- height: size,
9404
- viewBox: "0 0 1024 1024",
9405
- className,
9406
- "aria-hidden": "true",
9407
- xmlns: "http://www.w3.org/2000/svg",
9408
- children: SHEET_PATHS.map(([d, fill]) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
9409
- d,
9410
- fill
9411
- }, d))
9412
- });
9413
- }
9414
- /**
9415
- * The tab chip's title seat (`sidebar.right.pane.tab.title`): the emblem before
9416
- * the label, so the chip reads as the files chip does. The label comes from the
9417
- * plugin's own bound translate — read at render, so the chip follows the active
9418
- * locale — rather than the tab-information hook, which a foreign or
9419
- * not-yet-committed tab record can throw on. It carries a trailing gutter
9420
- * (`.lc-title-label`) so the active chip's fade lands past the text, never on
9421
- * the last glyphs.
9422
- * @param t - the plugin-namespace translate bound in `apply`.
9423
- * @returns the title component to register under the tab type's id.
9424
- */
9425
- function makeContextTabTitle(t) {
9426
- return function ContextTabTitle() {
9427
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ContextIcon, {
9428
- size: 16,
9429
- className: "lc-title-icon"
9430
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9431
- className: "lc-title-label",
9432
- children: t("tab")
9433
- })] });
9434
- };
9435
- }
9436
- //#endregion
9437
9451
  //#region src/client/sidebar.ts
9438
9452
  /**
9439
9453
  * The right Sidebar's Context tab (dsh 0.1.5-rc.1+).
@@ -9665,7 +9679,7 @@ window.__ModuleLoader__.load({
9665
9679
  }
9666
9680
  //#endregion
9667
9681
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/browser.css.mjs
9668
- const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{background-image:linear-gradient(to right, transparent calc(100% - min(.5px, 12.5%)), var(--dsw-alias-bg-layer-1) 0)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-est{margin-left:auto}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;display:flex}.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{width:8px;height:8px;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;flex:none;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:flex}.lc-br-chev:before{content:\"\";width:5px;height:5px;transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-bottom:1px solid;border-right:1px solid;transform:rotate(-45deg)}.lc-br-chev-on:before{transform:rotate(45deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;display:flex}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-tool-plugin{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);border-color:#0000;border-radius:6px;font-size:12px}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time,.lc-br-hits{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}";
9682
+ const css$3 = ".lc-br-dna-ctl{flex:none;margin-left:0}.lc-br-bar-dna .lc-stacked-seg{background-image:linear-gradient(to right, transparent calc(100% - min(.5px, 12.5%)), var(--dsw-alias-bg-layer-1) 0)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-est{margin-left:auto}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;display:flex}.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-open{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-row i{width:8px;height:8px;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;flex:none;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none;justify-content:center;align-items:center;display:flex}.lc-br-chev:before{content:\"\";width:5px;height:5px;transition:transform var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-bottom:1px solid;border-right:1px solid;transform:rotate(-45deg)}.lc-br-chev-on:before{transform:rotate(45deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;display:flex}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-tool-plugin{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);border-color:#0000;border-radius:6px;font-size:12px}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time,.lc-br-hits{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}";
9669
9683
  const tagId$3 = "dsh-context/browser.css";
9670
9684
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
9671
9685
  const tag = document.createElement("style");
package/lib/index.d.ts CHANGED
@@ -620,6 +620,14 @@ interface SurfaceNode {
620
620
  */
621
621
  gone?: number;
622
622
  form?: string;
623
+ /**
624
+ * The producer identity the matching inject event names (host pricing.ts
625
+ * `injectionSourceName`: the plugin id, the reconciled instruction files,
626
+ * or the durable kind). Stamped on injection nodes alongside the event, so
627
+ * the browser rows label them the way the events card does; absent when the
628
+ * source carries no readable identity or the node predates the stamp.
629
+ */
630
+ name?: string;
623
631
  text?: string;
624
632
  tool?: string;
625
633
  err?: boolean;
package/lib/index.js CHANGED
@@ -594,10 +594,10 @@ function toolCallNames(blocks) {
594
594
  * context provenance (client-runtime context-provenance.ts): workspace
595
595
  * instructions name the files they were reconciled from, a plugin source its
596
596
  * plugin id, and any other producer its own durable kind. Returns '' when
597
- * the source carries no readable identity at all.
597
+ * the source is missing or carries no readable identity at all.
598
598
  */
599
599
  function injectionSourceName(source) {
600
- if (source.kind === "agent-instructions" && Array.isArray(source.changes)) {
600
+ if (source?.kind === "agent-instructions" && Array.isArray(source.changes)) {
601
601
  const paths = [];
602
602
  for (const change of source.changes) {
603
603
  const path = change?.path;
@@ -605,8 +605,10 @@ function injectionSourceName(source) {
605
605
  }
606
606
  if (paths.length > 0) return paths.join(", ");
607
607
  }
608
- if (typeof source.plugin === "string" && source.plugin !== "") return source.plugin;
609
- return typeof source.kind === "string" && source.kind !== "" ? source.kind : "";
608
+ const plugin = source?.plugin;
609
+ if (typeof plugin === "string" && plugin !== "") return plugin;
610
+ const kind = source?.kind;
611
+ return typeof kind === "string" && kind !== "" ? kind : "";
610
612
  }
611
613
  function isInjection(source) {
612
614
  return source !== null && source !== void 0 && (typeof source.kind === "string" && source.kind !== "" && source.kind !== "user" || typeof source.form === "string");
@@ -1627,7 +1629,10 @@ function applyTimeline(state, event, bounds) {
1627
1629
  rec.name = typeof source.name === "string" ? source.name : "?";
1628
1630
  } else {
1629
1631
  const label = injectionSourceName(source);
1630
- if (label !== "") rec.name = label;
1632
+ if (label !== "") {
1633
+ rec.name = label;
1634
+ node.name = label;
1635
+ }
1631
1636
  if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
1632
1637
  }
1633
1638
  s.events.push(rec);
@@ -2276,6 +2281,7 @@ const surfaceNodeSchema = z.object({
2276
2281
  imgs: z.number().int().nonnegative().optional(),
2277
2282
  gone: z.number().int().nonnegative().optional(),
2278
2283
  form: z.string().optional(),
2284
+ name: z.string().optional(),
2279
2285
  text: z.string().optional(),
2280
2286
  tool: z.string().optional(),
2281
2287
  err: z.boolean().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-context",
3
- "version": "0.52.0",
3
+ "version": "0.52.1",
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": {
@@ -45,7 +45,7 @@
45
45
  "coverage": "vitest run --coverage",
46
46
  "release": "bash scripts/publish.sh",
47
47
  "social-preview": "node docs/social-preview/generate.ts",
48
- "prepare": "husky"
48
+ "prepare": "husky && tsdown"
49
49
  },
50
50
  "dsh": {
51
51
  "bundle": {