dsh-context 0.49.3 → 0.50.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 +3 -2
- package/lib/client.js +336 -144
- package/lib/index.d.ts +22 -7
- package/lib/index.js +113 -31
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Open any session and click the **Context / 上下文** tab:
|
|
|
47
47
|
|
|
48
48
|
| Card | The question it answers |
|
|
49
49
|
| --- | --- |
|
|
50
|
-
| **Context Stats** | Turns, steps, live tool calls
|
|
50
|
+
| **Context Stats** | Turns, steps, live tool calls, the session's cache-hit rate — plus a list-price cost estimate (hover the `?` for per-1M rates). |
|
|
51
51
|
| **Token Stats** | Where the billed tokens went: cache read/write, uncached input, output — around the cache-hit ring. |
|
|
52
52
|
| **Timing Stats** | How active time split across model calls, tool runs, and overhead. |
|
|
53
53
|
| **Current Context** | What's in the window *right now*. |
|
|
@@ -102,7 +102,7 @@ Pick **Live (next request)** or any retained step, and browse what that request
|
|
|
102
102
|
|
|
103
103
|

|
|
104
104
|
|
|
105
|
-
Every injection, compaction, prune, model switch, and plan-mode toggle — labeled with its producer (instruction file, plugin id, skill name), its net token delta (compactions show what they reclaimed), turn/step, and time. The **Inject / Compact / Prune / Switch / Mode** chips filter the log by kind.
|
|
105
|
+
Every injection, compaction, prune, model switch, and plan-mode toggle — labeled with its producer (instruction file, plugin id, skill name), its net token delta (compactions show what they reclaimed), turn/step, and time. The **Inject / Compact / Prune / Switch / Mode** chips filter the log by kind, each carrying its whole-session event tally.
|
|
106
106
|
|
|
107
107
|
### 📁 File Activity — what the agent did to your files
|
|
108
108
|
|
|
@@ -115,6 +115,7 @@ One row per touched file — read, written, or searched — aggregated up to whi
|
|
|
115
115
|
- **Every mode counts** — native tools, the Minimal preset's `str_replace_editor`, and the nested calls inside PTC `run_code` programs are all folded into per-tool rows.
|
|
116
116
|
- **Searches land on real files** — matched files get their own ops rows with hit counts.
|
|
117
117
|
- **Click a row** to expand its full operation log — every op jumps straight to the exact tool result in the Context browser.
|
|
118
|
+
- **Click a file name** to open its preview in the right Sidebar (dsh 0.1.5-rc.1+), exactly as the built-in Files sidebar does — the same viewer, the same tab-per-file behavior. On a harness without that column the name opens on your system as before.
|
|
118
119
|
|
|
119
120
|
### 🕸 Agent Network — the family portrait
|
|
120
121
|
|
package/lib/client.js
CHANGED
|
@@ -26,13 +26,12 @@ window.__ModuleLoader__.load({
|
|
|
26
26
|
"stats.title": "上下文统计",
|
|
27
27
|
"stats.turns": "轮次",
|
|
28
28
|
"stats.steps": "步数",
|
|
29
|
-
"stats.
|
|
30
|
-
"stats.
|
|
31
|
-
"stats.prunes": "剪枝",
|
|
29
|
+
"stats.humanInputs": "用户输入",
|
|
30
|
+
"stats.humanInputsTip": "当前会话中的用户消息与问题回答次数。",
|
|
32
31
|
"stats.toolCalls": "工具调用",
|
|
33
|
-
"stats.
|
|
32
|
+
"stats.cacheHit": "缓存命中",
|
|
34
33
|
"stats.cost": "预估费用",
|
|
35
|
-
"stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-
|
|
34
|
+
"stats.costTip": "按 DeepSeek 官方刊例价估算整个会话的累计费用:输入区分缓存命中/未命中,输出含思考;按请求时间区分高峰(北京时间工作日 9:00–12:00、14:00–18:00)与空闲时段(半价,周末全天均为空闲时段)。适用于 deepseek-flash / deepseek-v4-pro(旧 ID deepseek-v4-flash 等按 Flash 价格计费;按模型名匹配,不限 provider),价格仅供参考。",
|
|
36
35
|
"stats.costPriceHead": "每百万 tokens 价格(高峰 | 空闲半价):",
|
|
37
36
|
"stats.costHit": "命中",
|
|
38
37
|
"stats.costMiss": "未命中",
|
|
@@ -42,7 +41,7 @@ window.__ModuleLoader__.load({
|
|
|
42
41
|
"timing.ttft": "模型等待",
|
|
43
42
|
"timing.gen": "模型生成",
|
|
44
43
|
"timing.reasoning": "模型思考",
|
|
45
|
-
"timing.text": "
|
|
44
|
+
"timing.text": "模型输出",
|
|
46
45
|
"timing.toolArgs": "工具参数",
|
|
47
46
|
"timing.tools": "工具执行",
|
|
48
47
|
"timing.other": "其他开销",
|
|
@@ -50,10 +49,7 @@ window.__ModuleLoader__.load({
|
|
|
50
49
|
"timing.toolTimes": "{n}次",
|
|
51
50
|
"timing.empty": "暂无耗时数据 · 对话开始后这里会显示时长分布",
|
|
52
51
|
"tokens.title": "Token 统计",
|
|
53
|
-
"tokens.
|
|
54
|
-
"tokens.cacheRead": "缓存输入",
|
|
55
|
-
"tokens.cacheWrite": "缓存写入",
|
|
56
|
-
"tokens.uncached": "未缓存输入",
|
|
52
|
+
"tokens.total": "总用量",
|
|
57
53
|
"tokens.output": "输出",
|
|
58
54
|
"tokens.outputNote": "含思考",
|
|
59
55
|
"plugin.title": "插件信息",
|
|
@@ -132,6 +128,7 @@ window.__ModuleLoader__.load({
|
|
|
132
128
|
"files.hits": "{n} 处命中",
|
|
133
129
|
"files.readTip": "读取第 {a}–{b} 行",
|
|
134
130
|
"files.open": "在系统中打开",
|
|
131
|
+
"files.preview": "在侧边栏预览",
|
|
135
132
|
"files.readEst": "按调用参数 limit 估算的读取行数",
|
|
136
133
|
"files.errs": "{n} 次失败",
|
|
137
134
|
"files.form.image": "图片",
|
|
@@ -321,13 +318,12 @@ window.__ModuleLoader__.load({
|
|
|
321
318
|
"stats.title": "Context Stats",
|
|
322
319
|
"stats.turns": "Turns",
|
|
323
320
|
"stats.steps": "Steps",
|
|
324
|
-
"stats.
|
|
325
|
-
"stats.
|
|
326
|
-
"stats.prunes": "Prunes",
|
|
321
|
+
"stats.humanInputs": "Human Inputs",
|
|
322
|
+
"stats.humanInputsTip": "User messages and question answerings in current session.",
|
|
327
323
|
"stats.toolCalls": "Tool Calls",
|
|
328
|
-
"stats.
|
|
324
|
+
"stats.cacheHit": "Cache Hit",
|
|
329
325
|
"stats.cost": "Cost",
|
|
330
|
-
"stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-
|
|
326
|
+
"stats.costTip": "Rough cumulative cost of the whole session at DeepSeek’s list prices: input split by cache hit/miss, output includes reasoning; each request is priced at the peak (Beijing Time weekdays 09:00–12:00, 14:00–18:00) or half-price off-peak rate by its time — weekends bill at off-peak all day. Applies to deepseek-flash / deepseek-v4-pro (legacy IDs such as deepseek-v4-flash bill at the Flash rates; matched by model name, any provider); prices are for reference only.",
|
|
331
327
|
"stats.costPriceHead": "Per-1M-token rates (peak | off-peak at half price):",
|
|
332
328
|
"stats.costHit": "hit",
|
|
333
329
|
"stats.costMiss": "miss",
|
|
@@ -345,10 +341,7 @@ window.__ModuleLoader__.load({
|
|
|
345
341
|
"timing.toolTimes": "{n} runs",
|
|
346
342
|
"timing.empty": "No timing data yet — durations appear as the conversation runs",
|
|
347
343
|
"tokens.title": "Token Stats",
|
|
348
|
-
"tokens.
|
|
349
|
-
"tokens.cacheRead": "Cached Input",
|
|
350
|
-
"tokens.cacheWrite": "Cache Write",
|
|
351
|
-
"tokens.uncached": "Uncached Input",
|
|
344
|
+
"tokens.total": "Total",
|
|
352
345
|
"tokens.output": "Output",
|
|
353
346
|
"tokens.outputNote": "incl. reasoning",
|
|
354
347
|
"plugin.title": "Plugin Info",
|
|
@@ -427,6 +420,7 @@ window.__ModuleLoader__.load({
|
|
|
427
420
|
"files.hits": "{n} hits",
|
|
428
421
|
"files.readTip": "Read lines {a}–{b}",
|
|
429
422
|
"files.open": "Open on your system",
|
|
423
|
+
"files.preview": "Preview in the sidebar",
|
|
430
424
|
"files.readEst": "Lines read, estimated from the limit argument",
|
|
431
425
|
"files.errs": "{n} failed",
|
|
432
426
|
"files.form.image": "Image",
|
|
@@ -819,6 +813,30 @@ window.__ModuleLoader__.load({
|
|
|
819
813
|
value: Math.round(p.raw * scale)
|
|
820
814
|
}));
|
|
821
815
|
}
|
|
816
|
+
/**
|
|
817
|
+
* The Token card's billed split, by WHAT the tokens are rather than by how
|
|
818
|
+
* the provider cached them: the six composition categories share the
|
|
819
|
+
* provider-reported prompt-side total (uncached + cache read + cache write —
|
|
820
|
+
* the chat stats line's billed input) by the composition card's own
|
|
821
|
+
* estimated ratios, and the provider's exact output count closes the ring as
|
|
822
|
+
* the seventh part. Only the per-category split is estimated — every
|
|
823
|
+
* category's sum and the output figure are provider-reported, so the parts
|
|
824
|
+
* total equals the chat line's whole-session token count by construction. A
|
|
825
|
+
* zero/negative prompt total (or a hostile negative output) never invents a
|
|
826
|
+
* split: the prompt parts zero out / the output clamps at 0.
|
|
827
|
+
*/
|
|
828
|
+
function billedParts(current, breakdown, usage) {
|
|
829
|
+
const input = usage.uncachedInputTokens + usage.cacheReadTokens + usage.cacheWriteTokens;
|
|
830
|
+
const estimated = officialParts(current, breakdown);
|
|
831
|
+
return [...input > 0 ? anchoredParts(estimated, input) : estimated.map((p) => ({
|
|
832
|
+
...p,
|
|
833
|
+
value: 0
|
|
834
|
+
})), {
|
|
835
|
+
key: "output",
|
|
836
|
+
color: "#ec4899",
|
|
837
|
+
value: Math.max(0, usage.outputTokens)
|
|
838
|
+
}];
|
|
839
|
+
}
|
|
822
840
|
//#endregion
|
|
823
841
|
//#region src/client/headline.ts
|
|
824
842
|
function headlineOf(data, pressure = null, breakdown = null) {
|
|
@@ -1000,6 +1018,7 @@ window.__ModuleLoader__.load({
|
|
|
1000
1018
|
droppedNodes: numOf(data.droppedNodes),
|
|
1001
1019
|
...typeof data.images === "number" ? { images: data.images } : {},
|
|
1002
1020
|
...typeof data.toolCalls === "number" ? { toolCalls: data.toolCalls } : {},
|
|
1021
|
+
...typeof data.humanInputs === "number" ? { humanInputs: data.humanInputs } : {},
|
|
1003
1022
|
archive: objectsOf(data.archive),
|
|
1004
1023
|
...counts !== void 0 ? { counts } : {},
|
|
1005
1024
|
...last !== void 0 ? { last } : {},
|
|
@@ -1355,6 +1374,37 @@ window.__ModuleLoader__.load({
|
|
|
1355
1374
|
} catch {}
|
|
1356
1375
|
};
|
|
1357
1376
|
}
|
|
1377
|
+
/**
|
|
1378
|
+
* The right Sidebar's resource opener over `ctx.sidebarRight`, or undefined
|
|
1379
|
+
* when this harness serves no such column (every line older than 0.1.5-rc.1) or
|
|
1380
|
+
* the face is hostile — the caller then keeps its system-open degradation.
|
|
1381
|
+
* Synchronous, and it reports whether the column took the address: `openResource`
|
|
1382
|
+
* throws for a no-type-claims address or with no session surface mounted, and an
|
|
1383
|
+
* unwired preview must fall back rather than become an inert click. The face is
|
|
1384
|
+
* re-proved at call time (the service can land or be revoked across an HMR
|
|
1385
|
+
* reload), so the returned closure reads it per open.
|
|
1386
|
+
*/
|
|
1387
|
+
function openResourceVia(ctx) {
|
|
1388
|
+
const faceOf = () => {
|
|
1389
|
+
try {
|
|
1390
|
+
const face = asRecord(ctx.get("sidebarRight"));
|
|
1391
|
+
return face !== null && typeof face.openResource === "function" ? face : void 0;
|
|
1392
|
+
} catch {
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
if (faceOf() === void 0) return void 0;
|
|
1397
|
+
return (address) => {
|
|
1398
|
+
const face = faceOf();
|
|
1399
|
+
if (face === void 0) return false;
|
|
1400
|
+
try {
|
|
1401
|
+
face.openResource(address);
|
|
1402
|
+
return true;
|
|
1403
|
+
} catch {
|
|
1404
|
+
return false;
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
}
|
|
1358
1408
|
//#endregion
|
|
1359
1409
|
//#region src/client/historyPage.ts
|
|
1360
1410
|
/**
|
|
@@ -2463,15 +2513,17 @@ window.__ModuleLoader__.load({
|
|
|
2463
2513
|
}
|
|
2464
2514
|
/**
|
|
2465
2515
|
* Cache-hit share of billed prompt-side input (`reads` over `billed`),
|
|
2466
|
-
* TRUNCATED to
|
|
2467
|
-
* chat stats line's '缓存命中' figure and the stats board's cell
|
|
2468
|
-
* nothing was billed. The 1e-9 epsilon
|
|
2469
|
-
* token counts never sit that close to a
|
|
2516
|
+
* TRUNCATED to `decimals` places (cut, not round) — same formula as the
|
|
2517
|
+
* harness chat stats line's '缓存命中' figure and the stats board's cell
|
|
2518
|
+
* (which shows one decimal). Null when nothing was billed. The 1e-9 epsilon
|
|
2519
|
+
* absorbs only float noise (integer token counts never sit that close to a
|
|
2520
|
+
* boundary).
|
|
2470
2521
|
*/
|
|
2471
|
-
function cacheHitPercent(reads, billed) {
|
|
2522
|
+
function cacheHitPercent(reads, billed, decimals = 2) {
|
|
2472
2523
|
if (!(billed > 0)) return null;
|
|
2473
|
-
const
|
|
2474
|
-
|
|
2524
|
+
const factor = 10 ** decimals;
|
|
2525
|
+
const scaled = Math.trunc(reads / billed * 100 * factor + 1e-9);
|
|
2526
|
+
return `${Math.floor(scaled / factor)}.${String(scaled % factor).padStart(decimals, "0")}`;
|
|
2475
2527
|
}
|
|
2476
2528
|
function fmtTime(t) {
|
|
2477
2529
|
const d = new Date(t);
|
|
@@ -3892,7 +3944,9 @@ window.__ModuleLoader__.load({
|
|
|
3892
3944
|
//#region src/client/components/stackedBar.tsx
|
|
3893
3945
|
/**
|
|
3894
3946
|
* Composition bar + legend; the shared hover-link tooltip is bespoke — no shared primitive reproduces the cross-segment/legend linkage —
|
|
3895
|
-
* styled through the shared `--dsw-alias-*` tokens.
|
|
3947
|
+
* styled through the shared `--dsw-alias-*` tokens. On mount the segments and the free track grow open
|
|
3948
|
+
* left to right (stackedBar.css, staggered by the capped per-piece `--lc-i` slots below, so a long DNA
|
|
3949
|
+
* band list settles fast); the reserve band stays unanimated.
|
|
3896
3950
|
*/
|
|
3897
3951
|
/**
|
|
3898
3952
|
* Mirror of dsh-compaction-basic's default `thresholdRatio` (0.8): it compacts at step boundaries once `floor(contextWindow × ratio)` is
|
|
@@ -3900,6 +3954,12 @@ window.__ModuleLoader__.load({
|
|
|
3900
3954
|
* `thresholdRatio`/`modelPolicies` should adjust it to match.
|
|
3901
3955
|
*/
|
|
3902
3956
|
const AUTO_COMPACT_RATIO = .8;
|
|
3957
|
+
/**
|
|
3958
|
+
* Entrance stagger cap: the browser's DNA bands can list dozens of items, so the grow-in cascade stops
|
|
3959
|
+
* widening after this many slots and late bands simply join within the cap (stackedBar.css delays by
|
|
3960
|
+
* `--lc-i`); the composition bar's few category segments never reach it.
|
|
3961
|
+
*/
|
|
3962
|
+
const STAGGER_CAP = 8;
|
|
3903
3963
|
function makeStackedBar(kit) {
|
|
3904
3964
|
const { t, fmt, catLabel } = kit;
|
|
3905
3965
|
return function StackedBar(props) {
|
|
@@ -3981,7 +4041,8 @@ window.__ModuleLoader__.load({
|
|
|
3981
4041
|
className: "lc-stacked-seg" + (on ? " lc-stacked-seg-on" : "") + (pickKey !== void 0 ? " lc-stacked-seg-pick" : ""),
|
|
3982
4042
|
style: {
|
|
3983
4043
|
width: `${widths[i]}%`,
|
|
3984
|
-
backgroundColor: p.color
|
|
4044
|
+
backgroundColor: p.color,
|
|
4045
|
+
"--lc-i": Math.min(i, STAGGER_CAP)
|
|
3985
4046
|
},
|
|
3986
4047
|
onMouseEnter: () => {
|
|
3987
4048
|
if (props.onHoverKey !== void 0) props.onHoverKey(p.key);
|
|
@@ -3993,7 +4054,10 @@ window.__ModuleLoader__.load({
|
|
|
3993
4054
|
}),
|
|
3994
4055
|
free > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3995
4056
|
className: "lc-stacked-free" + (props.hoverKey === "free" ? " lc-stacked-free-on" : ""),
|
|
3996
|
-
style: {
|
|
4057
|
+
style: {
|
|
4058
|
+
width: `${free / scale * 100}%`,
|
|
4059
|
+
"--lc-i": Math.min(visible.length, 9)
|
|
4060
|
+
},
|
|
3997
4061
|
onMouseEnter: () => {
|
|
3998
4062
|
if (props.onHoverKey !== void 0) props.onHoverKey("free");
|
|
3999
4063
|
}
|
|
@@ -4647,6 +4711,68 @@ window.__ModuleLoader__.load({
|
|
|
4647
4711
|
};
|
|
4648
4712
|
}
|
|
4649
4713
|
//#endregion
|
|
4714
|
+
//#region node_modules/.pnpm/@deepseek-ai+dsh-util-workspace-path@0.1.5-rc.1_@deepseek-ai+cordis@4.0.2/node_modules/@deepseek-ai/dsh-util-workspace-path/lib/index.js
|
|
4715
|
+
/**
|
|
4716
|
+
* The `dsh-resource://file/…` address grammar: how a file is named across the
|
|
4717
|
+
* Sidebar and the resource model, built and parsed without touching a
|
|
4718
|
+
* filesystem.
|
|
4719
|
+
* @module
|
|
4720
|
+
*/
|
|
4721
|
+
/** The scheme and type every file address opens with. */
|
|
4722
|
+
const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
|
|
4723
|
+
/** Component-encode one id or path segment, keeping `:` literal for drive letters. */
|
|
4724
|
+
function encodeSegment(segment) {
|
|
4725
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
4726
|
+
}
|
|
4727
|
+
/** Encode a `/`-separated path segment by segment. */
|
|
4728
|
+
function encodePath(path) {
|
|
4729
|
+
return path.split("/").map(encodeSegment).join("/");
|
|
4730
|
+
}
|
|
4731
|
+
/**
|
|
4732
|
+
* Build the address of a file read through one Session.
|
|
4733
|
+
* @param sessionId - the Session whose Host workspace resolves the path.
|
|
4734
|
+
* @param path - absolute or workspace-relative path; backslashes are normalized to `/`, and leading `./` prefixes are dropped.
|
|
4735
|
+
* @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
|
|
4736
|
+
*/
|
|
4737
|
+
function sessionFileAddress(sessionId, path) {
|
|
4738
|
+
const normalized = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "");
|
|
4739
|
+
return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(normalized)}`;
|
|
4740
|
+
}
|
|
4741
|
+
/**
|
|
4742
|
+
* Browser-safe Workspace path and display helpers.
|
|
4743
|
+
* @module @deepseek-ai/dsh-util-workspace-path
|
|
4744
|
+
*/
|
|
4745
|
+
/** Whether a path uses a Windows drive or UNC prefix. */
|
|
4746
|
+
function isWindowsStylePath(value) {
|
|
4747
|
+
return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
|
|
4748
|
+
}
|
|
4749
|
+
/**
|
|
4750
|
+
* Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
|
|
4751
|
+
* @param path - the path to classify.
|
|
4752
|
+
* @returns `true` for an absolute path; `false` for a Workspace-relative one.
|
|
4753
|
+
*/
|
|
4754
|
+
function isAbsoluteWorkspacePath(path) {
|
|
4755
|
+
return path.startsWith("/") || isWindowsStylePath(path);
|
|
4756
|
+
}
|
|
4757
|
+
/**
|
|
4758
|
+
* The address for a path as a caller holds it: a relative path, or an absolute
|
|
4759
|
+
* path inside the Session's workspace, becomes a `session`-scoped address; an
|
|
4760
|
+
* absolute path outside it, or one whose workspace root is unknown, keeps its
|
|
4761
|
+
* absolute path in that Session's address.
|
|
4762
|
+
* @param sessionId - the Session the path is read in.
|
|
4763
|
+
* @param cwd - that Session's workspace root, when known.
|
|
4764
|
+
* @param path - absolute or workspace-relative path, in either separator spelling.
|
|
4765
|
+
* @returns the `dsh-resource://file/…` address.
|
|
4766
|
+
*/
|
|
4767
|
+
function fileAddressFor(sessionId, cwd, path) {
|
|
4768
|
+
const normalized = path.replace(/\\/g, "/");
|
|
4769
|
+
if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
|
|
4770
|
+
const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
4771
|
+
if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
|
|
4772
|
+
if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
|
|
4773
|
+
return sessionFileAddress(sessionId, normalized);
|
|
4774
|
+
}
|
|
4775
|
+
//#endregion
|
|
4650
4776
|
//#region src/client/fileActivity.ts
|
|
4651
4777
|
/** The file's form — multimodal reads and image extensions scan apart; a trailing slash marks a directory target. */
|
|
4652
4778
|
function formOf(tool, path) {
|
|
@@ -5138,6 +5264,23 @@ window.__ModuleLoader__.load({
|
|
|
5138
5264
|
return workspace.replace(/\/+$/, "") + "/" + path.replace(/^\/+/, "");
|
|
5139
5265
|
}
|
|
5140
5266
|
/**
|
|
5267
|
+
* The right-Sidebar preview address of a real file — the session-scoped
|
|
5268
|
+
* `dsh-resource://file/…` address the shipped preview type claims, built by
|
|
5269
|
+
* the harness's own `fileAddressFor` (the files sidebar's idiom: a path under
|
|
5270
|
+
* the workspace collapses to its session-relative spelling, so every route to
|
|
5271
|
+
* one file settles on one tab). Undefined for a pathless search's PATTERN, a
|
|
5272
|
+
* directory target, a missing session, and any path the encoder rejects — the
|
|
5273
|
+
* caller falls back to the system opener or renders the name inert.
|
|
5274
|
+
*/
|
|
5275
|
+
function previewAddressOf(path, form, pattern, sessionId, workspace) {
|
|
5276
|
+
if (pattern === true || form === "dir" || sessionId === void 0 || sessionId === "") return void 0;
|
|
5277
|
+
try {
|
|
5278
|
+
return fileAddressFor(sessionId, workspace, path);
|
|
5279
|
+
} catch {
|
|
5280
|
+
return;
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
/**
|
|
5141
5284
|
* The row's display form of a path: inside the workspace (or already
|
|
5142
5285
|
* workspace-relative) it shortens to a './'-prefixed relative — an absolute
|
|
5143
5286
|
* path outside the workspace, a Windows drive path, and an already-'.'
|
|
@@ -6090,7 +6233,9 @@ window.__ModuleLoader__.load({
|
|
|
6090
6233
|
* with no value are skipped; an all-zero ring renders as one neutral track so
|
|
6091
6234
|
* the card never draws a misleading "100% of nothing" pie. The center type
|
|
6092
6235
|
* scales with the ring's size, so the label always fits inside the hole the
|
|
6093
|
-
* thin stroke leaves.
|
|
6236
|
+
* thin stroke leaves. On mount the slices sweep in one after another, growing
|
|
6237
|
+
* their dasharcs clockwise from 12 o'clock (stats.css, staggered by the
|
|
6238
|
+
* per-slice `--lc-i` slot below).
|
|
6094
6239
|
*/
|
|
6095
6240
|
/**
|
|
6096
6241
|
* The hairline divider between two slices, in dasharray units (1 unit ≈ 1% of
|
|
@@ -6143,7 +6288,7 @@ window.__ModuleLoader__.load({
|
|
|
6143
6288
|
r: "15.9155",
|
|
6144
6289
|
fill: "none",
|
|
6145
6290
|
strokeWidth: "4"
|
|
6146
|
-
}) : arcs.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
6291
|
+
}) : arcs.map((a, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
6147
6292
|
className: "lc-donut-seg" + (props.hoverKey === a.key ? " lc-donut-seg-on" : ""),
|
|
6148
6293
|
cx: "21",
|
|
6149
6294
|
cy: "21",
|
|
@@ -6153,6 +6298,7 @@ window.__ModuleLoader__.load({
|
|
|
6153
6298
|
strokeWidth: "4",
|
|
6154
6299
|
strokeDasharray: `${a.len} ${100 - a.len}`,
|
|
6155
6300
|
strokeDashoffset: a.offset,
|
|
6301
|
+
style: { "--lc-i": i },
|
|
6156
6302
|
onMouseEnter: () => {
|
|
6157
6303
|
if (props.onHoverKey !== void 0) props.onHoverKey(a.key);
|
|
6158
6304
|
}
|
|
@@ -6329,6 +6475,9 @@ window.__ModuleLoader__.load({
|
|
|
6329
6475
|
* Interaction mirrors the Context browser's element rows: a row click
|
|
6330
6476
|
* expands the file's own operation log; each operation is itself the click
|
|
6331
6477
|
* target that jumps to (and reveals) the exact tool result in the browser.
|
|
6478
|
+
* The file name opens the file's right-Sidebar preview where that column
|
|
6479
|
+
* exists (dsh 0.1.5-rc.1+, the shipped files-sidebar idiom), and the system
|
|
6480
|
+
* opener where it does not.
|
|
6332
6481
|
*/
|
|
6333
6482
|
function makeFileCard(kit, settings) {
|
|
6334
6483
|
const { t, fmt, fmtTime } = kit;
|
|
@@ -6523,6 +6672,8 @@ window.__ModuleLoader__.load({
|
|
|
6523
6672
|
const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
|
|
6524
6673
|
const glyph = glyphOf(e.path, e.form);
|
|
6525
6674
|
const abs = e.pattern === true ? void 0 : absPathOf(e.path, props.workspace);
|
|
6675
|
+
const previewable = props.onPreview !== void 0 && e.pattern !== true && e.form !== "dir";
|
|
6676
|
+
const openable = previewable || abs !== void 0 && props.onOpen !== void 0;
|
|
6526
6677
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6527
6678
|
className: "lc-fa-item" + (open ? " lc-fa-item-on" : ""),
|
|
6528
6679
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
@@ -6548,12 +6699,13 @@ window.__ModuleLoader__.load({
|
|
|
6548
6699
|
}),
|
|
6549
6700
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6550
6701
|
className: "lc-fa-path",
|
|
6551
|
-
children: [dir !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: dir }) : null,
|
|
6702
|
+
children: [dir !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("em", { children: dir }) : null, openable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
|
|
6552
6703
|
className: "lc-fa-file",
|
|
6553
|
-
title: t("files.open"),
|
|
6704
|
+
title: t(previewable ? "files.preview" : "files.open"),
|
|
6554
6705
|
onClick: (ev) => {
|
|
6555
6706
|
ev.stopPropagation();
|
|
6556
|
-
props.
|
|
6707
|
+
if (previewable && props.onPreview?.(e) === true) return;
|
|
6708
|
+
if (abs !== void 0) props.onOpen?.(abs);
|
|
6557
6709
|
},
|
|
6558
6710
|
children: base
|
|
6559
6711
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: base })]
|
|
@@ -6680,7 +6832,7 @@ window.__ModuleLoader__.load({
|
|
|
6680
6832
|
return function PluginInfo() {
|
|
6681
6833
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
6682
6834
|
(0, react.useEffect)(() => {
|
|
6683
|
-
if ("0.
|
|
6835
|
+
if ("0.50.0".includes("-dev")) return;
|
|
6684
6836
|
let on = true;
|
|
6685
6837
|
fetchLatestVersion().then((v) => {
|
|
6686
6838
|
if (on && v) setLatest(v);
|
|
@@ -6689,8 +6841,8 @@ window.__ModuleLoader__.load({
|
|
|
6689
6841
|
on = false;
|
|
6690
6842
|
};
|
|
6691
6843
|
}, []);
|
|
6692
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
6693
|
-
const nameText = "dsh-context (v0.
|
|
6844
|
+
const update = latest !== null && isNewerVersion(latest, "0.50.0") ? latest : null;
|
|
6845
|
+
const nameText = "dsh-context (v0.50.0)";
|
|
6694
6846
|
const nameValue = [nameText];
|
|
6695
6847
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6696
6848
|
className: "lc-pi-update",
|
|
@@ -7174,19 +7326,41 @@ window.__ModuleLoader__.load({
|
|
|
7174
7326
|
}
|
|
7175
7327
|
}
|
|
7176
7328
|
};
|
|
7177
|
-
/**
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7329
|
+
/** The Pro-series rates (deepseek-v4-pro; peak = 2× off-peak), per currency. */
|
|
7330
|
+
const PRO_RATES = {
|
|
7331
|
+
usd: {
|
|
7332
|
+
peak: {
|
|
7333
|
+
hit: .044,
|
|
7334
|
+
miss: 1.32,
|
|
7335
|
+
out: 3.96
|
|
7336
|
+
},
|
|
7337
|
+
off: {
|
|
7338
|
+
hit: .022,
|
|
7339
|
+
miss: .66,
|
|
7340
|
+
out: 1.98
|
|
7341
|
+
}
|
|
7342
|
+
},
|
|
7343
|
+
cny: {
|
|
7344
|
+
peak: {
|
|
7345
|
+
hit: .3,
|
|
7346
|
+
miss: 9,
|
|
7347
|
+
out: 27
|
|
7348
|
+
},
|
|
7349
|
+
off: {
|
|
7350
|
+
hit: .15,
|
|
7351
|
+
miss: 4.5,
|
|
7352
|
+
out: 13.5
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7355
|
+
};
|
|
7182
7356
|
const PRICES = {
|
|
7183
7357
|
usd: {
|
|
7184
7358
|
flash: FLASH_RATES.usd,
|
|
7185
|
-
pro:
|
|
7359
|
+
pro: PRO_RATES.usd
|
|
7186
7360
|
},
|
|
7187
7361
|
cny: {
|
|
7188
7362
|
flash: FLASH_RATES.cny,
|
|
7189
|
-
pro:
|
|
7363
|
+
pro: PRO_RATES.cny
|
|
7190
7364
|
}
|
|
7191
7365
|
};
|
|
7192
7366
|
/**
|
|
@@ -7224,7 +7398,7 @@ window.__ModuleLoader__.load({
|
|
|
7224
7398
|
*/
|
|
7225
7399
|
function sessionPrices(currency) {
|
|
7226
7400
|
return ["flash", "pro"].map((id) => ({
|
|
7227
|
-
family: id === "flash" ? "deepseek-
|
|
7401
|
+
family: id === "flash" ? "deepseek-flash / deepseek-v4-flash" : "deepseek-v4-pro",
|
|
7228
7402
|
peak: PRICES[currency][id].peak,
|
|
7229
7403
|
off: PRICES[currency][id].off
|
|
7230
7404
|
}));
|
|
@@ -7236,17 +7410,21 @@ window.__ModuleLoader__.load({
|
|
|
7236
7410
|
//#endregion
|
|
7237
7411
|
//#region src/client/components/statsContext.tsx
|
|
7238
7412
|
/**
|
|
7239
|
-
* The Context card: what the session's context IS and how it evolved —
|
|
7240
|
-
*
|
|
7241
|
-
* calls
|
|
7242
|
-
*
|
|
7243
|
-
* here is part of a spendable whole, so no pie —
|
|
7244
|
-
*
|
|
7245
|
-
*
|
|
7246
|
-
*
|
|
7247
|
-
*
|
|
7248
|
-
*
|
|
7249
|
-
*
|
|
7413
|
+
* The Context card: what the session's context IS and how it evolved — a
|
|
7414
|
+
* six-cell grid of the session's shape (turns / steps / human inputs / live
|
|
7415
|
+
* tool calls), the whole-session cache-hit rate, and the whole-session cost
|
|
7416
|
+
* estimate.
|
|
7417
|
+
* Count figures only: nothing here is part of a spendable whole, so no pie —
|
|
7418
|
+
* proportions live in the composition card, and the context-event tallies
|
|
7419
|
+
* live on the events card's kind filters (contextView.tsx). The cache-hit
|
|
7420
|
+
* cell reads the official `tokenUsage` projection — the same source and
|
|
7421
|
+
* formula as the harness chat stats line under the composer, shown with one
|
|
7422
|
+
* decimal — and dashes until a provider reports usage. The cost cell prices
|
|
7423
|
+
* the host-folded cumulative billed totals (complete session log, never
|
|
7424
|
+
* trimmed) at the hardcoded DeepSeek V4 list prices (cost.ts) in the locale's
|
|
7425
|
+
* currency; its hover bubble (a '?' marker + styled DOM tip) explains the
|
|
7426
|
+
* whole-session estimate and lists the per-1M-token table straight from
|
|
7427
|
+
* cost.ts, so printed rates can never drift from the math.
|
|
7250
7428
|
*
|
|
7251
7429
|
* The counts arrive precomputed: the split-generation wire head carries them
|
|
7252
7430
|
* (shared/types.ts `TimelineCounts` — computed over the retained records),
|
|
@@ -7315,6 +7493,7 @@ window.__ModuleLoader__.load({
|
|
|
7315
7493
|
]
|
|
7316
7494
|
}, r.family))]
|
|
7317
7495
|
}, "prices")];
|
|
7496
|
+
const hit = props.usage === null ? null : cacheHitPercent(numOf(props.usage.cacheReadTokens), numOf(props.usage.uncachedInputTokens) + numOf(props.usage.cacheReadTokens) + numOf(props.usage.cacheWriteTokens), 1);
|
|
7318
7497
|
const cell = (label, value, tip) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7319
7498
|
className: "lc-stat" + (tip === void 0 ? "" : " lc-stat-tipped"),
|
|
7320
7499
|
children: [
|
|
@@ -7350,12 +7529,10 @@ window.__ModuleLoader__.load({
|
|
|
7350
7529
|
children: [
|
|
7351
7530
|
cell(t("stats.turns"), props.counts.turns),
|
|
7352
7531
|
cell(t("stats.steps"), props.counts.steps),
|
|
7532
|
+
cell(t("stats.humanInputs"), props.humanInputs ?? 0, t("stats.humanInputsTip")),
|
|
7353
7533
|
cell(t("stats.toolCalls"), props.toolCalls ?? 0),
|
|
7354
|
-
cell(t("stats.
|
|
7355
|
-
cell(t("stats.cost"), cost === null ? "—" : formatCost(cost, currency), costTip)
|
|
7356
|
-
cell(t("stats.injects"), props.counts.injects),
|
|
7357
|
-
cell(t("stats.compactions"), props.counts.compactions),
|
|
7358
|
-
cell(t("stats.prunes"), props.counts.prunes)
|
|
7534
|
+
cell(t("stats.cacheHit"), hit === null ? "—" : `${hit}%`),
|
|
7535
|
+
cell(t("stats.cost"), cost === null ? "—" : formatCost(cost, currency), costTip)
|
|
7359
7536
|
]
|
|
7360
7537
|
})]
|
|
7361
7538
|
});
|
|
@@ -7585,59 +7762,39 @@ window.__ModuleLoader__.load({
|
|
|
7585
7762
|
//#endregion
|
|
7586
7763
|
//#region src/client/components/statsTokens.tsx
|
|
7587
7764
|
/**
|
|
7588
|
-
* The Token card: the
|
|
7589
|
-
*
|
|
7590
|
-
*
|
|
7591
|
-
*
|
|
7592
|
-
*
|
|
7593
|
-
*
|
|
7594
|
-
* the
|
|
7595
|
-
|
|
7765
|
+
* The Token card: the session's whole billed token usage — the SAME total
|
|
7766
|
+
* the harness chat stats line shows under the composer (uncached input +
|
|
7767
|
+
* cache read + cache write + output off the official `tokenUsage`
|
|
7768
|
+
* projection) — split by WHAT the tokens are, not by how the provider
|
|
7769
|
+
* cached them. The six composition categories share the provider-reported
|
|
7770
|
+
* prompt-side total by the composition card's own estimated ratios
|
|
7771
|
+
* (billedParts), and the provider's exact output count is its own slice, so
|
|
7772
|
+
* the ring and the center figure always equal the chat line's figure by
|
|
7773
|
+
* construction. The estimated category counts carry the ≈ marker (the
|
|
7774
|
+
* composition card's convention); the center total and the output count are
|
|
7775
|
+
* exact. Zero parts stay hidden once any usage is reported; the empty state
|
|
7776
|
+
* (no provider report yet) keeps all seven rows behind a dash center.
|
|
7777
|
+
*/
|
|
7778
|
+
const NO_USAGE = {
|
|
7779
|
+
uncachedInputTokens: 0,
|
|
7780
|
+
outputTokens: 0,
|
|
7781
|
+
cacheReadTokens: 0,
|
|
7782
|
+
cacheWriteTokens: 0
|
|
7783
|
+
};
|
|
7596
7784
|
function makeStatsTokens(kit, Donut) {
|
|
7597
|
-
const { t, fmt, fmtShare } = kit;
|
|
7785
|
+
const { t, fmt, fmtShare, catLabel } = kit;
|
|
7598
7786
|
const SliceList = makeSliceList(kit);
|
|
7599
7787
|
return function StatsTokens(props) {
|
|
7600
7788
|
const [hoverKey, setHoverKey] = (0, react.useState)(null);
|
|
7601
|
-
const
|
|
7602
|
-
const
|
|
7603
|
-
const
|
|
7604
|
-
const
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
color: "#22c55e",
|
|
7611
|
-
label: t("tokens.cacheRead"),
|
|
7612
|
-
value: reads
|
|
7613
|
-
},
|
|
7614
|
-
{
|
|
7615
|
-
key: "write",
|
|
7616
|
-
color: "#f59e0b",
|
|
7617
|
-
label: t("tokens.cacheWrite"),
|
|
7618
|
-
value: writes
|
|
7619
|
-
},
|
|
7620
|
-
{
|
|
7621
|
-
key: "uncached",
|
|
7622
|
-
color: "#a855f7",
|
|
7623
|
-
label: t("tokens.uncached"),
|
|
7624
|
-
value: uncached
|
|
7625
|
-
},
|
|
7626
|
-
{
|
|
7627
|
-
key: "output",
|
|
7628
|
-
color: "#3b82f6",
|
|
7629
|
-
label: t("tokens.output"),
|
|
7630
|
-
note: t("tokens.outputNote"),
|
|
7631
|
-
value: output
|
|
7632
|
-
}
|
|
7633
|
-
];
|
|
7634
|
-
const shown = billed > 0 ? buckets.filter((b) => b.value > 0) : buckets;
|
|
7635
|
-
const rows = shown.map((b) => ({
|
|
7636
|
-
key: b.key,
|
|
7637
|
-
color: b.color,
|
|
7638
|
-
label: b.label,
|
|
7639
|
-
pct: fmtShare(b.value, billed),
|
|
7640
|
-
count: b.note === void 0 ? fmt(b.value) : `${fmt(b.value)} · ${b.note}`
|
|
7789
|
+
const total = (props.usage !== null ? numOf(props.usage.uncachedInputTokens) + numOf(props.usage.cacheReadTokens) + numOf(props.usage.cacheWriteTokens) : 0) + (props.usage !== null ? numOf(props.usage.outputTokens) : 0);
|
|
7790
|
+
const parts = billedParts(props.current, props.breakdown, props.usage ?? NO_USAGE);
|
|
7791
|
+
const shown = total > 0 ? parts.filter((p) => p.value > 0) : parts;
|
|
7792
|
+
const rows = shown.map((p) => ({
|
|
7793
|
+
key: p.key,
|
|
7794
|
+
color: p.color,
|
|
7795
|
+
label: p.key === "output" ? t("tokens.output") : catLabel(p.key),
|
|
7796
|
+
pct: fmtShare(p.value, total),
|
|
7797
|
+
count: p.key === "output" ? `${fmt(p.value)} · ${t("tokens.outputNote")}` : "≈" + fmt(p.value)
|
|
7641
7798
|
}));
|
|
7642
7799
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7643
7800
|
className: "lc-card lc-col-stats lc-col-donut",
|
|
@@ -7650,14 +7807,10 @@ window.__ModuleLoader__.load({
|
|
|
7650
7807
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7651
7808
|
className: "lc-donut-row",
|
|
7652
7809
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Donut, {
|
|
7653
|
-
segments: shown
|
|
7654
|
-
key: b.key,
|
|
7655
|
-
color: b.color,
|
|
7656
|
-
value: b.value
|
|
7657
|
-
})),
|
|
7810
|
+
segments: shown,
|
|
7658
7811
|
size: 96,
|
|
7659
|
-
centerTop:
|
|
7660
|
-
centerSub: t("tokens.
|
|
7812
|
+
centerTop: props.usage === null ? "—" : fmt(total),
|
|
7813
|
+
centerSub: t("tokens.total"),
|
|
7661
7814
|
hoverKey,
|
|
7662
7815
|
onHoverKey: setHoverKey
|
|
7663
7816
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliceList, {
|
|
@@ -7673,7 +7826,8 @@ window.__ModuleLoader__.load({
|
|
|
7673
7826
|
//#region src/client/components/trendChart.tsx
|
|
7674
7827
|
/**
|
|
7675
7828
|
* Bespoke per-request history chart — no shared data-viz primitive — styled through the shared `--dsw-alias-*` tokens; helpers
|
|
7676
|
-
* aggregateByTurn/attachMarkers are shared with ContextView.
|
|
7829
|
+
* aggregateByTurn/attachMarkers are shared with ContextView. On mount each bar rises from its baseline,
|
|
7830
|
+
* staggered left to right with the cascade capped for long logs (trendChart.css, `--lc-i` slots below).
|
|
7677
7831
|
*/
|
|
7678
7832
|
/**
|
|
7679
7833
|
* Collapse per-step requests into one bar per turn — each turn is represented by its LAST step's record, tagged `stepCount` for the bar's
|
|
@@ -7730,6 +7884,7 @@ window.__ModuleLoader__.load({
|
|
|
7730
7884
|
const fmtSigned = (v) => (v > 0 ? "+" : "") + fmt(v);
|
|
7731
7885
|
const BAR_W = 14;
|
|
7732
7886
|
const BAR_GAP = 2;
|
|
7887
|
+
const STAGGER_CAP = 20;
|
|
7733
7888
|
const TURN_FILLS = ["rgba(128,128,128,0.12)", "rgba(128,128,128,0.26)"];
|
|
7734
7889
|
const LABEL_OVERHANG = 48;
|
|
7735
7890
|
const LABEL_GAP = 2;
|
|
@@ -7775,6 +7930,7 @@ window.__ModuleLoader__.load({
|
|
|
7775
7930
|
const { req, marker } = props;
|
|
7776
7931
|
const markerAt = marker !== void 0 ? eventAt(marker) : null;
|
|
7777
7932
|
const diverge = props.upPx !== void 0 && props.downPx !== void 0 && props.deltaScale !== void 0;
|
|
7933
|
+
const enterStyle = { "--lc-i": Math.min(props.enterIndex, STAGGER_CAP) };
|
|
7778
7934
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7779
7935
|
className: "lc-bar" + (props.selected ? " lc-bar-selected" : "") + (props.hovered ? " lc-bar-hovered" : "") + (props.inTurn ? " lc-bar-in-turn" : ""),
|
|
7780
7936
|
"data-seq": req.seq,
|
|
@@ -7791,7 +7947,10 @@ window.__ModuleLoader__.load({
|
|
|
7791
7947
|
children: "✂"
|
|
7792
7948
|
}) : null, diverge ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7793
7949
|
className: "lc-bar-up",
|
|
7794
|
-
style: {
|
|
7950
|
+
style: {
|
|
7951
|
+
bottom: `${props.downPx}px`,
|
|
7952
|
+
...enterStyle
|
|
7953
|
+
},
|
|
7795
7954
|
children: CATS.map((c) => {
|
|
7796
7955
|
const d = req[c.key] || 0;
|
|
7797
7956
|
if (d <= 0) return null;
|
|
@@ -7806,7 +7965,10 @@ window.__ModuleLoader__.load({
|
|
|
7806
7965
|
})
|
|
7807
7966
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7808
7967
|
className: "lc-bar-down",
|
|
7809
|
-
style: {
|
|
7968
|
+
style: {
|
|
7969
|
+
top: `${props.upPx}px`,
|
|
7970
|
+
...enterStyle
|
|
7971
|
+
},
|
|
7810
7972
|
children: CATS.map((c) => {
|
|
7811
7973
|
const d = req[c.key] || 0;
|
|
7812
7974
|
if (d >= 0) return null;
|
|
@@ -7821,6 +7983,7 @@ window.__ModuleLoader__.load({
|
|
|
7821
7983
|
})
|
|
7822
7984
|
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7823
7985
|
className: "lc-bar-stack",
|
|
7986
|
+
style: enterStyle,
|
|
7824
7987
|
children: CATS.map((c) => {
|
|
7825
7988
|
const v = req[c.key] || 0;
|
|
7826
7989
|
if (!v) return null;
|
|
@@ -8189,9 +8352,10 @@ window.__ModuleLoader__.load({
|
|
|
8189
8352
|
upPx: delta ? upPx : void 0,
|
|
8190
8353
|
downPx: delta ? downPx : void 0,
|
|
8191
8354
|
deltaScale: delta ? deltaScale : void 0,
|
|
8355
|
+
enterIndex: i,
|
|
8192
8356
|
onSelect: props.onSelect,
|
|
8193
8357
|
onHover: props.onHover
|
|
8194
|
-
}, req.seq))
|
|
8358
|
+
}, `${req.seq}:${props.granularity}`))
|
|
8195
8359
|
]
|
|
8196
8360
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8197
8361
|
className: "lc-turns",
|
|
@@ -8359,6 +8523,12 @@ window.__ModuleLoader__.load({
|
|
|
8359
8523
|
}, [sessionId]);
|
|
8360
8524
|
const requests = data ? data.requests : [];
|
|
8361
8525
|
const events = data ? data.events : [];
|
|
8526
|
+
const counts = data?.counts ?? countsOfRecords(requests, events);
|
|
8527
|
+
const kindCounts = {
|
|
8528
|
+
inject: counts.injects,
|
|
8529
|
+
compaction: counts.compactions,
|
|
8530
|
+
prune: counts.prunes
|
|
8531
|
+
};
|
|
8362
8532
|
const shownEvents = pickedKinds.length === EVENT_KINDS.length ? events : events.filter((e) => pickedKinds.includes(e.kind));
|
|
8363
8533
|
const displayRequests = (0, react.useMemo)(() => granularity === "turn" ? aggregateByTurn(requests) : requests, [requests, granularity]);
|
|
8364
8534
|
const markers = (0, react.useMemo)(() => attachMarkers(displayRequests, events), [displayRequests, events]);
|
|
@@ -8436,6 +8606,16 @@ window.__ModuleLoader__.load({
|
|
|
8436
8606
|
};
|
|
8437
8607
|
}, [ctx]);
|
|
8438
8608
|
const fileOpener = (0, react.useMemo)(() => canOpenPaths ? openPathVia(ctx) : void 0, [canOpenPaths, ctx]);
|
|
8609
|
+
const previewOpener = (0, react.useMemo)(() => openResourceVia(ctx), [ctx]);
|
|
8610
|
+
const sessionKey = typeof sessionId === "string" ? sessionId : void 0;
|
|
8611
|
+
const previewFile = (0, react.useMemo)(() => previewOpener === void 0 ? void 0 : (entry) => {
|
|
8612
|
+
const address = previewAddressOf(entry.path, entry.form, entry.pattern, sessionKey, workspace);
|
|
8613
|
+
return address !== void 0 && previewOpener(address);
|
|
8614
|
+
}, [
|
|
8615
|
+
previewOpener,
|
|
8616
|
+
sessionKey,
|
|
8617
|
+
workspace
|
|
8618
|
+
]);
|
|
8439
8619
|
const locateFileOp = (0, react.useCallback)((op) => {
|
|
8440
8620
|
const seq = op.parent ?? op.seq;
|
|
8441
8621
|
const step = locateStepOf(requests, seq, op.gone);
|
|
@@ -8627,20 +8807,24 @@ window.__ModuleLoader__.load({
|
|
|
8627
8807
|
className: "lc-root",
|
|
8628
8808
|
ref: rootRef,
|
|
8629
8809
|
children: [
|
|
8810
|
+
inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8811
|
+
className: "lc-cols lc-head",
|
|
8812
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsContext, {
|
|
8813
|
+
counts,
|
|
8814
|
+
humanInputs: data.humanInputs,
|
|
8815
|
+
toolCalls: data.toolCalls,
|
|
8816
|
+
usage,
|
|
8817
|
+
cost: data.cost,
|
|
8818
|
+
locale: activeLocale
|
|
8819
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})]
|
|
8820
|
+
}),
|
|
8630
8821
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8631
8822
|
className: "lc-cols lc-head",
|
|
8632
|
-
children: [
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
cost: data.cost,
|
|
8638
|
-
locale: activeLocale
|
|
8639
|
-
}),
|
|
8640
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTokens, { usage }),
|
|
8641
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: data.timing ?? null }),
|
|
8642
|
-
inSidebar ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginInfo, {})
|
|
8643
|
-
]
|
|
8823
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTokens, {
|
|
8824
|
+
usage,
|
|
8825
|
+
current: data.current,
|
|
8826
|
+
breakdown
|
|
8827
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatsTiming, { timing: data.timing ?? null })]
|
|
8644
8828
|
}),
|
|
8645
8829
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8646
8830
|
className: "lc-cols lc-cols-main",
|
|
@@ -8663,13 +8847,20 @@ window.__ModuleLoader__.load({
|
|
|
8663
8847
|
children: t("events.title")
|
|
8664
8848
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8665
8849
|
className: "lc-kinds",
|
|
8666
|
-
children: EVENT_KINDS.map((k) =>
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8850
|
+
children: EVENT_KINDS.map((k) => {
|
|
8851
|
+
const n = kindCounts[k];
|
|
8852
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
8853
|
+
"data-kind": k,
|
|
8854
|
+
className: "lc-gran-btn" + (pickedKinds.includes(k) ? " lc-gran-on lc-kind-" + k : ""),
|
|
8855
|
+
onClick: () => {
|
|
8856
|
+
toggleKind(k);
|
|
8857
|
+
},
|
|
8858
|
+
children: [t("kind." + k), n !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8859
|
+
className: "lc-kind-n",
|
|
8860
|
+
children: kit.fmt(n)
|
|
8861
|
+
}) : null]
|
|
8862
|
+
}, k);
|
|
8863
|
+
})
|
|
8673
8864
|
})]
|
|
8674
8865
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EventList, {
|
|
8675
8866
|
events: shownEvents,
|
|
@@ -8680,6 +8871,7 @@ window.__ModuleLoader__.load({
|
|
|
8680
8871
|
activity: fileActivity,
|
|
8681
8872
|
scope: fileScope,
|
|
8682
8873
|
workspace,
|
|
8874
|
+
onPreview: previewFile,
|
|
8683
8875
|
onOpen: fileOpener,
|
|
8684
8876
|
onLocate: locateFileOp,
|
|
8685
8877
|
state: source.detailState,
|
|
@@ -8994,7 +9186,7 @@ window.__ModuleLoader__.load({
|
|
|
8994
9186
|
}
|
|
8995
9187
|
//#endregion
|
|
8996
9188
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/base.css.mjs
|
|
8997
|
-
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{height:auto;padding:12px;overflow:visible}.lc-title-icon{flex:none}.lc-title-label{padding-right:30px}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-trend-adaptive{flex:none;margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:min(360px,100%)}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=
|
|
9189
|
+
const css$13 = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}[data-sidebar-right-open] .lc-root,[data-sidebar-right-float-host] .lc-root{height:auto;padding:12px;overflow:visible}.lc-title-icon{flex:none}.lc-title-label{padding-right:30px}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-root .lc-card{margin-bottom:7px;padding:7px 8px;container:lc-card/inline-size}.lc-root .lc-cols{gap:7px;margin-bottom:7px}.lc-card-title{flex-wrap:wrap;align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-trend-ctl{align-items:center;gap:6px;margin-left:auto;display:flex}.lc-trend-ctl .lc-gran{margin-left:0}.lc-trend-adaptive{flex:none;margin-left:0}.lc-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-gran-btn:hover{color:var(--dsw-alias-label-primary)}.lc-gran-on,.lc-gran-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-kind-n{font-variant-numeric:tabular-nums;opacity:.65;margin-left:4px}.lc-gran-on .lc-kind-n{opacity:.85}.lc-tip{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;color:var(--dsw-alias-label-primary);box-shadow:var(--dsw-shadow-lv3,0 2px 8px #0000002e);pointer-events:none;opacity:0;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:6px 10px;font-size:12px;position:absolute}.lc-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:min(360px,100%)}.lc-cols>.lc-card,.lc-col>.lc-card:last-child{margin-bottom:0}.lc-col-browser{flex-direction:column;display:flex}.lc-col-browser>.lc-card{flex:1}.lc-head{container:lc-head-row/inline-size}.lc-head>.lc-card{flex:1 1 0;min-width:0}@container lc-head-row (width<=720px){.lc-head>.lc-card{flex-basis:100%}}.lc-head>.lc-card:has(.lc-stats){flex-direction:column;display:flex}.lc-head .lc-stats{flex:1;align-content:stretch}.lc-head .lc-stat{justify-content:center}.lc-empty{color:var(--dsw-alias-label-secondary);text-align:center;padding:18px 0}.lc-error{flex-direction:column;align-items:center;gap:8px;padding:40px 16px;display:flex}.lc-error-msg{font-family:var(--ds-font-family-code,ui-monospace, SFMono-Regular, Menlo, monospace);color:var(--dsw-alias-state-error-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);overflow-wrap:anywhere;border-radius:6px;max-width:100%;padding:4px 8px;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;transition:border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;padding:4px 14px;font-size:12px}.lc-error-retry:hover{border-color:var(--dsw-alias-label-primary)}.lc-foot{color:var(--dsw-alias-label-secondary);margin-top:4px;font-size:12px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])),[data-conversation-scroll]:has(.lc-root,.lc-modal-backdrop)~[data-width-handle]{display:none}";
|
|
8998
9190
|
const tagId$13 = "dsh-context/base.css";
|
|
8999
9191
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$13) + "]") === null) {
|
|
9000
9192
|
const tag = document.createElement("style");
|
|
@@ -9005,7 +9197,7 @@ window.__ModuleLoader__.load({
|
|
|
9005
9197
|
}
|
|
9006
9198
|
//#endregion
|
|
9007
9199
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stats.css.mjs
|
|
9008
|
-
const css$12 = ".lc-stats{grid-template-columns:repeat(
|
|
9200
|
+
const css$12 = ".lc-stats{grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.lc-stats .lc-stat{padding:6px 3px}.lc-stats .lc-stat-q{margin:0;position:absolute;top:3px;right:4px}.lc-stat-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;font-weight:600;overflow:hidden}.lc-stat-value{color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:center;font-size:14px;font-weight:600;overflow:hidden}.lc-stat-sub{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;font-size:11px;overflow:hidden}.lc-stat-tipped{position:relative}.lc-stat-q{text-align:center;width:11px;height:11px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);cursor:help;transition:color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), border-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:50%;margin-left:4px;font-size:9px;font-style:normal;font-weight:700;line-height:11px;display:inline-block}.lc-stat-tipped:hover .lc-stat-q{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-primary)}.lc-stat-tip{max-width:280px;font-weight:400;line-height:1.5;top:calc(100% + 6px);left:0}.lc-stat-tipped:hover .lc-stat-tip{opacity:1}.lc-stat-tip-prices{border-top:1px dashed var(--dsw-alias-border-l1);margin-top:5px;padding-top:5px;display:block}.lc-stat-tip-head{font-weight:600;display:block}.lc-stat-tip-row{margin-top:2px;display:block}.lc-stat-tip-model{color:var(--dsw-alias-label-primary)}.lc-card-sub{color:var(--dsw-alias-label-secondary);margin-left:auto;font-size:12px;font-weight:400}.lc-overview-num{align-items:baseline;gap:6px;margin-bottom:8px;display:flex}.lc-overview-num>b{font-size:20px}.lc-overview-num span{color:var(--dsw-alias-label-secondary)}.lc-overview-pct{margin-left:auto;font-size:11px}.lc-overview-pct b{color:var(--dsw-alias-label-primary);margin-right:4px;font-size:20px}.lc-stat-head{color:var(--dsw-alias-label-secondary);margin:12px 0 8px;font-size:11px;font-weight:600}.lc-donut-row{justify-content:flex-start;align-items:center;gap:12px;min-width:0;display:flex}.lc-col-donut{flex-direction:column;display:flex}.lc-col-donut .lc-donut-row{flex:1}.lc-donut-row .lc-sl{flex:auto}.lc-donut{flex:none;position:relative}.lc-donut svg{display:block}.lc-donut-track{stroke:#8080802e}.lc-donut-seg{transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);animation:.45s cubic-bezier(.4,0,.2,1) backwards lc-donut-in;animation-delay:calc(var(--lc-i,0) * 50ms)}@keyframes lc-donut-in{0%{stroke-dasharray:0 100}}.lc-donut-dim .lc-donut-seg{opacity:.35}.lc-donut-dim .lc-donut-seg-on{opacity:1}.lc-donut-center{pointer-events:none;text-align:center;box-sizing:border-box;flex-direction:column;justify-content:center;align-items:center;gap:1px;padding:0 7px;display:flex;position:absolute;inset:0}.lc-donut-center b{white-space:nowrap;text-overflow:ellipsis;max-width:100%;line-height:1.15;overflow:hidden}.lc-donut-center span{color:var(--dsw-alias-label-secondary);white-space:nowrap}.lc-sl{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:4px 20px;min-width:0;display:grid;overflow:hidden}.lc-sl-row{min-width:0;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;flex-direction:column;gap:1px;margin:0 -6px;padding:1px 6px;font-size:12px;display:flex}.lc-sl-row-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-sl-row-dim{opacity:.55}.lc-sl-main{align-items:center;gap:6px;min-width:0;display:flex}.lc-sl-dot{border-radius:2px;flex:none;width:8px;height:8px}.lc-sl-label{white-space:nowrap;text-overflow:ellipsis;flex:auto;min-width:0;font-weight:600;line-height:1.25;overflow:hidden}.lc-sl-pct{text-align:right;font-variant-numeric:tabular-nums;flex:none;min-width:34px;font-weight:700}.lc-sl-sub{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;white-space:nowrap;text-overflow:ellipsis;margin-left:14px;font-size:11px;line-height:1.3;overflow:hidden}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.lc-pi-row-btn{appearance:none;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;width:100%;padding:0}.lc-pi-row{min-width:0;color:inherit;flex-direction:row;justify-content:space-between;align-items:baseline;gap:12px;text-decoration:none;display:flex}.lc-pi-row:hover .lc-pi-value{text-decoration:underline}.lc-pi-hint{text-decoration:none}.lc-pi-hint:hover{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-pi-row-btn .lc-pi-value{font-weight:400}@container lc-card (width>=700px){.lc-stats{grid-template-columns:repeat(6,minmax(0,1fr))}}@container lc-card (width<=320px){.lc-donut-row{gap:8px}.lc-stats{grid-template-columns:repeat(2,minmax(0,1fr))}}@container lc-card (width<=240px){.lc-donut-row{flex-wrap:wrap}.lc-donut{margin:0 auto}.lc-donut-row .lc-sl{flex-basis:100%}}@media (prefers-reduced-motion:reduce){.lc-donut-seg{animation:none}}";
|
|
9009
9201
|
const tagId$12 = "dsh-context/stats.css";
|
|
9010
9202
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$12) + "]") === null) {
|
|
9011
9203
|
const tag = document.createElement("style");
|
|
@@ -9038,7 +9230,7 @@ window.__ModuleLoader__.load({
|
|
|
9038
9230
|
}
|
|
9039
9231
|
//#endregion
|
|
9040
9232
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/stackedBar.css.mjs
|
|
9041
|
-
const css$9 = ".lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:5px;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;padding:3px 8px;bottom:calc(100% + 6px);transform:translate(-50%)}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg-pick{cursor:pointer}.lc-stacked-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-stacked-free{box-sizing:border-box;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;align-items:center;gap:5px;padding:1px 6px;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}";
|
|
9233
|
+
const css$9 = ".lc-stacked-wrap{width:100%;position:relative}.lc-stacked{background:#8080802e;border-radius:5px;width:100%;display:flex;position:relative;overflow:hidden}.lc-occupied-box{border:2px solid var(--dsw-alias-label-tertiary);box-sizing:border-box;pointer-events:none;opacity:0;box-shadow:0 0 0 1px var(--dsw-alias-bg-layer-2);transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:5px;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;padding:3px 8px;bottom:calc(100% + 6px);transform:translate(-50%)}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg-pick{cursor:pointer}.lc-stacked-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-stacked-free{box-sizing:border-box;transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-stacked-seg,.lc-stacked-free{transform-origin:0;animation:.4s cubic-bezier(.4,0,.2,1) backwards lc-stacked-in;animation-delay:calc(var(--lc-i,0) * 40ms)}@keyframes lc-stacked-in{0%{transform:scaleX(0)}}.lc-stacked-seg-on{filter:brightness(1.18)}.lc-reserve{z-index:1;pointer-events:auto;cursor:help;background:repeating-linear-gradient(45deg, color-mix(in srgb, var(--dsw-alias-state-warn-primary) 24%, transparent) 0 5px, transparent 5px 10px);position:absolute;top:0;bottom:0}.lc-stacked-free-on{border:2px dashed var(--dsw-alias-label-secondary);border-radius:3px}.lc-stacked-dim .lc-stacked-seg{opacity:.35}.lc-stacked-dim .lc-stacked-seg-on{opacity:1}.lc-stacked-dim .lc-stacked-free{opacity:.35}.lc-stacked-dim .lc-stacked-free-on{opacity:1}.lc-legend{grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px 14px;margin-top:10px;display:grid}.lc-chip{min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;transition:background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:6px;align-items:center;gap:5px;padding:1px 6px;display:flex}.lc-chip-label{white-space:nowrap;text-overflow:ellipsis;min-width:0;font-weight:600;overflow:hidden}.lc-chip-nums{white-space:nowrap;flex:none;align-items:baseline;gap:6px;margin-left:auto;display:inline-flex}.lc-chip i,.lc-detail-row i{border-radius:2px;width:8px;height:8px;display:inline-block}.lc-chip i{transition:box-shadow var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chip em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-chip-on{background:var(--dsw-alias-interactive-bg-hover);font-weight:600}.lc-chip-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}@media (prefers-reduced-motion:reduce){.lc-stacked-seg,.lc-stacked-free{animation:none}}";
|
|
9042
9234
|
const tagId$9 = "dsh-context/stackedBar.css";
|
|
9043
9235
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
|
|
9044
9236
|
const tag = document.createElement("style");
|
|
@@ -9049,7 +9241,7 @@ window.__ModuleLoader__.load({
|
|
|
9049
9241
|
}
|
|
9050
9242
|
//#endregion
|
|
9051
9243
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
|
|
9052
|
-
const css$8 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080;top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}";
|
|
9244
|
+
const css$8 = ".lc-chartrow{align-items:stretch;gap:6px;display:flex}.lc-axis{box-sizing:border-box;width:40px;height:150px;color:var(--dsw-alias-label-secondary);padding-top:18px;font-size:11px;position:relative}.lc-axis span{line-height:1;position:absolute;right:0}.lc-axis-top{top:13px}.lc-axis-q3{top:41px}.lc-axis-mid{top:69px}.lc-axis-q1{top:97px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{overscroll-behavior-x:contain;scrollbar-gutter:stable;padding-bottom:var(--dsh-scrollbar-width,8px);overflow:auto hidden}.lc-chart{box-sizing:border-box;align-items:flex-end;gap:2px;width:max-content;min-width:100%;height:130px;padding-top:18px;display:flex;position:relative}.lc-grid{border-top:1px dashed var(--dsw-alias-border-l2);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-q3{top:46px}.lc-grid-mid{top:74px}.lc-grid-q1{top:102px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080;top:130px}.lc-bar{cursor:pointer;outline-offset:1px;width:14px;height:100%;transition:opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), background-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), outline-color var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:2px;outline:1px dashed #0000;flex:none;align-items:flex-end;display:flex;position:relative}.lc-chart-dim .lc-bar{opacity:.35}.lc-chart-dim .lc-bar-in-turn{opacity:1}.lc-chart-dim .lc-turn{opacity:.35}.lc-chart-dim .lc-turn-on{opacity:1}.lc-chart-tip{z-index:5;box-sizing:border-box;overflow-wrap:break-word;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:100%;color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;flex-direction:column;gap:2px;padding:3px 8px;font-size:12px;display:flex;position:absolute;bottom:calc(100% + 4px);left:0;box-shadow:0 2px 8px #0000002e}.lc-chart-tip span+span{color:var(--dsw-alias-label-secondary)}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline-color:var(--dsw-alias-brand-primary)}.lc-bar-selected{outline:2px solid var(--dsw-alias-label-primary);outline-offset:1px}.lc-bar-in-turn{background:#80808024}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down{transform-origin:50% 100%;animation:.35s cubic-bezier(.4,0,.2,1) backwards lc-bar-in;animation-delay:calc(var(--lc-i,0) * 15ms)}.lc-bar-down{transform-origin:50% 0}@keyframes lc-bar-in{0%{transform:scaleY(0)}}.lc-bar-stack>div:first-child{border-radius:0 0 2px 2px}.lc-bar-stack>div:last-child{border-radius:2px 2px 0 0}.lc-bar-stack>div:only-child{border-radius:2px}.lc-bar-up,.lc-bar-down{width:100%;display:flex;position:absolute;left:0}.lc-bar-up{flex-direction:column-reverse}.lc-bar-down{flex-direction:column}.lc-bar-up>div,.lc-bar-down>div{width:100%}.lc-bar-up>div:last-child{border-radius:2px 2px 0 0}.lc-bar-down>div:last-child{border-radius:0 0 2px 2px}.lc-cat-seg{transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out)}.lc-chart[data-catdim] .lc-cat-seg{opacity:.35}.lc-chart[data-catdim=system] .lc-cat-seg[data-cat=system],.lc-chart[data-catdim=tools] .lc-cat-seg[data-cat=tools],.lc-chart[data-catdim=user] .lc-cat-seg[data-cat=user],.lc-chart[data-catdim=inject] .lc-cat-seg[data-cat=inject],.lc-chart[data-catdim=assistant] .lc-cat-seg[data-cat=assistant],.lc-chart[data-catdim=tool] .lc-cat-seg[data-cat=tool]{opacity:1;filter:brightness(1.18)}.lc-bar-marker{color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-turns{gap:2px;width:max-content;min-width:100%;margin-top:4px;display:flex;overflow:hidden}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;cursor:pointer;height:14px;transition:filter var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out), opacity var(--ds-transition-duration,.2s) var(--ds-ease-in-out,ease-in-out);border-radius:3px;flex:none;font-size:10px;font-weight:600;line-height:14px}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{display:inline-block}@media (prefers-reduced-motion:reduce){.lc-bar-stack,.lc-bar-up,.lc-bar-down{animation:none}}";
|
|
9053
9245
|
const tagId$8 = "dsh-context/trendChart.css";
|
|
9054
9246
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$8) + "]") === null) {
|
|
9055
9247
|
const tag = document.createElement("style");
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import "@deepseek-ai/dsh-session";
|
|
3
3
|
import { Context } from "@deepseek-ai/cordis";
|
|
4
4
|
//#region src/host/config.d.ts
|
|
5
|
-
interface Config {
|
|
5
|
+
export interface Config {
|
|
6
6
|
/** Cap on kept per-step request records (the hard step backstop). */
|
|
7
7
|
maxRequestSteps?: number;
|
|
8
8
|
/** Newest whole-turn window kept; trimming crosses whole turns, never mid-turn. */
|
|
@@ -23,7 +23,7 @@ interface Config {
|
|
|
23
23
|
* The cordis `Config` validator: strict on keys, defaults on the schema fields; tolerates `undefined` (a patch row without a `config:`
|
|
24
24
|
* block — defaults win).
|
|
25
25
|
*/
|
|
26
|
-
declare const Config: z.ZodPreprocess<z.ZodObject<{
|
|
26
|
+
export declare const Config: z.ZodPreprocess<z.ZodObject<{
|
|
27
27
|
maxRequestSteps: z.ZodDefault<z.ZodNumber>;
|
|
28
28
|
maxKeptTurns: z.ZodDefault<z.ZodNumber>;
|
|
29
29
|
maxEvents: z.ZodDefault<z.ZodNumber>;
|
|
@@ -135,6 +135,13 @@ interface TimelineState {
|
|
|
135
135
|
* Absent until a DeepSeek flash/pro request reports usage.
|
|
136
136
|
*/
|
|
137
137
|
cost?: SessionCostUsage;
|
|
138
|
+
/**
|
|
139
|
+
* Whole-session human-input tally (see Snapshot.humanInputs): every
|
|
140
|
+
* non-injection `user/message` plus every answered `ask_user_question`
|
|
141
|
+
* result. Running total — never trimmed, like `cost`/`timing`. Absent until
|
|
142
|
+
* the first human input folds.
|
|
143
|
+
*/
|
|
144
|
+
humanInputs?: number;
|
|
138
145
|
archiveFloor?: number;
|
|
139
146
|
/**
|
|
140
147
|
* The detail collections' revision marker (see ContextTimelineDetail):
|
|
@@ -339,6 +346,14 @@ interface Snapshot {
|
|
|
339
346
|
* hosts; clients treat absence as zero.
|
|
340
347
|
*/
|
|
341
348
|
toolCalls?: number;
|
|
349
|
+
/**
|
|
350
|
+
* Whole-session human-input tally: every non-injection `user/message`
|
|
351
|
+
* (the user's own messages) plus every answered `ask_user_question`
|
|
352
|
+
* result (one per answer submission). A running total over the COMPLETE
|
|
353
|
+
* log — turns the retained window no longer holds still count. Absent
|
|
354
|
+
* from older hosts; clients treat absence as zero.
|
|
355
|
+
*/
|
|
356
|
+
humanInputs?: number;
|
|
342
357
|
/**
|
|
343
358
|
* Split-generation head fields — present exactly when the host serves the
|
|
344
359
|
* SLIM head (the heavy collections moved to the on-demand detail channel,
|
|
@@ -568,7 +583,7 @@ interface CostFamilyUsage {
|
|
|
568
583
|
* token totals per DeepSeek model family (matched on the model NAME,
|
|
569
584
|
* provider-agnostic) and pricing period. The Client prices these with its
|
|
570
585
|
* hardcoded list-price table in the locale's currency. Absent until a
|
|
571
|
-
* deepseek-flash / deepseek-v4
|
|
586
|
+
* deepseek-flash / deepseek-v4-flash / deepseek-v4-pro request reports
|
|
572
587
|
* usage.
|
|
573
588
|
*/
|
|
574
589
|
interface SessionCostUsage {
|
|
@@ -688,8 +703,8 @@ interface ContextHeaders {
|
|
|
688
703
|
}
|
|
689
704
|
//#endregion
|
|
690
705
|
//#region src/host/index.d.ts
|
|
691
|
-
declare const name = "dsh-context";
|
|
692
|
-
declare const inject: string[];
|
|
693
|
-
declare function apply(ctx: Context, config: Config): void;
|
|
706
|
+
export declare const name = "dsh-context";
|
|
707
|
+
export declare const inject: string[];
|
|
708
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
694
709
|
//#endregion
|
|
695
|
-
export {
|
|
710
|
+
export type { Category, ContextEventRecord, ContextHeaders, ContextTimeline, ContextTimelineDetail, HeaderRecord, HeaderTool, HeadersState, RequestRecord, Snapshot, SurfaceNode, TimelineCounts, TimelineLast, TimelineState };
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
-
import { dirname, join, normalize } from "node:path";
|
|
3
|
+
import { dirname, join, normalize, relative } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { deriveEventMessage } from "@deepseek-ai/dsh-session";
|
|
@@ -1340,6 +1340,13 @@ function accumulateCost(st, time, usage) {
|
|
|
1340
1340
|
*/
|
|
1341
1341
|
/** The timing card's per-tool ranking cap: the busiest 16 names are kept. */
|
|
1342
1342
|
const TOOL_TIMING_CAP = 16;
|
|
1343
|
+
/**
|
|
1344
|
+
* The interactive Q&A tool (`dsh-tool-ask-user`): its settled result IS the
|
|
1345
|
+
* user's answer, so it folds into the human-input tally alongside the user's
|
|
1346
|
+
* own messages. One result = one answer submission, however many questions
|
|
1347
|
+
* the prompt carried.
|
|
1348
|
+
*/
|
|
1349
|
+
const ASK_USER_TOOL = "ask_user_question";
|
|
1343
1350
|
/** The decode buckets of the generation split, in card order (see TimingTotals). */
|
|
1344
1351
|
const DECODE_KINDS = [
|
|
1345
1352
|
"reasoning",
|
|
@@ -1600,7 +1607,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1600
1607
|
if (source.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
|
|
1601
1608
|
}
|
|
1602
1609
|
s.events.push(rec);
|
|
1603
|
-
}
|
|
1610
|
+
} else s.humanInputs = (s.humanInputs ?? 0) + 1;
|
|
1604
1611
|
break;
|
|
1605
1612
|
}
|
|
1606
1613
|
case "tool/result": {
|
|
@@ -1613,6 +1620,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1613
1620
|
const s = ensure();
|
|
1614
1621
|
bumpDetailRev(s);
|
|
1615
1622
|
const node = applySurface(s, event, event.type, data, toolMsg);
|
|
1623
|
+
if (node.tool === ASK_USER_TOOL) s.humanInputs = (s.humanInputs ?? 0) + 1;
|
|
1616
1624
|
if (pendingEntry !== void 0) pushFileOps(s, opsOfCall({
|
|
1617
1625
|
seq: event.seq,
|
|
1618
1626
|
time: event.time,
|
|
@@ -1775,6 +1783,7 @@ function headFieldsOf(state) {
|
|
|
1775
1783
|
},
|
|
1776
1784
|
images: state.surface.reduce((n, node) => n + (node.imgs ?? 0), 0),
|
|
1777
1785
|
toolCalls: state.surface.reduce((n, node) => node.cat === "tool" ? n + 1 : n, 0),
|
|
1786
|
+
humanInputs: state.humanInputs ?? 0,
|
|
1778
1787
|
requests: [],
|
|
1779
1788
|
events: [],
|
|
1780
1789
|
nodes: [],
|
|
@@ -2009,7 +2018,7 @@ function watchDetailChannel(ctx, bounds) {
|
|
|
2009
2018
|
* host probes and gates; the client displays what the wire record carries),
|
|
2010
2019
|
* so this module must stay dependency-free.
|
|
2011
2020
|
*
|
|
2012
|
-
* The baseline mirrors the support matrix (
|
|
2021
|
+
* The baseline mirrors the support matrix (docs/compatibility.md and the
|
|
2013
2022
|
* package's `dsh.compatibility.dshReleases` declaration): the oldest dsh
|
|
2014
2023
|
* release this plugin works on. A harness BELOW it gets the fallback units
|
|
2015
2024
|
* (host/fallback.ts) instead of the real folds.
|
|
@@ -2387,6 +2396,7 @@ const contextTimelineSchema = z.object({
|
|
|
2387
2396
|
current: currentSchema,
|
|
2388
2397
|
images: z.number().int().nonnegative().optional(),
|
|
2389
2398
|
toolCalls: z.number().int().nonnegative().optional(),
|
|
2399
|
+
humanInputs: z.number().int().nonnegative().optional(),
|
|
2390
2400
|
counts: countsSchema.optional(),
|
|
2391
2401
|
last: lastSchema.optional(),
|
|
2392
2402
|
detailRev: z.number().int().nonnegative().optional(),
|
|
@@ -2437,6 +2447,7 @@ const timelineStateSchema = z.object({
|
|
|
2437
2447
|
}).strict().optional(),
|
|
2438
2448
|
archiveFloor: z.number().optional(),
|
|
2439
2449
|
timing: timingTotalsSchema.optional(),
|
|
2450
|
+
humanInputs: z.number().int().nonnegative().optional(),
|
|
2440
2451
|
stepStart: z.object({
|
|
2441
2452
|
time: z.number(),
|
|
2442
2453
|
firstToken: z.number().optional(),
|
|
@@ -2511,7 +2522,7 @@ function createContextTimelineDefinition(config, slim) {
|
|
|
2511
2522
|
},
|
|
2512
2523
|
init: () => createTimelineState(),
|
|
2513
2524
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
2514
|
-
stateVersion:
|
|
2525
|
+
stateVersion: 16
|
|
2515
2526
|
};
|
|
2516
2527
|
}
|
|
2517
2528
|
//#endregion
|
|
@@ -2712,35 +2723,72 @@ function watchStepIdentity(ctx) {
|
|
|
2712
2723
|
* The probe never imports a harness package for its version; it reads
|
|
2713
2724
|
* manifests through Node resolution from two anchors, in order:
|
|
2714
2725
|
*
|
|
2715
|
-
* 1.
|
|
2716
|
-
*
|
|
2717
|
-
*
|
|
2718
|
-
*
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
*
|
|
2722
|
-
*
|
|
2726
|
+
* 1. This module's own ESM resolution of the library packages the Host half
|
|
2727
|
+
* imports at runtime. That is the very pipeline the process resolves those
|
|
2728
|
+
* imports through, so an embedding shell that redirects plugin imports —
|
|
2729
|
+
* the packaged Desktop's ASAR resolver bridge does exactly this — redirects
|
|
2730
|
+
* the probe identically, and the answer names the module instances the
|
|
2731
|
+
* harness is actually running. A witness that lands inside this package's
|
|
2732
|
+
* own tree is the plugin's dependency closure (a `link:`-installed dev
|
|
2733
|
+
* checkout's pinned devDependencies), never the harness, so it is discarded
|
|
2734
|
+
* for the home anchor.
|
|
2735
|
+
* 2. The harness home's healed `profiles/node_modules` mirror (located via
|
|
2736
|
+
* the app-boot `dshHomePath` service). It names the installation that last
|
|
2737
|
+
* healed it: authoritative for a dev checkout, whose own tree holds pinned
|
|
2738
|
+
* devDependencies, but only as fresh as the last CLI profile boot. A
|
|
2739
|
+
* packaged Desktop never heals it, so a stale global-CLI mirror can outlive
|
|
2740
|
+
* the CLI and misname the running harness — that is why it answers only
|
|
2741
|
+
* when the running anchor has nothing it can trust.
|
|
2723
2742
|
*
|
|
2724
2743
|
* The `@deepseek-ai/dsh` CLI package is probed ONLY through the home anchor:
|
|
2725
|
-
* the plugin never
|
|
2726
|
-
*
|
|
2744
|
+
* the plugin never imports it, so a hit from the running anchor can only be an
|
|
2745
|
+
* ambient install above the plugin's tree (e.g. a global copy under
|
|
2727
2746
|
* ~/node_modules) — not necessarily the RUNNING harness.
|
|
2728
2747
|
*
|
|
2729
2748
|
* Every step is guarded: any failure (absent service, unresolvable package,
|
|
2730
|
-
* unreadable/invalid manifest, non-string version) degrades to
|
|
2731
|
-
* and the gate treats an unknown version as SATISFIED — a probe
|
|
2732
|
-
* never blank a working deployment.
|
|
2749
|
+
* non-file URL, unreadable/invalid manifest, non-string version) degrades to
|
|
2750
|
+
* `undefined`, and the gate treats an unknown version as SATISFIED — a probe
|
|
2751
|
+
* misfire must never blank a working deployment.
|
|
2733
2752
|
*/
|
|
2734
2753
|
/**
|
|
2735
|
-
* Library packages whose manifest version IS the harness release version
|
|
2736
|
-
*
|
|
2737
|
-
*
|
|
2738
|
-
*
|
|
2739
|
-
* to probe from either anchor.
|
|
2754
|
+
* Library packages whose manifest version IS the harness release version (the
|
|
2755
|
+
* dsh monorepo versions every package in lockstep). The home anchor probes
|
|
2756
|
+
* them after the CLI package; the running anchor probes the two first- and
|
|
2757
|
+
* second-ordered.
|
|
2740
2758
|
*/
|
|
2741
2759
|
const LIBRARY_PROBE_PACKAGES = ["@deepseek-ai/dsh-session-projection", "@deepseek-ai/dsh-session"];
|
|
2760
|
+
/**
|
|
2761
|
+
* Running-anchor probe order: the package the Host half genuinely imports at
|
|
2762
|
+
* runtime first (host/fold.ts imports it, so it MUST resolve whenever this
|
|
2763
|
+
* plugin runs), then its co-versioned sibling.
|
|
2764
|
+
*/
|
|
2765
|
+
const RUNNING_PROBE_PACKAGES = ["@deepseek-ai/dsh-session", "@deepseek-ai/dsh-session-projection"];
|
|
2742
2766
|
/** Home-anchor probe order: the user-facing CLI version first, then the libraries. */
|
|
2743
2767
|
const HOME_PROBE_PACKAGES = ["@deepseek-ai/dsh", ...LIBRARY_PROBE_PACKAGES];
|
|
2768
|
+
/**
|
|
2769
|
+
* This package's root. A running-anchor witness under it belongs to the
|
|
2770
|
+
* plugin's own dependency closure, not to the harness.
|
|
2771
|
+
*
|
|
2772
|
+
* Located through the package's own `./package.json` self-reference: Node
|
|
2773
|
+
* resolves that against the nearest package.json, so the answer is the package
|
|
2774
|
+
* root in the source tree and in the bundled profile install alike — a fixed
|
|
2775
|
+
* number of `..` steps cannot be, since `src/host/` and `lib/` differ by a
|
|
2776
|
+
* level. A loader that cannot self-resolve (an exotic loader, or a fork renamed
|
|
2777
|
+
* without a matching `exports`) falls back to this module's own directory: the
|
|
2778
|
+
* guard then covers less of the package, but it still can never mistake the
|
|
2779
|
+
* plugin's own `node_modules` for the harness.
|
|
2780
|
+
*/
|
|
2781
|
+
function ownPackageRoot() {
|
|
2782
|
+
try {
|
|
2783
|
+
return dirname(fileURLToPath(import.meta.resolve("dsh-context/package.json")));
|
|
2784
|
+
} catch {
|
|
2785
|
+
/* v8 ignore next -- a loader that cannot resolve one of its own package's
|
|
2786
|
+
published subpaths is out of the suite's reach; the fallback keeps one
|
|
2787
|
+
probe path (an unanswered probe fails open either way). */
|
|
2788
|
+
return dirname(fileURLToPath(import.meta.url));
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
const PLUGIN_ROOT = ownPackageRoot();
|
|
2744
2792
|
/** One manifest's `version`, or undefined on any read/shape failure. */
|
|
2745
2793
|
function versionOfManifest(manifestPath, expectedName) {
|
|
2746
2794
|
try {
|
|
@@ -2790,11 +2838,51 @@ function probeAnchor(resolve, packageNames) {
|
|
|
2790
2838
|
}
|
|
2791
2839
|
}
|
|
2792
2840
|
/**
|
|
2841
|
+
* Whether `candidate` lies inside `root`'s tree. A `..`-prefixed relative path
|
|
2842
|
+
* escapes it; a sibling such as `dsh-context-extra` is reached through `..` too,
|
|
2843
|
+
* so it never counts as a child. A pathological child literally named `..foo`
|
|
2844
|
+
* would degrade to the home anchor, which is the safe direction.
|
|
2845
|
+
*/
|
|
2846
|
+
function isInside(root, candidate) {
|
|
2847
|
+
return !relative(root, candidate).startsWith("..");
|
|
2848
|
+
}
|
|
2849
|
+
/**
|
|
2850
|
+
* The running module tree's answer, or undefined when it cannot be trusted.
|
|
2851
|
+
* Each probe package is checked on its own: a witness inside the plugin's own
|
|
2852
|
+
* closure is the plugin's dependency, not the harness, and a trusted witness
|
|
2853
|
+
* whose manifest cannot be read is no answer either — the next package may
|
|
2854
|
+
* still resolve to one.
|
|
2855
|
+
*/
|
|
2856
|
+
function probeRunningTree(resolve, pluginRoot) {
|
|
2857
|
+
for (const packageName of RUNNING_PROBE_PACKAGES) {
|
|
2858
|
+
let witness;
|
|
2859
|
+
try {
|
|
2860
|
+
witness = resolve(packageName);
|
|
2861
|
+
} catch {
|
|
2862
|
+
continue;
|
|
2863
|
+
}
|
|
2864
|
+
if (isInside(pluginRoot, witness)) continue;
|
|
2865
|
+
const version = versionViaManifest(resolve, packageName) ?? versionViaEntry(resolve, packageName);
|
|
2866
|
+
if (version !== void 0) return version;
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
/** The default running anchor: this module's own ESM resolution. */
|
|
2870
|
+
function resolveRunningModule(specifier) {
|
|
2871
|
+
return import.meta.resolve(specifier);
|
|
2872
|
+
}
|
|
2873
|
+
/**
|
|
2793
2874
|
* The running harness's version string, or undefined when nothing answers
|
|
2794
2875
|
* (the gate fails open on it — see the header note).
|
|
2795
|
-
* @param
|
|
2796
|
-
|
|
2797
|
-
|
|
2876
|
+
* @param resolveUrl - the running anchor's URL resolver (injectable for hermetic tests).
|
|
2877
|
+
* @param pluginRoot - this package's root for the own-closure check (injectable for hermetic tests).
|
|
2878
|
+
*/
|
|
2879
|
+
function detectHarnessVersion(ctx, resolveUrl = resolveRunningModule, pluginRoot = PLUGIN_ROOT) {
|
|
2880
|
+
const running = probeRunningTree((specifier) => {
|
|
2881
|
+
const url = resolveUrl(specifier);
|
|
2882
|
+
if (!url.startsWith("file:")) throw new Error(`dsh-context: non-file module URL for ${specifier}`);
|
|
2883
|
+
return fileURLToPath(url);
|
|
2884
|
+
}, pluginRoot);
|
|
2885
|
+
if (running !== void 0) return running;
|
|
2798
2886
|
try {
|
|
2799
2887
|
const homePath = ctx.get("dshHomePath");
|
|
2800
2888
|
if (typeof homePath === "function") {
|
|
@@ -2803,12 +2891,6 @@ function detectHarnessVersion(ctx, selfUrl = import.meta.url) {
|
|
|
2803
2891
|
if (home !== void 0) return home;
|
|
2804
2892
|
}
|
|
2805
2893
|
} catch {}
|
|
2806
|
-
try {
|
|
2807
|
-
const req = createRequire(selfUrl);
|
|
2808
|
-
return probeAnchor((specifier) => req.resolve(specifier), LIBRARY_PROBE_PACKAGES);
|
|
2809
|
-
} catch {
|
|
2810
|
-
return;
|
|
2811
|
-
}
|
|
2812
2894
|
}
|
|
2813
2895
|
//#endregion
|
|
2814
2896
|
//#region 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.50.0",
|
|
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": {
|
|
@@ -107,6 +107,7 @@
|
|
|
107
107
|
"@deepseek-ai/dsh-session": "0.1.2-rc.1",
|
|
108
108
|
"@deepseek-ai/dsh-session-projection": "0.1.2-rc.1",
|
|
109
109
|
"@deepseek-ai/dsh-settings": "0.1.2-rc.1",
|
|
110
|
+
"@deepseek-ai/dsh-util-workspace-path": "0.1.5-rc.1",
|
|
110
111
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
111
112
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
112
113
|
"@types/node": "^26.2.0",
|
|
@@ -117,12 +118,12 @@
|
|
|
117
118
|
"husky": "^9.1.7",
|
|
118
119
|
"jsdom": "^30.0.1",
|
|
119
120
|
"lightningcss": "^1.33.0",
|
|
120
|
-
"oxlint": "1.
|
|
121
|
+
"oxlint": "1.82.0",
|
|
121
122
|
"oxlint-tsgolint": "7.0.2001",
|
|
122
123
|
"playwright-core": "^1.62.1",
|
|
123
124
|
"react": "^18.3.1",
|
|
124
125
|
"react-dom": "^18.3.1",
|
|
125
|
-
"tsdown": "^0.
|
|
126
|
+
"tsdown": "^0.23.0",
|
|
126
127
|
"typescript": "^7.0.2",
|
|
127
128
|
"vitest": "^4.1.11",
|
|
128
129
|
"zod": "^4.4.3"
|