dsh-context 0.20.1 → 0.21.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/lib/client.js +107 -9
- package/lib/index.js +42 -6
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -67,6 +67,7 @@ var DICT_ZH = {
|
|
|
67
67
|
"nodes.empty": "\u5F53\u524D\u6CA1\u6709\u6A21\u578B\u53EF\u89C1\u7684\u6D88\u606F",
|
|
68
68
|
"loading": "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u65E5\u5FD7\u2026",
|
|
69
69
|
"error": "\u4E0A\u4E0B\u6587\u6570\u636E\u8BFB\u53D6\u5931\u8D25\uFF1A",
|
|
70
|
+
"error.retry": "\u91CD\u8BD5",
|
|
70
71
|
"footer": "\u4F30\u7B97\u53E3\u5F84\uFF1A\u4E0E dsh \u5185\u7F6E tokenMeter \u76F8\u540C\u7684\u56FA\u5B9A\u5BC6\u5EA6\u542F\u53D1\u5F0F\uFF08\u7EA6 4 \u5B57\u7B26 \u2248 1 token\uFF09\uFF1B\u300C\u5B9E\u9645\u300D\u4E3A\u4F9B\u5E94\u5546\u4E0A\u62A5\u7528\u91CF\u3002",
|
|
71
72
|
"tip.step": "\u7B2C {t} \u8F6E \xB7 \u7B2C{s}\u6B65",
|
|
72
73
|
"tip.turn": "\u7B2C {t} \u8F6E \xB7 \u5171 {n} \u6B65",
|
|
@@ -177,6 +178,7 @@ var DICT_EN = {
|
|
|
177
178
|
"nodes.empty": "No model-visible messages right now",
|
|
178
179
|
"loading": "Reading the session log\u2026",
|
|
179
180
|
"error": "Failed to read context data: ",
|
|
181
|
+
"error.retry": "Retry",
|
|
180
182
|
"footer": "Estimate: same fixed-density heuristic as dsh\u2019s built-in tokenMeter (~4 chars \u2248 1 token); \u201Cactual\u201D is provider-reported usage.",
|
|
181
183
|
"tip.step": "Turn {t} \xB7 Step {s}",
|
|
182
184
|
"tip.turn": "Turn {t} \xB7 {n} steps",
|
|
@@ -338,12 +340,60 @@ function asRecord(value) {
|
|
|
338
340
|
if (value === null || value === void 0 || typeof value !== "object") return null;
|
|
339
341
|
return value;
|
|
340
342
|
}
|
|
341
|
-
|
|
343
|
+
function numOf(value) {
|
|
344
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
345
|
+
}
|
|
346
|
+
function objectsOf(value) {
|
|
347
|
+
if (!Array.isArray(value)) return [];
|
|
348
|
+
return value.filter((v) => v !== null && typeof v === "object");
|
|
349
|
+
}
|
|
350
|
+
function timelineOf(value) {
|
|
351
|
+
const data = asRecord(value);
|
|
352
|
+
if (data === null) return null;
|
|
353
|
+
const current = data.current;
|
|
354
|
+
const numericBreakdown = current !== null && typeof current === "object" && ["system", "tools", "user", "inject", "assistant", "tool", "total"].every((k) => typeof current[k] === "number");
|
|
355
|
+
if (numericBreakdown && Array.isArray(data.requests) && Array.isArray(data.events) && Array.isArray(data.nodes) && Array.isArray(data.archive) && Array.isArray(data.toolList)) {
|
|
356
|
+
return data;
|
|
357
|
+
}
|
|
358
|
+
const safeCurrent = current !== null && typeof current === "object" ? current : {};
|
|
359
|
+
const cost = typeof data.cost === "object" && data.cost !== null && !Array.isArray(data.cost) ? data.cost : void 0;
|
|
360
|
+
return {
|
|
361
|
+
ok: true,
|
|
362
|
+
...typeof data.model === "string" ? { model: data.model } : {},
|
|
363
|
+
...typeof data.provider === "string" ? { provider: data.provider } : {},
|
|
364
|
+
...typeof data.contextWindow === "number" ? { contextWindow: data.contextWindow } : {},
|
|
365
|
+
current: {
|
|
366
|
+
system: numOf(safeCurrent.system),
|
|
367
|
+
tools: numOf(safeCurrent.tools),
|
|
368
|
+
user: numOf(safeCurrent.user),
|
|
369
|
+
inject: numOf(safeCurrent.inject),
|
|
370
|
+
assistant: numOf(safeCurrent.assistant),
|
|
371
|
+
tool: numOf(safeCurrent.tool),
|
|
372
|
+
total: numOf(safeCurrent.total)
|
|
373
|
+
},
|
|
374
|
+
toolList: objectsOf(data.toolList),
|
|
375
|
+
requests: objectsOf(data.requests),
|
|
376
|
+
events: objectsOf(data.events),
|
|
377
|
+
nodes: objectsOf(data.nodes),
|
|
378
|
+
droppedNodes: numOf(data.droppedNodes),
|
|
379
|
+
archive: objectsOf(data.archive),
|
|
380
|
+
...cost !== void 0 ? { cost } : {},
|
|
381
|
+
...typeof data.surfaceFloor === "number" ? { surfaceFloor: data.surfaceFloor } : {},
|
|
382
|
+
...typeof data.archiveFloor === "number" ? { archiveFloor: data.archiveFloor } : {}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
342
385
|
var contextPressureOf = (value) => asRecord(value);
|
|
343
386
|
var tokenUsageOf = (value) => asRecord(value);
|
|
344
387
|
function headersOf(value) {
|
|
345
388
|
const headers = asRecord(value);
|
|
346
|
-
|
|
389
|
+
if (headers === null || !Array.isArray(headers.headers)) return null;
|
|
390
|
+
for (const h2 of headers.headers) {
|
|
391
|
+
if (h2 === null || typeof h2 !== "object") return null;
|
|
392
|
+
const entry = h2;
|
|
393
|
+
if (!Array.isArray(entry.tools)) return null;
|
|
394
|
+
if (entry.system !== void 0 && typeof entry.system !== "string") return null;
|
|
395
|
+
}
|
|
396
|
+
return headers;
|
|
347
397
|
}
|
|
348
398
|
|
|
349
399
|
// src/client/assemble.ts
|
|
@@ -982,6 +1032,34 @@ function makeCurrentComposition(kit, StackedBar, Legend) {
|
|
|
982
1032
|
};
|
|
983
1033
|
}
|
|
984
1034
|
|
|
1035
|
+
// src/client/components/errorBoundary.tsx
|
|
1036
|
+
function makeErrorBoundary(t) {
|
|
1037
|
+
return class ErrorBoundary extends React.Component {
|
|
1038
|
+
constructor(props) {
|
|
1039
|
+
super(props);
|
|
1040
|
+
this.state = { error: null };
|
|
1041
|
+
}
|
|
1042
|
+
static getDerivedStateFromError(error) {
|
|
1043
|
+
return { error: error instanceof Error ? error : new Error(String(error)) };
|
|
1044
|
+
}
|
|
1045
|
+
render() {
|
|
1046
|
+
const error = this.state.error;
|
|
1047
|
+
if (error === null) return this.props.children;
|
|
1048
|
+
return /* @__PURE__ */ React.createElement("div", { className: "lc-root" }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-error" }, /* @__PURE__ */ React.createElement("span", null, t("error")), /* @__PURE__ */ React.createElement("code", { className: "lc-error-msg" }, error.message), /* @__PURE__ */ React.createElement(
|
|
1049
|
+
"button",
|
|
1050
|
+
{
|
|
1051
|
+
type: "button",
|
|
1052
|
+
className: "lc-error-retry",
|
|
1053
|
+
onClick: () => {
|
|
1054
|
+
this.setState({ error: null });
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
t("error.retry")
|
|
1058
|
+
)));
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
985
1063
|
// src/client/components/contextModal.tsx
|
|
986
1064
|
function makeContextModal(ctx, kit) {
|
|
987
1065
|
const { t } = kit;
|
|
@@ -990,7 +1068,8 @@ function makeContextModal(ctx, kit) {
|
|
|
990
1068
|
const Legend = makeLegend(kit);
|
|
991
1069
|
const CurrentComposition = makeCurrentComposition(kit, StackedBar, Legend);
|
|
992
1070
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
993
|
-
|
|
1071
|
+
const ErrorBoundary = makeErrorBoundary(t);
|
|
1072
|
+
function ContextModalBody(props) {
|
|
994
1073
|
const sessionId = typeof props.sessionId === "string" ? props.sessionId : "";
|
|
995
1074
|
const open = typeof props.useContextModal === "function" ? props.useContextModal((s) => s) : false;
|
|
996
1075
|
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
@@ -1051,6 +1130,9 @@ function makeContextModal(ctx, kit) {
|
|
|
1051
1130
|
}
|
|
1052
1131
|
}
|
|
1053
1132
|
))));
|
|
1133
|
+
}
|
|
1134
|
+
return function ContextModal(props) {
|
|
1135
|
+
return h(ErrorBoundary, null, h(ContextModalBody, props));
|
|
1054
1136
|
};
|
|
1055
1137
|
}
|
|
1056
1138
|
|
|
@@ -1273,6 +1355,13 @@ var STYLES = [
|
|
|
1273
1355
|
".lc-node-tokens { color: var(--dsw-alias-label-secondary); }",
|
|
1274
1356
|
".lc-nodes-more { color: var(--dsw-alias-label-secondary); padding: 3px 0; }",
|
|
1275
1357
|
".lc-empty { color: var(--dsw-alias-label-secondary); padding: 18px 0; text-align: center; }",
|
|
1358
|
+
// Error-boundary fallback card: the tab degrades to this instead of
|
|
1359
|
+
// white-screening; the message runs in mono on a padded chip, and Retry
|
|
1360
|
+
// re-mounts the subtree once a healthy value arrives.
|
|
1361
|
+
".lc-error { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 40px 16px; }",
|
|
1362
|
+
".lc-error-msg { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--dsw-alias-state-error-primary); background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; padding: 4px 8px; max-width: 100%; overflow-wrap: anywhere; }",
|
|
1363
|
+
".lc-error-retry { border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; padding: 4px 14px; border-radius: 6px; cursor: pointer; }",
|
|
1364
|
+
".lc-error-retry:hover { border-color: var(--dsw-alias-label-primary); }",
|
|
1276
1365
|
".lc-foot { color: var(--dsw-alias-label-secondary); font-size: 12px; margin-top: 4px; }",
|
|
1277
1366
|
// ---- /context modal (centered dialog; escapes the composer anchor via fixed positioning) ----
|
|
1278
1367
|
".lc-modal-backdrop { position: fixed; inset: 0; z-index: 200; background: rgba(0, 0, 0, 0.45); display: flex; align-items: center; justify-content: center; }",
|
|
@@ -1406,7 +1495,9 @@ function fmt(n) {
|
|
|
1406
1495
|
return String(Math.round(n));
|
|
1407
1496
|
}
|
|
1408
1497
|
function fmtTime(t) {
|
|
1409
|
-
|
|
1498
|
+
const d = new Date(t);
|
|
1499
|
+
if (isNaN(d.getTime())) return "\u2014";
|
|
1500
|
+
return d.toLocaleTimeString("en-GB", { hour12: false });
|
|
1410
1501
|
}
|
|
1411
1502
|
|
|
1412
1503
|
// src/client/components/events.tsx
|
|
@@ -1472,7 +1563,7 @@ function makeEventList(kit) {
|
|
|
1472
1563
|
|
|
1473
1564
|
// src/client/meta.ts
|
|
1474
1565
|
var PLUGIN_NAME = "dsh-context";
|
|
1475
|
-
var PLUGIN_VERSION = true ? "0.
|
|
1566
|
+
var PLUGIN_VERSION = true ? "0.21.1" : "0.0.0-dev";
|
|
1476
1567
|
var PLUGIN_REPO = true ? "https://github.com/bowenliang123/dsh-context" : "https://github.com/bowenliang123/dsh-context";
|
|
1477
1568
|
var PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
|
|
1478
1569
|
|
|
@@ -1563,7 +1654,7 @@ function estimateSessionCost(usage, currency) {
|
|
|
1563
1654
|
const b = fam[period];
|
|
1564
1655
|
if (b === void 0) continue;
|
|
1565
1656
|
const p = PRICES[currency][family][period];
|
|
1566
|
-
total += (b.cacheRead * p.hit + (b.uncached + b.cacheWrite) * p.miss + b.output * p.out) / 1e6;
|
|
1657
|
+
total += (numOf(b.cacheRead) * p.hit + (numOf(b.uncached) + numOf(b.cacheWrite)) * p.miss + numOf(b.output) * p.out) / 1e6;
|
|
1567
1658
|
any = true;
|
|
1568
1659
|
}
|
|
1569
1660
|
}
|
|
@@ -1586,9 +1677,12 @@ function formatPriceRate(amount, currency) {
|
|
|
1586
1677
|
|
|
1587
1678
|
// src/client/components/statsBoard.tsx
|
|
1588
1679
|
function cacheHitPercent(usage) {
|
|
1589
|
-
const
|
|
1680
|
+
const uncached = numOf(usage.uncachedInputTokens);
|
|
1681
|
+
const reads = numOf(usage.cacheReadTokens);
|
|
1682
|
+
const writes = numOf(usage.cacheWriteTokens);
|
|
1683
|
+
const billed = uncached + reads + writes;
|
|
1590
1684
|
if (billed === 0) return null;
|
|
1591
|
-
const hundredths = Math.trunc(
|
|
1685
|
+
const hundredths = Math.trunc(reads / billed * 1e4 + 1e-9);
|
|
1592
1686
|
return `${Math.floor(hundredths / 100)}.${String(hundredths % 100).padStart(2, "0")}`;
|
|
1593
1687
|
}
|
|
1594
1688
|
function makeStatsBoard(kit) {
|
|
@@ -1811,7 +1905,8 @@ function makeContextView(ctx, kit) {
|
|
|
1811
1905
|
const StatsBoard = makeStatsBoard(kit);
|
|
1812
1906
|
const PluginInfo = makePluginInfo(kit);
|
|
1813
1907
|
const ContextBrowser = makeContextBrowser(kit, StackedBar);
|
|
1814
|
-
|
|
1908
|
+
const ErrorBoundary = makeErrorBoundary(t);
|
|
1909
|
+
function ContextViewBody(props) {
|
|
1815
1910
|
const sessionId = props.sessionId;
|
|
1816
1911
|
const data = typeof props.useProjection === "function" ? timelineOf(props.useProjection("contextTimeline")) : null;
|
|
1817
1912
|
const pressure = typeof props.useProjection === "function" ? contextPressureOf(props.useProjection("contextPressure")) : null;
|
|
@@ -1959,6 +2054,9 @@ function makeContextView(ctx, kit) {
|
|
|
1959
2054
|
},
|
|
1960
2055
|
t("kind." + k)
|
|
1961
2056
|
)))), /* @__PURE__ */ React.createElement(EventList, { events: shownEvents })), /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-col" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, 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")));
|
|
2057
|
+
}
|
|
2058
|
+
return function ContextView(props) {
|
|
2059
|
+
return h(ErrorBoundary, null, h(ContextViewBody, props));
|
|
1962
2060
|
};
|
|
1963
2061
|
}
|
|
1964
2062
|
|
package/lib/index.js
CHANGED
|
@@ -103,6 +103,7 @@ var contextHeadersSchema = z2.object({
|
|
|
103
103
|
tools: z2.array(headerToolSchema)
|
|
104
104
|
}).strict())
|
|
105
105
|
}).strict();
|
|
106
|
+
var contextHeadersStateSchema = contextHeadersSchema;
|
|
106
107
|
function recordOf(event) {
|
|
107
108
|
if (event.type !== "request/header") return null;
|
|
108
109
|
const header = event.data.header;
|
|
@@ -130,9 +131,17 @@ function recordOf(event) {
|
|
|
130
131
|
return record;
|
|
131
132
|
}
|
|
132
133
|
function createContextHeadersDefinition() {
|
|
133
|
-
|
|
134
|
+
const view = (state) => ({
|
|
135
|
+
headers: state.headers.map((h) => ({ ...h, tools: h.tools.map((t) => ({ ...t })) }))
|
|
136
|
+
});
|
|
137
|
+
const definition = {
|
|
134
138
|
key: "contextHeaders",
|
|
139
|
+
// dsh <= 0.1.0-rc.8 contract: one schema validates the wire payload, `view` is top-level.
|
|
135
140
|
schema: contextHeadersSchema,
|
|
141
|
+
view,
|
|
142
|
+
// dsh >= 0.1.1-rc.1 contract: `stateSchema` validates persisted state, the client view lives in `wire`.
|
|
143
|
+
stateSchema: contextHeadersStateSchema,
|
|
144
|
+
wire: { viewSchema: contextHeadersSchema, view },
|
|
136
145
|
init: () => ({ headers: [] }),
|
|
137
146
|
apply: (state, event) => {
|
|
138
147
|
const record = recordOf(event);
|
|
@@ -142,11 +151,9 @@ function createContextHeadersDefinition() {
|
|
|
142
151
|
const headers = [...state.headers, record];
|
|
143
152
|
return { headers: headers.length > HEADERS_MAX ? headers.slice(-HEADERS_MAX) : headers };
|
|
144
153
|
},
|
|
145
|
-
view: (state) => ({
|
|
146
|
-
headers: state.headers.map((h) => ({ ...h, tools: h.tools.map((t) => ({ ...t })) }))
|
|
147
|
-
}),
|
|
148
154
|
stateVersion: 1
|
|
149
155
|
};
|
|
156
|
+
return definition;
|
|
150
157
|
}
|
|
151
158
|
|
|
152
159
|
// src/host/timeline.ts
|
|
@@ -616,14 +623,42 @@ var contextTimelineSchema = z3.object({
|
|
|
616
623
|
surfaceFloor: z3.number().int().nonnegative().optional(),
|
|
617
624
|
archiveFloor: z3.number().int().nonnegative().optional()
|
|
618
625
|
}).strict();
|
|
626
|
+
var timelineStateSchema = z3.object({
|
|
627
|
+
surface: z3.array(surfaceNodeSchema),
|
|
628
|
+
sums: z3.object({
|
|
629
|
+
user: z3.number().int().nonnegative(),
|
|
630
|
+
inject: z3.number().int().nonnegative(),
|
|
631
|
+
assistant: z3.number().int().nonnegative(),
|
|
632
|
+
tool: z3.number().int().nonnegative()
|
|
633
|
+
}).strict(),
|
|
634
|
+
systemTokens: z3.number().int().nonnegative(),
|
|
635
|
+
toolsTokens: z3.number().int().nonnegative(),
|
|
636
|
+
toolList: z3.array(z3.object({ name: z3.string(), tokens: z3.number().int().nonnegative() }).strict()),
|
|
637
|
+
model: z3.string().optional(),
|
|
638
|
+
provider: z3.string().optional(),
|
|
639
|
+
lastModel: z3.string().optional(),
|
|
640
|
+
contextWindow: z3.number().optional(),
|
|
641
|
+
requests: z3.array(requestRecordSchema),
|
|
642
|
+
events: z3.array(contextEventSchema),
|
|
643
|
+
archived: z3.array(surfaceNodeSchema),
|
|
644
|
+
cost: z3.object({ flash: costFamilySchema.optional(), pro: costFamilySchema.optional() }).strict().optional(),
|
|
645
|
+
archiveFloor: z3.number().optional(),
|
|
646
|
+
callNames: z3.record(z3.string(), z3.string()),
|
|
647
|
+
pendingShadowedSeqs: z3.array(z3.number()).optional()
|
|
648
|
+
});
|
|
619
649
|
function createContextTimelineDefinition(config) {
|
|
620
650
|
const bounds = resolveBounds(config);
|
|
621
|
-
|
|
651
|
+
const view = (state) => buildTimelineView(state, bounds);
|
|
652
|
+
const definition = {
|
|
622
653
|
key: "contextTimeline",
|
|
654
|
+
// dsh <= 0.1.0-rc.8 contract: one schema validates the wire payload, `view` is top-level.
|
|
623
655
|
schema: contextTimelineSchema,
|
|
656
|
+
view,
|
|
657
|
+
// dsh >= 0.1.1-rc.1 contract: `stateSchema` validates persisted state, the client view lives in `wire`.
|
|
658
|
+
stateSchema: timelineStateSchema,
|
|
659
|
+
wire: { viewSchema: contextTimelineSchema, view },
|
|
624
660
|
init: () => createTimelineState(),
|
|
625
661
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
626
|
-
view: (state) => buildTimelineView(state, bounds),
|
|
627
662
|
// 2 since 0.11: the occupancy mirror (pressureTokens/sampledSurfaceTokens/
|
|
628
663
|
// occupancyWindow) left the persisted state — the client now reads the
|
|
629
664
|
// official token-meter `contextPressure` projection instead. Old cached
|
|
@@ -644,6 +679,7 @@ function createContextTimelineDefinition(config) {
|
|
|
644
679
|
// are refolded.
|
|
645
680
|
stateVersion: 5
|
|
646
681
|
};
|
|
682
|
+
return definition;
|
|
647
683
|
}
|
|
648
684
|
|
|
649
685
|
// src/host/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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": {
|