dsh-context 0.10.2 → 0.11.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.
- package/README.md +13 -8
- package/lib/client.js +158 -42
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,21 +5,26 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/dsh-context)
|
|
6
6
|
[](https://github.com/bowenliang123/dsh-context)
|
|
7
7
|
|
|
8
|
-
**
|
|
8
|
+
**The best [DeepSeek Harness plugin](https://www.deepseek.com/harness/) for Agent's context insights and management.**
|
|
9
9
|
|
|
10
|
-
`dsh-context`
|
|
10
|
+
`dsh-context` provides full context lifecycle management features.
|
|
11
|
+
- **Context tab** — an UI context dashboard for DeepSeek Harness’s context stats, composition, history, events, and messages.
|
|
12
|
+
- **`/context` command** — the slash command shows the context model for current context composition and recent context evolution.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
- **The `/context` command** — the same headline and recent trend as a centered dialog straight from the composer (the screenshot above).
|
|
14
|
+
## Install / Update
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
One command, from any DeepSeek Harness installation:
|
|
16
|
+
To Install from any DeepSeek Harness installation:
|
|
18
17
|
|
|
19
18
|
```sh
|
|
20
19
|
dsh plugin --profile web add dsh-context
|
|
21
20
|
```
|
|
22
21
|
|
|
22
|
+
Or to update the `dsh-context` plugin:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
dsh plugin --profile web update dsh-context@latest
|
|
26
|
+
```
|
|
27
|
+
|
|
23
28
|
Then start the web UI with `dsh web`. No build step, no restart.
|
|
24
29
|
|
|
25
30
|
## Use it
|
|
@@ -40,7 +45,7 @@ Type `/context` (or pick it from the `/` menu) and press Enter: a centered dialo
|
|
|
40
45
|
|
|
41
46
|
### 📊 Context stats — the session at a glance
|
|
42
47
|
|
|
43
|
-
Turns, steps, how
|
|
48
|
+
Turns, steps, how many injections, compactions, and prunes have happened.
|
|
44
49
|
|
|
45
50
|
### 🧱 Current composition — what's in the window right now
|
|
46
51
|
|
package/lib/client.js
CHANGED
|
@@ -25,13 +25,13 @@ var DICT_ZH = {
|
|
|
25
25
|
"stats.hint": "\u7EDF\u8BA1\u5F53\u524D\u4FDD\u7559\u7684\u5386\u53F2\u7A97\u53E3\uFF08\u4E0E\u8D8B\u52BF\u56FE\u4E00\u81F4\uFF09",
|
|
26
26
|
"stats.turns": "\u8F6E\u6B21",
|
|
27
27
|
"stats.steps": "\u6B65\u6570",
|
|
28
|
-
"stats.
|
|
29
|
-
"stats.
|
|
30
|
-
"stats.
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
28
|
+
"stats.injects": "\u6CE8\u5165",
|
|
29
|
+
"stats.compactions": "\u538B\u7F29",
|
|
30
|
+
"stats.prunes": "\u526A\u679D",
|
|
31
|
+
"plugin.title": "\u63D2\u4EF6\u4FE1\u606F",
|
|
32
|
+
"plugin.hint": "The best DSH context plugin \u2B50",
|
|
33
|
+
"plugin.name": "\u63D2\u4EF6",
|
|
34
|
+
"plugin.github": "GitHub",
|
|
35
35
|
"tools.top": "\u5DE5\u5177\u5B9A\u4E49 Top\uFF1A",
|
|
36
36
|
"tools.more": "\u7B49 {n} \u4E2A",
|
|
37
37
|
"trend.title": "\u5386\u53F2\u8D8B\u52BF",
|
|
@@ -50,6 +50,10 @@ var DICT_ZH = {
|
|
|
50
50
|
"events.at": "\u7B2C {t} \u8F6E \xB7 \u7B2C {s} \u6B65",
|
|
51
51
|
"events.range": "\u7B2C {t} \u8F6E \xB7 \u7B2C {a}\u2192{b} \u6B65",
|
|
52
52
|
"events.rangeTo": "\u7B2C {a} \u8F6E \xB7 \u7B2C {as} \u6B65 \u2192 \u7B2C {b} \u8F6E \xB7 \u7B2C {bs} \u6B65",
|
|
53
|
+
"kind.inject": "\u6CE8\u5165",
|
|
54
|
+
"kind.compaction": "\u538B\u7F29",
|
|
55
|
+
"kind.prune": "\u526A\u679D",
|
|
56
|
+
"kind.model": "\u5207\u6362",
|
|
53
57
|
"nodes.title": "\u6D88\u606F\u6784\u6210",
|
|
54
58
|
"nodes.hint": "\u5F53\u524D\u6A21\u578B\u53EF\u89C1\u7684\u6D88\u606F\uFF0C\u6700\u65B0\u5728\u524D",
|
|
55
59
|
"nodes.more": "\u2026 \u66F4\u65E9\u7684 {n} \u6761\u6D88\u606F\u5DF2\u7701\u7565",
|
|
@@ -98,13 +102,13 @@ var DICT_EN = {
|
|
|
98
102
|
"stats.hint": "Over the retained history window (same as the History chart)",
|
|
99
103
|
"stats.turns": "Turns",
|
|
100
104
|
"stats.steps": "Steps",
|
|
101
|
-
"stats.
|
|
102
|
-
"stats.
|
|
103
|
-
"stats.
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
105
|
+
"stats.injects": "Injections",
|
|
106
|
+
"stats.compactions": "Compactions",
|
|
107
|
+
"stats.prunes": "Prunes",
|
|
108
|
+
"plugin.title": "Plugin info",
|
|
109
|
+
"plugin.hint": "The best DSH context plugin \u2B50",
|
|
110
|
+
"plugin.name": "Plugin",
|
|
111
|
+
"plugin.github": "GitHub",
|
|
108
112
|
"tools.top": "Top tool schemas:",
|
|
109
113
|
"tools.more": "of {n}",
|
|
110
114
|
"trend.title": "History",
|
|
@@ -123,6 +127,10 @@ var DICT_EN = {
|
|
|
123
127
|
"events.at": "Turn {t} \xB7 Step {s}",
|
|
124
128
|
"events.range": "Turn {t} \xB7 Step {a}\u2192{b}",
|
|
125
129
|
"events.rangeTo": "Turn {a} \xB7 Step {as} \u2192 Turn {b} \xB7 Step {bs}",
|
|
130
|
+
"kind.inject": "Inject",
|
|
131
|
+
"kind.compaction": "Compact",
|
|
132
|
+
"kind.prune": "Prune",
|
|
133
|
+
"kind.model": "Switch",
|
|
126
134
|
"nodes.title": "Messages",
|
|
127
135
|
"nodes.hint": "currently model-visible, newest first",
|
|
128
136
|
"nodes.more": "\u2026 {n} earlier messages omitted",
|
|
@@ -656,11 +664,11 @@ var STYLES = [
|
|
|
656
664
|
".lc-card-title { font-weight: 600; margin-bottom: 10px; display: flex; align-items: baseline; gap: 8px; }",
|
|
657
665
|
".lc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 8px; }",
|
|
658
666
|
".lc-stat { background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; padding: 8px 10px; display: flex; flex-direction: column; gap: 2px; min-width: 0; }",
|
|
659
|
-
".lc-stat-label { color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
|
|
660
|
-
".lc-stat-value { color: var(--dsw-alias-label-primary); font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
|
|
667
|
+
".lc-stat-label { color: var(--dsw-alias-label-secondary); font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
|
|
668
|
+
".lc-stat-value { color: var(--dsw-alias-label-primary); font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: center; }",
|
|
661
669
|
".lc-stat-sub { color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
|
|
662
670
|
".lc-card-sub { font-weight: 400; color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
663
|
-
".lc-gran { margin-left: auto; display: flex; gap: 2px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 1px; }",
|
|
671
|
+
".lc-gran, .lc-kinds { margin-left: auto; display: flex; gap: 2px; background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 1px; }",
|
|
664
672
|
".lc-gran-btn { border: 0; background: transparent; color: var(--dsw-alias-label-secondary); font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 5px; cursor: pointer; font-family: inherit; }",
|
|
665
673
|
".lc-gran-btn:hover { color: var(--dsw-alias-label-primary); }",
|
|
666
674
|
".lc-gran-on, .lc-gran-on:hover { background: var(--dsw-alias-button-primary-fill); color: var(--dsw-alias-label-primary-foreground); }",
|
|
@@ -746,14 +754,47 @@ var STYLES = [
|
|
|
746
754
|
".lc-detail-pct { width: 34px; text-align: right; color: var(--dsw-alias-label-secondary); }",
|
|
747
755
|
".lc-cols { display: flex; gap: 14px; flex-wrap: wrap; }",
|
|
748
756
|
".lc-col { flex: 1; min-width: 280px; }",
|
|
757
|
+
// Head row: stats board + plugin info sit side by side under the shared
|
|
758
|
+
// `lc-cols` flex — stats takes ~7/10 of the row, plugin info ~3/10, both
|
|
759
|
+
// wrap onto their own line when the available width falls below each card's
|
|
760
|
+
// min-width (so a narrow viewport keeps both readable). Both children are
|
|
761
|
+
// `.lc-card` themselves (rendered by StatsBoard / PluginInfo); the flex
|
|
762
|
+
// sizing lives on the head row's direct children so the existing card
|
|
763
|
+
// chrome doesn't change.
|
|
764
|
+
".lc-head > .lc-card { margin-bottom: 0; }",
|
|
765
|
+
".lc-head > .lc-card:first-child { flex: 7 1 0; min-width: 360px; }",
|
|
766
|
+
".lc-head > .lc-card:last-child { flex: 3 1 0; min-width: 220px; }",
|
|
767
|
+
// Plugin info: two full-width rows (Plugin / GitHub), each one horizontal
|
|
768
|
+
// line with the label on the left and the value on the right — a compact
|
|
769
|
+
// definition list rather than a multi-cell grid.
|
|
770
|
+
".lc-pi-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }",
|
|
771
|
+
// The row IS the link: an `<a>` with the label + value on one baseline.
|
|
772
|
+
// Hovering a row underlines its value — the only hover affordance.
|
|
773
|
+
".lc-pi-row { display: flex; flex-direction: row; justify-content: space-between; align-items: baseline; gap: 12px; min-width: 0; text-decoration: none; color: inherit; }",
|
|
774
|
+
".lc-pi-row:hover .lc-pi-value { text-decoration: underline; }",
|
|
775
|
+
// Upgrade chip appended to the Plugin value when the npm registry has a
|
|
776
|
+
// newer version than the baked-in one.
|
|
777
|
+
".lc-pi-update { color: var(--dsw-alias-state-warn-primary); font-size: 11px; margin-left: 6px; white-space: nowrap; }",
|
|
778
|
+
".lc-pi-label { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }",
|
|
779
|
+
// min-width: 0 lets the value shrink inside the flex row and truncate with
|
|
780
|
+
// an ellipsis; without it a narrow card pushes the value over the label.
|
|
781
|
+
".lc-pi-value { min-width: 0; color: var(--dsw-alias-label-primary); font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: right; }",
|
|
749
782
|
".lc-events, .lc-nodes { display: flex; flex-direction: column; gap: 2px; max-height: 320px; overflow-y: auto; }",
|
|
750
783
|
".lc-event { display: flex; align-items: center; gap: 8px; padding: 3px 0; }",
|
|
751
784
|
".lc-event-icon { width: 18px; text-align: center; color: var(--dsw-alias-state-warn-primary); }",
|
|
752
785
|
".lc-event-icon.lc-event-inject { color: #a855f7; }",
|
|
753
786
|
".lc-event-icon.lc-event-model { color: var(--dsw-alias-brand-primary); }",
|
|
787
|
+
// Kind chip: the event classification at a glance; the tint matches the
|
|
788
|
+
// impact direction (inject = adds context, compaction/prune = frees it,
|
|
789
|
+
// model switch = neutral), mirroring the token sign colors below.
|
|
790
|
+
".lc-kind { flex: none; font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 4px; white-space: nowrap; }",
|
|
791
|
+
".lc-kind-inject { background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent); color: var(--dsw-alias-state-success-primary); }",
|
|
792
|
+
".lc-kind-compaction { background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent); color: var(--dsw-alias-state-error-primary); }",
|
|
793
|
+
".lc-kind-prune { background: color-mix(in srgb, #8b5cf6 15%, transparent); color: #8b5cf6; }",
|
|
794
|
+
".lc-kind-model { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent); color: var(--dsw-alias-brand-primary); }",
|
|
754
795
|
".lc-event-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
755
796
|
".lc-event-at { flex: none; color: var(--dsw-alias-label-secondary); font-size: 11px; white-space: nowrap; }",
|
|
756
|
-
".lc-event-tokens { color: var(--dsw-alias-state-success-primary); }",
|
|
797
|
+
".lc-event-tokens { color: var(--dsw-alias-state-success-primary); font-weight: 600; white-space: nowrap; }",
|
|
757
798
|
".lc-event-tokens.lc-up { color: var(--dsw-alias-state-warn-primary); }",
|
|
758
799
|
".lc-event-time { color: var(--dsw-alias-label-secondary); font-size: 12px; }",
|
|
759
800
|
".lc-node { display: flex; align-items: center; gap: 8px; padding: 3px 0; }",
|
|
@@ -829,12 +870,25 @@ function makeEventList(kit) {
|
|
|
829
870
|
if (props.events.length === 0) {
|
|
830
871
|
return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
|
|
831
872
|
}
|
|
873
|
+
const rootRef = React.useRef(null);
|
|
874
|
+
React.useLayoutEffect(() => {
|
|
875
|
+
const root = rootRef.current;
|
|
876
|
+
if (!root) return;
|
|
877
|
+
const sync = () => {
|
|
878
|
+
for (const el of root.querySelectorAll(".lc-event-label")) {
|
|
879
|
+
el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
sync();
|
|
883
|
+
window.addEventListener("resize", sync);
|
|
884
|
+
return () => window.removeEventListener("resize", sync);
|
|
885
|
+
});
|
|
832
886
|
const sorted = props.events.slice().reverse();
|
|
833
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-events" }, sorted.map((ev, i) => {
|
|
887
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-events", ref: rootRef }, sorted.map((ev, i) => {
|
|
834
888
|
const label = eventLabel(ev);
|
|
835
889
|
const at = eventAt(ev);
|
|
836
890
|
const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(import_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "\u2022";
|
|
837
|
-
return /* @__PURE__ */ React.createElement("div", { key: ev.seq + "-" + i, className: "lc-event" }, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-
|
|
891
|
+
return /* @__PURE__ */ React.createElement("div", { key: ev.seq + "-" + i, className: "lc-event" }, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-kind lc-kind-" + ev.kind }, t("kind." + ev.kind)), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label" }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "\u2212") + fmt3(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime2(ev.time)));
|
|
838
892
|
}));
|
|
839
893
|
};
|
|
840
894
|
}
|
|
@@ -867,41 +921,83 @@ function makeNodeList(kit) {
|
|
|
867
921
|
};
|
|
868
922
|
}
|
|
869
923
|
|
|
924
|
+
// src/client/meta.ts
|
|
925
|
+
var PLUGIN_NAME = "dsh-context";
|
|
926
|
+
var PLUGIN_VERSION = true ? "0.11.0" : "0.0.0-dev";
|
|
927
|
+
var PLUGIN_REPO = true ? "https://github.com/bowenliang123/dsh-context" : "https://github.com/bowenliang123/dsh-context";
|
|
928
|
+
var PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
929
|
+
|
|
930
|
+
// src/client/latestVersion.ts
|
|
931
|
+
var REGISTRY_URL = "https://registry.npmjs.org/" + PLUGIN_NAME + "/latest";
|
|
932
|
+
var TTL_MS = 60 * 60 * 1e3;
|
|
933
|
+
var cached = null;
|
|
934
|
+
function fetchLatestVersion() {
|
|
935
|
+
if (!cached || Date.now() - cached.at >= TTL_MS) {
|
|
936
|
+
cached = {
|
|
937
|
+
at: Date.now(),
|
|
938
|
+
promise: fetch(REGISTRY_URL).then((res) => res.ok ? res.json() : null).then((body) => body && typeof body.version === "string" ? body.version : null).catch(() => null)
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
return cached.promise;
|
|
942
|
+
}
|
|
943
|
+
function isNewerVersion(latest, current) {
|
|
944
|
+
const parse = (v) => v.replace(/^v/, "").split("-", 1)[0].split(".").map((n) => parseInt(n, 10) || 0);
|
|
945
|
+
const a = parse(latest);
|
|
946
|
+
const b = parse(current);
|
|
947
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
948
|
+
const x = a[i] || 0;
|
|
949
|
+
const y = b[i] || 0;
|
|
950
|
+
if (x !== y) return x > y;
|
|
951
|
+
}
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// src/client/components/pluginInfo.tsx
|
|
956
|
+
function makePluginInfo(kit) {
|
|
957
|
+
const { t } = kit;
|
|
958
|
+
const row = (label, value, href) => /* @__PURE__ */ React.createElement("a", { className: "lc-pi-row", href, target: "_blank", rel: "noreferrer" }, /* @__PURE__ */ React.createElement("div", { className: "lc-pi-label" }, label), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-value" }, value));
|
|
959
|
+
return function PluginInfo() {
|
|
960
|
+
const [latest, setLatest] = React.useState(null);
|
|
961
|
+
React.useEffect(() => {
|
|
962
|
+
if (PLUGIN_VERSION.includes("-dev")) return;
|
|
963
|
+
let on = true;
|
|
964
|
+
fetchLatestVersion().then((v) => {
|
|
965
|
+
if (on && v) setLatest(v);
|
|
966
|
+
});
|
|
967
|
+
return () => {
|
|
968
|
+
on = false;
|
|
969
|
+
};
|
|
970
|
+
}, []);
|
|
971
|
+
const update = latest !== null && isNewerVersion(latest, PLUGIN_VERSION) ? latest : null;
|
|
972
|
+
const nameValue = [PLUGIN_NAME + " (v" + PLUGIN_VERSION + ")"];
|
|
973
|
+
if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", { key: "update", className: "lc-pi-update" }, "\u2191 v" + update));
|
|
974
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("plugin.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
870
978
|
// src/client/components/statsBoard.tsx
|
|
871
979
|
function makeStatsBoard(kit) {
|
|
872
|
-
const { t,
|
|
980
|
+
const { t, fmt: fmt3 } = kit;
|
|
873
981
|
return function StatsBoard(props) {
|
|
874
982
|
const turns = /* @__PURE__ */ new Set();
|
|
875
|
-
let steps = 0, compactions = 0, prunes = 0, injects = 0
|
|
876
|
-
let recycled = 0, est = 0, prompt = 0, output = 0;
|
|
983
|
+
let steps = 0, compactions = 0, prunes = 0, injects = 0;
|
|
877
984
|
for (const req of props.requests) {
|
|
878
985
|
turns.add(req.turn ?? 0);
|
|
879
986
|
steps++;
|
|
880
|
-
est += req.total;
|
|
881
|
-
if (typeof req.prompt === "number") prompt += req.prompt;
|
|
882
|
-
if (typeof req.output === "number") output += req.output;
|
|
883
987
|
}
|
|
884
988
|
for (const ev of props.events) {
|
|
885
|
-
if (ev.kind === "compaction")
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
} else if (ev.kind === "prune") {
|
|
889
|
-
prunes++;
|
|
890
|
-
recycled += ev.tokens || 0;
|
|
891
|
-
} else if (ev.kind === "inject") injects++;
|
|
892
|
-
else if (ev.kind === "model") switches++;
|
|
989
|
+
if (ev.kind === "compaction") compactions++;
|
|
990
|
+
else if (ev.kind === "prune") prunes++;
|
|
991
|
+
else if (ev.kind === "inject") injects++;
|
|
893
992
|
}
|
|
894
|
-
const cell = (label, value
|
|
895
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("stats.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt3(turns.size)), cell(t("stats.steps"), fmt3(steps)), cell(
|
|
896
|
-
t("stats.recycled"),
|
|
897
|
-
recycled > 0 ? "\u2212" + fmt3(recycled) : "0",
|
|
898
|
-
tr("stats.recycleSub", { c: compactions, p: prunes })
|
|
899
|
-
), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.switches"), fmt3(switches)), cell(t("stats.est"), "\u2248 " + fmt3(est)), cell(t("stats.actualPrompt"), fmt3(prompt)), cell(t("stats.output"), fmt3(output))));
|
|
993
|
+
const cell = (label, value) => /* @__PURE__ */ React.createElement("div", { className: "lc-stat" }, /* @__PURE__ */ React.createElement("span", { className: "lc-stat-label" }, label), /* @__PURE__ */ React.createElement("b", { className: "lc-stat-value" }, value));
|
|
994
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col lc-col-stats" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("stats.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("stats.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-stats" }, cell(t("stats.turns"), fmt3(turns.size)), cell(t("stats.steps"), fmt3(steps)), cell(t("stats.injects"), fmt3(injects)), cell(t("stats.compactions"), fmt3(compactions)), cell(t("stats.prunes"), fmt3(prunes))));
|
|
900
995
|
};
|
|
901
996
|
}
|
|
902
997
|
|
|
903
998
|
// src/client/components/contextView.tsx
|
|
904
999
|
var viewScroll = /* @__PURE__ */ new Map();
|
|
1000
|
+
var EVENT_KINDS = ["inject", "compaction", "prune", "model"];
|
|
905
1001
|
function makeContextView(ctx, kit) {
|
|
906
1002
|
const { t, tr, fmt: fmt3, fmtTime: fmtTime2, catLabel } = kit;
|
|
907
1003
|
const StackedBar = makeStackedBar(kit);
|
|
@@ -911,6 +1007,7 @@ function makeContextView(ctx, kit) {
|
|
|
911
1007
|
const EventList = makeEventList(kit);
|
|
912
1008
|
const NodeList = makeNodeList(kit);
|
|
913
1009
|
const StatsBoard = makeStatsBoard(kit);
|
|
1010
|
+
const PluginInfo = makePluginInfo(kit);
|
|
914
1011
|
return function ContextView(props) {
|
|
915
1012
|
const sessionId = props.sessionId;
|
|
916
1013
|
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
@@ -921,6 +1018,14 @@ function makeContextView(ctx, kit) {
|
|
|
921
1018
|
const [tick, setTick] = React.useState(0);
|
|
922
1019
|
const [granularity, setGranularity] = React.useState("step");
|
|
923
1020
|
const [hoverCat, setHoverCat] = React.useState(null);
|
|
1021
|
+
const [pickedKinds, setPickedKinds] = React.useState([...EVENT_KINDS]);
|
|
1022
|
+
const toggleKind = (k) => {
|
|
1023
|
+
setPickedKinds((p) => {
|
|
1024
|
+
if (p.length === EVENT_KINDS.length) return [k];
|
|
1025
|
+
if (!p.includes(k)) return [...p, k];
|
|
1026
|
+
return p.length === 1 ? [...EVENT_KINDS] : p.filter((x) => x !== k);
|
|
1027
|
+
});
|
|
1028
|
+
};
|
|
924
1029
|
const rootRef = React.useRef(null);
|
|
925
1030
|
const scrollerRef = React.useRef(null);
|
|
926
1031
|
const restoredRef = React.useRef(null);
|
|
@@ -952,6 +1057,7 @@ function makeContextView(ctx, kit) {
|
|
|
952
1057
|
}
|
|
953
1058
|
const requests = data.requests || [];
|
|
954
1059
|
const events = data.events || [];
|
|
1060
|
+
const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
|
|
955
1061
|
const nodes = data.nodes || [];
|
|
956
1062
|
const displayRequests = granularity === "turn" ? aggregateByTurn(requests) : requests;
|
|
957
1063
|
const markers = attachMarkers(displayRequests, events);
|
|
@@ -975,7 +1081,7 @@ function makeContextView(ctx, kit) {
|
|
|
975
1081
|
}
|
|
976
1082
|
}
|
|
977
1083
|
const head = headlineOf(data, pressure);
|
|
978
|
-
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("overview.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""))), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), data.toolList && data.toolList.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, t("tools.top"), data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
|
|
1084
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root", ref: rootRef }, /* @__PURE__ */ React.createElement("div", { className: "lc-cols lc-head" }, /* @__PURE__ */ React.createElement(StatsBoard, { requests, events }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("overview.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, (data.model ? data.model : "") + (data.provider ? " \xB7 " + data.provider : ""))), /* @__PURE__ */ React.createElement("div", { className: "lc-overview-num" }, /* @__PURE__ */ React.createElement("b", null, fmt3(head.tokens)), /* @__PURE__ */ React.createElement("span", null, head.window ? " / " + fmt3(head.window) + " " + tr("overview.ofWindow", { p: head.pct ?? 0 }) : " " + t("overview.estimate")), !head.estimated ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("overview.splitEst")) : null), /* @__PURE__ */ React.createElement(StackedBar, { parts: head.parts, height: 16, max: head.window, hoverKey: hoverCat, onHoverKey: setHoverCat }), /* @__PURE__ */ React.createElement(Legend, { parts: head.parts, hoverKey: hoverCat, onHoverKey: setHoverCat }), data.toolList && data.toolList.length > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-tools" }, t("tools.top"), data.toolList.slice().sort((a, b) => b.tokens - a.tokens).slice(0, 5).map((tool) => {
|
|
979
1085
|
return /* @__PURE__ */ React.createElement("span", { key: tool.name, className: "lc-tool-chip" }, tool.name + " " + fmt3(tool.tokens));
|
|
980
1086
|
}), data.toolList.length > 5 ? /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, " " + tr("tools.more", { n: data.toolList.length })) : null) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("trend.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement(
|
|
981
1087
|
"button",
|
|
@@ -1009,7 +1115,17 @@ function makeContextView(ctx, kit) {
|
|
|
1009
1115
|
onHover: setHoveredSeq,
|
|
1010
1116
|
onHoverTurn: setHoverTurn
|
|
1011
1117
|
}
|
|
1012
|
-
), /* @__PURE__ */ React.createElement(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("events.title")
|
|
1118
|
+
), /* @__PURE__ */ React.createElement(RequestDetail, { request: activeReq, marker: activeReq !== null ? markerOf(activeReq) : void 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("events.title"), /* @__PURE__ */ React.createElement("div", { className: "lc-kinds" }, EVENT_KINDS.map((k) => /* @__PURE__ */ React.createElement(
|
|
1119
|
+
"button",
|
|
1120
|
+
{
|
|
1121
|
+
key: k,
|
|
1122
|
+
className: "lc-gran-btn" + (pickedKinds.includes(k) ? " lc-gran-on lc-kind-" + k : ""),
|
|
1123
|
+
onClick: () => {
|
|
1124
|
+
toggleKind(k);
|
|
1125
|
+
}
|
|
1126
|
+
},
|
|
1127
|
+
t("kind." + k)
|
|
1128
|
+
)))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, t("nodes.title"), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("nodes.hint"))), /* @__PURE__ */ React.createElement(NodeList, { nodes, dropped: data.droppedNodes || 0 }))), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
|
|
1013
1129
|
};
|
|
1014
1130
|
}
|
|
1015
1131
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A DeepSeek Harness plugin for context dashboard and context command, for understanding how the context is made of, and how it evolves.",
|
|
3
|
+
"version": "0.11.0",
|
|
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": {
|
|
7
7
|
"type": "git",
|