dsh-context 0.52.0 → 0.52.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/README.md +11 -2
- package/lib/client.js +143 -87
- package/lib/index.d.ts +19 -0
- package/lib/index.js +53 -36
- package/package.json +2 -2
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
|
-
###
|
|
62
|
+
### Context Stats
|
|
63
|
+
|
|
64
|
+
#### Token Stats and Timing Stats
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
#### Context Stats
|
|
68
|
+

|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
### 🧱 Current Context — what's in the context window now
|
|
63
72
|
|
|
64
73
|

|
|
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 —
|
|
77
|
+
### 📈 Context Trend — how the context grew and evolved by turn or steps
|
|
69
78
|
|
|
70
79
|

|
|
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": "
|
|
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": "
|
|
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
|
}]);
|
|
@@ -1849,10 +1905,10 @@ window.__ModuleLoader__.load({
|
|
|
1849
1905
|
* - INLINE (older hosts, channel-less deployments, the baseline-gate
|
|
1850
1906
|
* fallback): the wire value carries the collections in place. It passes
|
|
1851
1907
|
* through untouched — no fetch ever happens.
|
|
1852
|
-
* - SPLIT (current host with the detail
|
|
1908
|
+
* - SPLIT (current host with the detail route live): the wire value is the
|
|
1853
1909
|
* slim head (~1KB — every session.list row, control baseline, and push
|
|
1854
|
-
* frame carries it whole). The collections arrive from the
|
|
1855
|
-
* `/dsh-context
|
|
1910
|
+
* frame carries it whole). The collections arrive from the plugin's
|
|
1911
|
+
* `/api/dsh-context/detail` fetch route (host/detail.ts): one targeted read
|
|
1856
1912
|
* when the tab/modal first opens, then a debounced refetch whenever the
|
|
1857
1913
|
* pushed `detailRev` outruns the served detail. Closed tabs fetch nothing.
|
|
1858
1914
|
*
|
|
@@ -1867,8 +1923,7 @@ window.__ModuleLoader__.load({
|
|
|
1867
1923
|
* failure surfaces as a retryable note on the detail cards instead of an
|
|
1868
1924
|
* empty chart.
|
|
1869
1925
|
*/
|
|
1870
|
-
const
|
|
1871
|
-
const DETAIL_ENDPOINT = "detail";
|
|
1926
|
+
const DETAIL_ROUTE = "/api/dsh-context/detail";
|
|
1872
1927
|
/**
|
|
1873
1928
|
* Narrow the detail endpoint's payload to a render-safe value (the same
|
|
1874
1929
|
* boundary rigor as `timelineOf`): collections re-proved per item, scalars
|
|
@@ -1879,8 +1934,10 @@ window.__ModuleLoader__.load({
|
|
|
1879
1934
|
const data = asRecord(value);
|
|
1880
1935
|
if (data === null) return null;
|
|
1881
1936
|
if (typeof data.rev !== "number" || !Number.isFinite(data.rev) || data.rev < 0) return null;
|
|
1937
|
+
const head = timelineOf(data.head);
|
|
1882
1938
|
return {
|
|
1883
1939
|
rev: data.rev,
|
|
1940
|
+
...head !== null ? { head } : {},
|
|
1884
1941
|
requests: objectsOf(data.requests),
|
|
1885
1942
|
events: objectsOf(data.events),
|
|
1886
1943
|
nodes: objectsOf(data.nodes),
|
|
@@ -1893,24 +1950,26 @@ window.__ModuleLoader__.load({
|
|
|
1893
1950
|
};
|
|
1894
1951
|
}
|
|
1895
1952
|
/**
|
|
1896
|
-
* The detail reader over the
|
|
1897
|
-
*
|
|
1898
|
-
*
|
|
1899
|
-
*
|
|
1900
|
-
*
|
|
1901
|
-
* session is not live anymore, and rejects on transport failure or a
|
|
1902
|
-
* malformed payload (the store turns the two into the retryable state).
|
|
1953
|
+
* The detail reader over the plugin's own `/api` fetch route (host/detail.ts).
|
|
1954
|
+
* Same-origin POST through the harness's authenticated fence; the returned
|
|
1955
|
+
* thunk resolves the session's current detail, `null` when the session is not
|
|
1956
|
+
* live anymore, and rejects on transport failure or a malformed payload (the
|
|
1957
|
+
* store turns the two into the retryable state).
|
|
1903
1958
|
*/
|
|
1904
1959
|
function makeDetailFetcher(ctx, sessionId) {
|
|
1905
1960
|
if (sessionId === "") return void 0;
|
|
1906
|
-
const call = rpcCallOf(ctx);
|
|
1907
|
-
if (call === void 0) return void 0;
|
|
1908
1961
|
return async () => {
|
|
1909
|
-
const
|
|
1910
|
-
|
|
1962
|
+
const response = await fetch(DETAIL_ROUTE, {
|
|
1963
|
+
method: "POST",
|
|
1964
|
+
headers: { "content-type": "application/json" },
|
|
1965
|
+
body: JSON.stringify({ sessionId })
|
|
1966
|
+
});
|
|
1967
|
+
if (!response.ok) throw new Error(`dsh-context: detail route HTTP ${response.status}`);
|
|
1968
|
+
const r = asRecord(await response.json());
|
|
1969
|
+
if (r === null || r.ok !== true) throw new Error("dsh-context: detail read failed");
|
|
1911
1970
|
if (r.value === null) return null;
|
|
1912
1971
|
const detail = detailOf(r.value);
|
|
1913
|
-
if (detail === null) throw new Error("dsh-context: detail
|
|
1972
|
+
if (detail === null) throw new Error("dsh-context: detail read malformed");
|
|
1914
1973
|
return detail;
|
|
1915
1974
|
};
|
|
1916
1975
|
}
|
|
@@ -4114,6 +4173,14 @@ window.__ModuleLoader__.load({
|
|
|
4114
4173
|
for (const n of asm.nodes) (m[n.cat] ??= []).push(n);
|
|
4115
4174
|
return m;
|
|
4116
4175
|
}
|
|
4176
|
+
/**
|
|
4177
|
+
* The producer identity a fold-stamped injection node carries
|
|
4178
|
+
* (shared/types.ts SurfaceNode.name): '' when absent or drift-typed — the
|
|
4179
|
+
* delivered payload is untrusted, so rows re-prove it before rendering.
|
|
4180
|
+
*/
|
|
4181
|
+
function nodeNameOf(n) {
|
|
4182
|
+
return typeof n.name === "string" ? n.name : "";
|
|
4183
|
+
}
|
|
4117
4184
|
function countOf(asm, byCat, c) {
|
|
4118
4185
|
if (c === "system") return asm.system !== null ? 1 : 0;
|
|
4119
4186
|
if (c === "tools") return asm.header !== null ? asm.header.tools.length : 0;
|
|
@@ -4422,12 +4489,13 @@ window.__ModuleLoader__.load({
|
|
|
4422
4489
|
const rowErr = (n.cat === "tool" || n.cat === "skill") && toolErrOf(n, conv).err;
|
|
4423
4490
|
let tag = null;
|
|
4424
4491
|
let preview = nodeText(n);
|
|
4492
|
+
const id = nodeNameOf(n);
|
|
4425
4493
|
if (n.cat === "tool") {
|
|
4426
4494
|
tag = n.tool ?? "?";
|
|
4427
4495
|
preview = callSummaryOf(conv) ?? t("node.toolResult");
|
|
4428
4496
|
} else if (n.cat === "skill") {
|
|
4429
4497
|
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;
|
|
4498
|
+
preview = (n.skill === void 0 && id !== "" ? id : null) ?? (n.text !== void 0 && n.text !== "" ? n.text : null) ?? callSummaryOf(conv) ?? preview;
|
|
4431
4499
|
} else if (n.cat === "assistant" && Array.isArray(n.calls) && n.calls.length > 0) {
|
|
4432
4500
|
tag = n.calls.join(" › ");
|
|
4433
4501
|
preview = (n.text !== void 0 && n.text !== "" ? n.text : null) ?? blockSummaryOf(conv) ?? t("node.empty");
|
|
@@ -4437,7 +4505,8 @@ window.__ModuleLoader__.load({
|
|
|
4437
4505
|
if (imgCount > 0 && openElem !== `n${n.seq}`) tag = t("attach.image") + (imgCount > 1 ? " ×" + String(imgCount) : "");
|
|
4438
4506
|
} else if (n.cat === "inject") {
|
|
4439
4507
|
tag = t("form." + (n.form || "context"));
|
|
4440
|
-
if (
|
|
4508
|
+
if (id !== "") preview = id;
|
|
4509
|
+
else if (n.text !== void 0 && n.text !== "") preview = n.form === "snapshot" ? t("node.snapshot") + n.text : n.text;
|
|
4441
4510
|
}
|
|
4442
4511
|
return {
|
|
4443
4512
|
n,
|
|
@@ -4448,7 +4517,7 @@ window.__ModuleLoader__.load({
|
|
|
4448
4517
|
};
|
|
4449
4518
|
});
|
|
4450
4519
|
const q = rowQuery.trim().toLowerCase();
|
|
4451
|
-
const shown = q === "" ? rows : rows.filter((r) => (r.tag ?? "").toLowerCase().includes(q) || r.preview.toLowerCase().includes(q));
|
|
4520
|
+
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
4521
|
const rowctl = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowToolbar, {
|
|
4453
4522
|
value: rowQuery,
|
|
4454
4523
|
placeholder: t("browser.search." + c),
|
|
@@ -4591,7 +4660,7 @@ window.__ModuleLoader__.load({
|
|
|
4591
4660
|
className: "lc-br-cat" + (openable ? "" : " lc-br-cat-empty"),
|
|
4592
4661
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4593
4662
|
type: "button",
|
|
4594
|
-
className: "lc-br-cat-row hover:bg-(--dsw-alias-interactive-bg-hover)" + (linked && props.hoverKey === c.key ? " lc-br-cat-on" : ""),
|
|
4663
|
+
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
4664
|
/* v8 ignore start -- the handlers exist only when linked,
|
|
4596
4665
|
and linked already requires onHoverKey defined (above). */
|
|
4597
4666
|
onMouseEnter: linked ? () => {
|
|
@@ -6305,8 +6374,15 @@ window.__ModuleLoader__.load({
|
|
|
6305
6374
|
* no current session, or a snapshot without a `byId` table (older harness).
|
|
6306
6375
|
* The current session synthesizes a row when the list has not delivered it
|
|
6307
6376
|
* yet, so the card can still show its live self stats.
|
|
6308
|
-
|
|
6309
|
-
|
|
6377
|
+
* @param fetched - detail-channel heads for COLD rows, keyed by session id.
|
|
6378
|
+
* The host's list block serves projection values only from the projection
|
|
6379
|
+
* cache, so a relative that never attached since the timeline unit last
|
|
6380
|
+
* changed lists pressure-only (no composition row) and its ring would
|
|
6381
|
+
* degrade to the occupancy arc; the card fetches those heads on demand.
|
|
6382
|
+
* A row's own value always wins — the injection lands only on rows whose
|
|
6383
|
+
* `contextTimeline` is absent.
|
|
6384
|
+
*/
|
|
6385
|
+
function agentForestOf(snapshot, currentId, self, fetched) {
|
|
6310
6386
|
const byId = asRecord(asRecord(snapshot)?.byId);
|
|
6311
6387
|
if (byId === null || currentId === void 0 || currentId === "") return null;
|
|
6312
6388
|
const rows = /* @__PURE__ */ new Map();
|
|
@@ -6361,7 +6437,12 @@ window.__ModuleLoader__.load({
|
|
|
6361
6437
|
const visit = (id, row, parentId, depth, seen, family) => {
|
|
6362
6438
|
if (seen.has(id) || nodes.length >= 25) return;
|
|
6363
6439
|
seen.add(id);
|
|
6364
|
-
const
|
|
6440
|
+
const values = row.projections;
|
|
6441
|
+
const fetchedHead = fetched !== void 0 ? fetched.get(id) : void 0;
|
|
6442
|
+
const stats = agentStatsOf(fetchedHead !== void 0 && values?.contextTimeline === void 0 ? {
|
|
6443
|
+
...values,
|
|
6444
|
+
contextTimeline: fetchedHead
|
|
6445
|
+
} : values);
|
|
6365
6446
|
const identity = stats.identity;
|
|
6366
6447
|
const node = {
|
|
6367
6448
|
...stats,
|
|
@@ -6620,9 +6701,13 @@ window.__ModuleLoader__.load({
|
|
|
6620
6701
|
*
|
|
6621
6702
|
* Data rides the harness's existing planes end to end — the session-list
|
|
6622
6703
|
* snapshot (`ctx.sessions.list`: lineage rows + per-session projection
|
|
6623
|
-
* values) and the tab's own projections for the current node
|
|
6624
|
-
*
|
|
6625
|
-
*
|
|
6704
|
+
* values) and the tab's own projections for the current node. The list block
|
|
6705
|
+
* serves projection values only from the host's projection cache, so a
|
|
6706
|
+
* relative that never attached since the timeline unit last changed lists
|
|
6707
|
+
* pressure-only (occupancy without composition); those nodes fetch their slim
|
|
6708
|
+
* head from the plugin's `/api` detail route (the one call below) and
|
|
6709
|
+
* re-render composed. A harness without the outward sessions service hides
|
|
6710
|
+
* the card.
|
|
6626
6711
|
*/
|
|
6627
6712
|
/** Caption box height under a node (3 wrapped label lines + the tokens line). */
|
|
6628
6713
|
const CAPTION_H = 60;
|
|
@@ -6635,6 +6720,7 @@ window.__ModuleLoader__.load({
|
|
|
6635
6720
|
}
|
|
6636
6721
|
function makeAgentGraph(ctx, kit) {
|
|
6637
6722
|
const { t, fmt, catLabel } = kit;
|
|
6723
|
+
const heads = /* @__PURE__ */ new Map();
|
|
6638
6724
|
function AgentGraph(props) {
|
|
6639
6725
|
const face = (0, react.useMemo)(() => sessionsFaceOf(ctx), []);
|
|
6640
6726
|
const subscribe = (0, react.useCallback)((fn) => {
|
|
@@ -6680,8 +6766,9 @@ window.__ModuleLoader__.load({
|
|
|
6680
6766
|
if (typeof face.refreshSubagents !== "function") return;
|
|
6681
6767
|
face.refreshSubagents(sessionId).catch(() => {});
|
|
6682
6768
|
}, [face, sessionId]);
|
|
6769
|
+
const [landed, setLanded] = (0, react.useState)(/* @__PURE__ */ new Map());
|
|
6683
6770
|
const built = (0, react.useMemo)(() => {
|
|
6684
|
-
const forest = agentForestOf(snapshot, sessionId, props.self);
|
|
6771
|
+
const forest = agentForestOf(snapshot, sessionId, props.self, landed);
|
|
6685
6772
|
return forest !== null ? {
|
|
6686
6773
|
forest,
|
|
6687
6774
|
layout: layoutForest(forest, stageWidth)
|
|
@@ -6690,8 +6777,29 @@ window.__ModuleLoader__.load({
|
|
|
6690
6777
|
snapshot,
|
|
6691
6778
|
sessionId,
|
|
6692
6779
|
props.self,
|
|
6693
|
-
stageWidth
|
|
6780
|
+
stageWidth,
|
|
6781
|
+
landed
|
|
6694
6782
|
]);
|
|
6783
|
+
(0, react.useEffect)(() => {
|
|
6784
|
+
if (built === null) return;
|
|
6785
|
+
const attach = (pending, id) => {
|
|
6786
|
+
pending.then((head) => {
|
|
6787
|
+
if (head !== null) setLanded((prev) => prev.get(id) === head ? prev : new Map(prev).set(id, head));
|
|
6788
|
+
}).catch(() => {});
|
|
6789
|
+
};
|
|
6790
|
+
for (const n of built.forest.nodes) {
|
|
6791
|
+
if (n.isCurrent || n.head !== null && n.head.parts.length > 0) continue;
|
|
6792
|
+
const cached = heads.get(n.id);
|
|
6793
|
+
if (cached !== void 0) {
|
|
6794
|
+
attach(cached, n.id);
|
|
6795
|
+
continue;
|
|
6796
|
+
}
|
|
6797
|
+
const fetcher = makeDetailFetcher(ctx, n.id);
|
|
6798
|
+
const pending = fetcher !== void 0 ? fetcher().then((d) => d?.head ?? null) : Promise.resolve(null);
|
|
6799
|
+
heads.set(n.id, pending);
|
|
6800
|
+
attach(pending, n.id);
|
|
6801
|
+
}
|
|
6802
|
+
}, [built]);
|
|
6695
6803
|
if (built === null) return null;
|
|
6696
6804
|
const { forest, layout } = built;
|
|
6697
6805
|
const byId = new Map(forest.nodes.map((n) => [n.id, n]));
|
|
@@ -7533,7 +7641,7 @@ window.__ModuleLoader__.load({
|
|
|
7533
7641
|
return function PluginInfo() {
|
|
7534
7642
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
7535
7643
|
(0, react.useEffect)(() => {
|
|
7536
|
-
if ("0.52.
|
|
7644
|
+
if ("0.52.2".includes("-dev")) return;
|
|
7537
7645
|
let on = true;
|
|
7538
7646
|
fetchLatestVersion().then((v) => {
|
|
7539
7647
|
if (on && v) setLatest(v);
|
|
@@ -7542,8 +7650,8 @@ window.__ModuleLoader__.load({
|
|
|
7542
7650
|
on = false;
|
|
7543
7651
|
};
|
|
7544
7652
|
}, []);
|
|
7545
|
-
const update = latest !== null && isNewerVersion(latest, "0.52.
|
|
7546
|
-
const nameText = "dsh-context (v0.52.
|
|
7653
|
+
const update = latest !== null && isNewerVersion(latest, "0.52.2") ? latest : null;
|
|
7654
|
+
const nameText = "dsh-context (v0.52.2)";
|
|
7547
7655
|
const nameValue = [nameText];
|
|
7548
7656
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7549
7657
|
className: "lc-pi-update",
|
|
@@ -9382,58 +9490,6 @@ window.__ModuleLoader__.load({
|
|
|
9382
9490
|
};
|
|
9383
9491
|
}
|
|
9384
9492
|
//#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
9493
|
//#region src/client/sidebar.ts
|
|
9438
9494
|
/**
|
|
9439
9495
|
* The right Sidebar's Context tab (dsh 0.1.5-rc.1+).
|
|
@@ -9665,7 +9721,7 @@ window.__ModuleLoader__.load({
|
|
|
9665
9721
|
}
|
|
9666
9722
|
//#endregion
|
|
9667
9723
|
//#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)}";
|
|
9724
|
+
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
9725
|
const tagId$3 = "dsh-context/browser.css";
|
|
9670
9726
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
|
|
9671
9727
|
const tag = document.createElement("style");
|
package/lib/index.d.ts
CHANGED
|
@@ -446,6 +446,17 @@ interface Snapshot {
|
|
|
446
446
|
*/
|
|
447
447
|
interface ContextTimelineDetail {
|
|
448
448
|
rev: number;
|
|
449
|
+
/**
|
|
450
|
+
* The slim wire head at the SAME fold cut as the collections: the
|
|
451
|
+
* composition scalars (`current`), the window/model envelope, and the
|
|
452
|
+
* precomputed counts. Sessions listed cold (never attached since the
|
|
453
|
+
* requesting unit last changed) carry no `contextTimeline` projection row
|
|
454
|
+
* for the browser's list reads, so the Agent network card fetches this
|
|
455
|
+
* head per node to render their composition rings. The host always serves
|
|
456
|
+
* it; optional so a payload missing it still serves the collections (the
|
|
457
|
+
* detail cards) and only the ring composition degrades.
|
|
458
|
+
*/
|
|
459
|
+
head?: ContextTimeline;
|
|
449
460
|
requests: RequestRecord[];
|
|
450
461
|
events: ContextEventRecord[];
|
|
451
462
|
nodes: SurfaceNode[];
|
|
@@ -620,6 +631,14 @@ interface SurfaceNode {
|
|
|
620
631
|
*/
|
|
621
632
|
gone?: number;
|
|
622
633
|
form?: string;
|
|
634
|
+
/**
|
|
635
|
+
* The producer identity the matching inject event names (host pricing.ts
|
|
636
|
+
* `injectionSourceName`: the plugin id, the reconciled instruction files,
|
|
637
|
+
* or the durable kind). Stamped on injection nodes alongside the event, so
|
|
638
|
+
* the browser rows label them the way the events card does; absent when the
|
|
639
|
+
* source carries no readable identity or the node predates the stamp.
|
|
640
|
+
*/
|
|
641
|
+
name?: string;
|
|
623
642
|
text?: string;
|
|
624
643
|
tool?: string;
|
|
625
644
|
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
|
|
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
|
-
|
|
609
|
-
|
|
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 !== "")
|
|
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);
|
|
@@ -1928,11 +1933,14 @@ function buildTimelineHead(state) {
|
|
|
1928
1933
|
}
|
|
1929
1934
|
/**
|
|
1930
1935
|
* The on-demand detail payload (host/detail.ts serves it off the live fold
|
|
1931
|
-
* state): the heavy collections
|
|
1936
|
+
* state): the heavy collections, the slim head at the SAME cut (the Agent
|
|
1937
|
+
* network's cold-node ring fetch renders composition off it), and the
|
|
1938
|
+
* revision marker the head carries.
|
|
1932
1939
|
*/
|
|
1933
1940
|
function buildTimelineDetail(state, bounds) {
|
|
1934
1941
|
return {
|
|
1935
1942
|
rev: state.detailRev ?? 0,
|
|
1943
|
+
head: buildTimelineHead(state),
|
|
1936
1944
|
...detailCollectionsOf(state, bounds)
|
|
1937
1945
|
};
|
|
1938
1946
|
}
|
|
@@ -1951,63 +1959,71 @@ function buildTimelineView(state, bounds) {
|
|
|
1951
1959
|
}
|
|
1952
1960
|
//#endregion
|
|
1953
1961
|
//#region src/host/detail.ts
|
|
1954
|
-
/** The plugin's
|
|
1955
|
-
const
|
|
1956
|
-
/**
|
|
1962
|
+
/** The plugin's detail route, under the authenticated `/api` fence. */
|
|
1963
|
+
const DETAIL_ROUTE = "/api/dsh-context/detail";
|
|
1964
|
+
/** One JSON reply of the detail route. */
|
|
1965
|
+
function reply(value) {
|
|
1966
|
+
return Response.json(value, { headers: { "cache-control": "no-store" } });
|
|
1967
|
+
}
|
|
1968
|
+
/** The typed failure envelope (the same shape the Connection RPC carried). */
|
|
1957
1969
|
function failure(code, message) {
|
|
1958
|
-
return {
|
|
1970
|
+
return reply({
|
|
1959
1971
|
ok: false,
|
|
1960
1972
|
error: {
|
|
1961
1973
|
code,
|
|
1962
|
-
message
|
|
1963
|
-
details: {}
|
|
1974
|
+
message
|
|
1964
1975
|
}
|
|
1965
|
-
};
|
|
1976
|
+
});
|
|
1966
1977
|
}
|
|
1967
1978
|
/**
|
|
1968
|
-
*
|
|
1979
|
+
* Serve the detail route whenever the connection and sessions services are
|
|
1969
1980
|
* both composed (see the module header for the load-order contract). The
|
|
1970
1981
|
* registration rides the injected fiber: either service unloading withdraws
|
|
1971
|
-
* the
|
|
1982
|
+
* the route and closes the gate.
|
|
1972
1983
|
*/
|
|
1973
1984
|
function watchDetailChannel(ctx, bounds) {
|
|
1974
1985
|
const gate = { live: false };
|
|
1975
1986
|
ctx.inject(["connection", "sessions"], (c) => {
|
|
1976
1987
|
const connection = c.get("connection");
|
|
1977
1988
|
const sessions = c.get("sessions");
|
|
1978
|
-
const
|
|
1989
|
+
const register = typeof connection?.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
1979
1990
|
const getSession = typeof sessions?.get === "function" ? sessions.get.bind(sessions) : void 0;
|
|
1980
|
-
if (
|
|
1991
|
+
if (register === void 0 || getSession === void 0) return;
|
|
1981
1992
|
const projections = ctx.sessionProjections;
|
|
1982
|
-
const handler = async (
|
|
1983
|
-
|
|
1984
|
-
|
|
1993
|
+
const handler = async (request) => {
|
|
1994
|
+
let sessionId;
|
|
1995
|
+
try {
|
|
1996
|
+
const body = await request.json();
|
|
1997
|
+
sessionId = body !== null && typeof body === "object" ? body.sessionId : void 0;
|
|
1998
|
+
} catch {
|
|
1999
|
+
return failure("dsh-context/bad-request", "body is not JSON");
|
|
2000
|
+
}
|
|
1985
2001
|
if (typeof sessionId !== "string" || sessionId === "") return failure("dsh-context/bad-request", "missing sessionId");
|
|
1986
2002
|
try {
|
|
1987
2003
|
const session = getSession(sessionId);
|
|
1988
2004
|
if (session !== void 0 && session !== null) {
|
|
1989
2005
|
const state = projections.stateOf(session, "contextTimeline");
|
|
1990
|
-
if (state === void 0) return {
|
|
2006
|
+
if (state === void 0) return reply({
|
|
1991
2007
|
ok: true,
|
|
1992
2008
|
value: null
|
|
1993
|
-
};
|
|
1994
|
-
return {
|
|
2009
|
+
});
|
|
2010
|
+
return reply({
|
|
1995
2011
|
ok: true,
|
|
1996
2012
|
value: buildTimelineDetail(state, bounds)
|
|
1997
|
-
};
|
|
2013
|
+
});
|
|
1998
2014
|
}
|
|
1999
2015
|
const query = ctx.get("sessionQuery");
|
|
2000
2016
|
const observe = typeof query?.observeSession === "function" ? query.observeSession.bind(query) : void 0;
|
|
2001
|
-
if (observe === void 0) return {
|
|
2017
|
+
if (observe === void 0) return reply({
|
|
2002
2018
|
ok: true,
|
|
2003
2019
|
value: null
|
|
2004
|
-
};
|
|
2020
|
+
});
|
|
2005
2021
|
const observation = await observe(sessionId, { projectionMode: "none" });
|
|
2006
2022
|
const events = observation?.events;
|
|
2007
|
-
if (!Array.isArray(events)) return {
|
|
2023
|
+
if (!Array.isArray(events)) return reply({
|
|
2008
2024
|
ok: true,
|
|
2009
2025
|
value: null
|
|
2010
|
-
};
|
|
2026
|
+
});
|
|
2011
2027
|
let state = createTimelineState();
|
|
2012
2028
|
try {
|
|
2013
2029
|
for (const ev of events) state = applyTimeline(state, ev, bounds);
|
|
@@ -2015,21 +2031,21 @@ function watchDetailChannel(ctx, bounds) {
|
|
|
2015
2031
|
const dispose = observation?.[Symbol.dispose];
|
|
2016
2032
|
if (typeof dispose === "function") dispose.call(observation);
|
|
2017
2033
|
}
|
|
2018
|
-
return {
|
|
2034
|
+
return reply({
|
|
2019
2035
|
ok: true,
|
|
2020
2036
|
value: buildTimelineDetail(state, bounds)
|
|
2021
|
-
};
|
|
2037
|
+
});
|
|
2022
2038
|
} catch (err) {
|
|
2023
2039
|
return failure("gateway/internal", err instanceof Error ? err.message : String(err));
|
|
2024
2040
|
}
|
|
2025
2041
|
};
|
|
2026
2042
|
try {
|
|
2027
|
-
c.effect(() => {
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
}, "dsh-context: detail
|
|
2043
|
+
c.effect(() => register({
|
|
2044
|
+
path: DETAIL_ROUTE,
|
|
2045
|
+
methods: ["POST"],
|
|
2046
|
+
requestBody: "buffered",
|
|
2047
|
+
fetch: handler
|
|
2048
|
+
}), "dsh-context: detail route");
|
|
2033
2049
|
} catch {
|
|
2034
2050
|
return;
|
|
2035
2051
|
}
|
|
@@ -2276,6 +2292,7 @@ const surfaceNodeSchema = z.object({
|
|
|
2276
2292
|
imgs: z.number().int().nonnegative().optional(),
|
|
2277
2293
|
gone: z.number().int().nonnegative().optional(),
|
|
2278
2294
|
form: z.string().optional(),
|
|
2295
|
+
name: z.string().optional(),
|
|
2279
2296
|
text: z.string().optional(),
|
|
2280
2297
|
tool: z.string().optional(),
|
|
2281
2298
|
err: z.boolean().optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.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": {
|
|
@@ -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": {
|