dsh-context 0.62.2 → 0.63.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 +1 -1
- package/lib/client.js +286 -151
- package/lib/index.d.ts +48 -0
- package/lib/index.js +119 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ A centered dialog opens with the **Current Composition** card and the **Context
|
|
|
174
174
|
|
|
175
175
|
## ⚙️ Settings
|
|
176
176
|
|
|
177
|
-
The **Context** preferences card holds this plugin's per-user settings — default placement, trend granularity (Step/Turn), trend mode (Total/Delta), tool and File Activity sort, and the sidebar insights entry. Where it lives depends on your dsh release:
|
|
177
|
+
The **Context** preferences card holds this plugin's per-user settings — default placement, trend granularity (Step/Turn), trend mode (Total/Delta), the trend duration curve (shown by default), tool and File Activity sort, and the sidebar insights entry. Where it lives depends on your dsh release:
|
|
178
178
|
|
|
179
179
|
- **dsh 0.1.7+** — the sidebar **Plugins** entry → the **dsh-context** bundle's page → its **Configuration** section (the card is served by the entry's live Config form).
|
|
180
180
|
- **older releases** — **Settings → Plugins → Plugin configuration** → the **Context** card.
|
package/lib/client.js
CHANGED
|
@@ -108,6 +108,7 @@ window.__ModuleLoader__.load({
|
|
|
108
108
|
"insightsEntry.hide": "隐藏",
|
|
109
109
|
"settings.gran": "趋势图 · 默认粒度",
|
|
110
110
|
"settings.mode": "趋势图 · 默认展示方式",
|
|
111
|
+
"settings.durationCurve": "趋势图 · 耗时曲线",
|
|
111
112
|
"settings.deltaBase": "上下文浏览器 · 对比基线",
|
|
112
113
|
"settings.toolSort": "工具定义 · 默认排序",
|
|
113
114
|
"settings.fileSort": "文件活动 · 默认排序",
|
|
@@ -120,6 +121,8 @@ window.__ModuleLoader__.load({
|
|
|
120
121
|
"trend.focus": "仅展示 {cat}",
|
|
121
122
|
"trend.adaptive": "自适应",
|
|
122
123
|
"trend.adaptiveHint": "按当前可视范围内的柱子重新缩放高度,滚动时随之变化",
|
|
124
|
+
"trend.duration": "耗时",
|
|
125
|
+
"trend.durationTip": "在柱子上叠加每步的活跃耗时曲线(步骤开始至结束,含模型与工具调用,不含等待你决策或回答的时间),按图中右侧的四分位坐标轴读数;轮次粒度下为一轮各步耗时之和",
|
|
123
126
|
"trend.dna": "DNA 模式",
|
|
124
127
|
"trend.dnaTip": "把每根柱子画成该次请求的上下文指纹:按模型读取顺序逐条铺色,配合增量模式逐条展示每次请求的增减;悬停查看条目,点击让上下文浏览器定位到该条目,同一条目在所有柱子中亮出其存续变化",
|
|
125
128
|
"trend.dnaItem": "{label}:{n}",
|
|
@@ -255,6 +258,7 @@ window.__ModuleLoader__.load({
|
|
|
255
258
|
"tip.total": "合计 ≈ {n}",
|
|
256
259
|
"tip.cat": "{cat} ≈ {n}",
|
|
257
260
|
"tip.delta": "Δ {n}",
|
|
261
|
+
"tip.duration": "耗时 {n}",
|
|
258
262
|
"ev.compaction": "压缩上下文(摘要替换 {n} 条消息)",
|
|
259
263
|
"ev.prune": "剪枝工具输出",
|
|
260
264
|
"ev.skill": "Skill 注入({name})",
|
|
@@ -506,6 +510,7 @@ window.__ModuleLoader__.load({
|
|
|
506
510
|
"insightsEntry.hide": "Hide",
|
|
507
511
|
"settings.gran": "Trend Chart · Default granularity",
|
|
508
512
|
"settings.mode": "Trend Chart · Default display",
|
|
513
|
+
"settings.durationCurve": "Trend Chart · Duration curve",
|
|
509
514
|
"settings.deltaBase": "Context Browser · Diff baseline",
|
|
510
515
|
"settings.toolSort": "Tool Schemas · Default sort",
|
|
511
516
|
"settings.fileSort": "File Activity · Default sort",
|
|
@@ -518,6 +523,8 @@ window.__ModuleLoader__.load({
|
|
|
518
523
|
"trend.focus": "Showing {cat} only",
|
|
519
524
|
"trend.adaptive": "Adaptive",
|
|
520
525
|
"trend.adaptiveHint": "Rescale bar heights to the visible range, following the scroll",
|
|
526
|
+
"trend.duration": "Duration",
|
|
527
|
+
"trend.durationTip": "Overlay each step’s active time (step start to end — model and tool calls, excluding waits for your decisions or answers) as a curve over the bars, read off the quartile axis on the chart’s right; turn granularity sums a turn’s steps",
|
|
521
528
|
"trend.dna": "DNA Mode",
|
|
522
529
|
"trend.dnaTip": "Draw each bar as that request’s context fingerprint — one band per item in the order the model reads it; with Delta, each band shows that item’s change against the previous bar. Hover for the item, click to reveal it in the Context browser, and every bar lights the same item’s lifetime",
|
|
523
530
|
"trend.dnaItem": "{label}: {n}",
|
|
@@ -653,6 +660,7 @@ window.__ModuleLoader__.load({
|
|
|
653
660
|
"tip.total": "Total ≈ {n}",
|
|
654
661
|
"tip.cat": "{cat} ≈ {n}",
|
|
655
662
|
"tip.delta": "Δ {n}",
|
|
663
|
+
"tip.duration": "Duration {n}",
|
|
656
664
|
"ev.compaction": "Context compacted (summary replaced {n} messages)",
|
|
657
665
|
"ev.prune": "Tool output pruned",
|
|
658
666
|
"ev.skill": "Skill injected ({name})",
|
|
@@ -2201,9 +2209,9 @@ window.__ModuleLoader__.load({
|
|
|
2201
2209
|
* The no-stale-content guarantees: the store is per session and shared by
|
|
2202
2210
|
* the tab and the modal; a refetch is single-flight with a trailing edge
|
|
2203
2211
|
* (a rev bumped mid-flight re-reads after settle); responses race-safe by
|
|
2204
|
-
* revision (
|
|
2205
|
-
*
|
|
2206
|
-
*
|
|
2212
|
+
* generation and revision (a host refold invalidates reads started before
|
|
2213
|
+
* its revisions restarted, and latest wins within a generation); a
|
|
2214
|
+
* transport failure keeps the last good
|
|
2207
2215
|
* detail and backs off; an absent answer (the session left the live set)
|
|
2208
2216
|
* stops the trailing until the head moves again. With no detail at all,
|
|
2209
2217
|
* failure surfaces as a retryable note on the detail cards instead of an
|
|
@@ -2292,6 +2300,7 @@ window.__ModuleLoader__.load({
|
|
|
2292
2300
|
targetRev = -1;
|
|
2293
2301
|
/** The head rev seen last — a DECREASE means the host refolded (revisions restart). */
|
|
2294
2302
|
lastHeadRev = -1;
|
|
2303
|
+
generation = 0;
|
|
2295
2304
|
failed = false;
|
|
2296
2305
|
inFlight = false;
|
|
2297
2306
|
timer = null;
|
|
@@ -2315,6 +2324,7 @@ window.__ModuleLoader__.load({
|
|
|
2315
2324
|
*/
|
|
2316
2325
|
request(rev) {
|
|
2317
2326
|
if (rev < this.lastHeadRev) {
|
|
2327
|
+
this.generation++;
|
|
2318
2328
|
this.acceptedRev = -1;
|
|
2319
2329
|
this.wantedRev = -1;
|
|
2320
2330
|
this.detail = null;
|
|
@@ -2352,9 +2362,11 @@ window.__ModuleLoader__.load({
|
|
|
2352
2362
|
}
|
|
2353
2363
|
this.inFlight = true;
|
|
2354
2364
|
this.targetRev = this.wantedRev;
|
|
2365
|
+
const generation = this.generation;
|
|
2355
2366
|
this.emit();
|
|
2356
2367
|
try {
|
|
2357
2368
|
const d = await this.fetcher();
|
|
2369
|
+
if (generation !== this.generation) return;
|
|
2358
2370
|
if (d !== null) {
|
|
2359
2371
|
if (d.rev >= this.acceptedRev || d.rev === this.targetRev) {
|
|
2360
2372
|
this.detail = d;
|
|
@@ -2368,12 +2380,15 @@ window.__ModuleLoader__.load({
|
|
|
2368
2380
|
this.failures++;
|
|
2369
2381
|
}
|
|
2370
2382
|
} catch {
|
|
2371
|
-
|
|
2372
|
-
|
|
2383
|
+
if (generation === this.generation) {
|
|
2384
|
+
this.failed = this.detail === null;
|
|
2385
|
+
this.failures++;
|
|
2386
|
+
}
|
|
2387
|
+
} finally {
|
|
2388
|
+
this.inFlight = false;
|
|
2389
|
+
if (this.wantedRev > this.acceptedRev) this.schedule();
|
|
2390
|
+
this.emit();
|
|
2373
2391
|
}
|
|
2374
|
-
this.inFlight = false;
|
|
2375
|
-
if (this.wantedRev > this.acceptedRev) this.schedule();
|
|
2376
|
-
this.emit();
|
|
2377
2392
|
}
|
|
2378
2393
|
emit() {
|
|
2379
2394
|
const pending = this.timer !== null || this.inFlight;
|
|
@@ -3003,21 +3018,31 @@ window.__ModuleLoader__.load({
|
|
|
3003
3018
|
*/
|
|
3004
3019
|
/**
|
|
3005
3020
|
* 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
|
|
3006
|
-
* column width; the log keeps one turn's requests consecutive, so a run of equal turns collapses to its final record.
|
|
3021
|
+
* column width; the log keeps one turn's requests consecutive, so a run of equal turns collapses to its final record. The duration
|
|
3022
|
+
* overlay's `activeMs` is the run's steps SUMMED (the turn's total step active time), absent only when no step of the run carried one.
|
|
3007
3023
|
*/
|
|
3008
3024
|
function aggregateByTurn(requests) {
|
|
3009
3025
|
const out = [];
|
|
3010
3026
|
let runSteps = 0;
|
|
3027
|
+
let runActiveMs = 0;
|
|
3028
|
+
let runHasActiveMs = false;
|
|
3011
3029
|
for (const req of requests) {
|
|
3012
3030
|
const last = out.length > 0 ? out[out.length - 1] : null;
|
|
3013
3031
|
if (last !== null && (last.turn ?? 0) === (req.turn ?? 0)) {
|
|
3014
3032
|
runSteps++;
|
|
3033
|
+
if (req.activeMs !== void 0) {
|
|
3034
|
+
runActiveMs += req.activeMs;
|
|
3035
|
+
runHasActiveMs = true;
|
|
3036
|
+
}
|
|
3015
3037
|
out[out.length - 1] = {
|
|
3016
3038
|
...req,
|
|
3017
|
-
stepCount: runSteps
|
|
3039
|
+
stepCount: runSteps,
|
|
3040
|
+
...runHasActiveMs ? { activeMs: runActiveMs } : {}
|
|
3018
3041
|
};
|
|
3019
3042
|
} else {
|
|
3020
3043
|
runSteps = 1;
|
|
3044
|
+
runActiveMs = req.activeMs ?? 0;
|
|
3045
|
+
runHasActiveMs = req.activeMs !== void 0;
|
|
3021
3046
|
out.push({
|
|
3022
3047
|
...req,
|
|
3023
3048
|
stepCount: 1
|
|
@@ -3064,7 +3089,7 @@ window.__ModuleLoader__.load({
|
|
|
3064
3089
|
return requests.length > 0 ? requests[0] : null;
|
|
3065
3090
|
}
|
|
3066
3091
|
function makeTrendChart(kit) {
|
|
3067
|
-
const { t, fmt, eventLabel, eventAt, catLabel } = kit;
|
|
3092
|
+
const { t, fmt, fmtDuration, eventLabel, eventAt, catLabel } = kit;
|
|
3068
3093
|
const CHART_H = 112;
|
|
3069
3094
|
const fmtSigned = (v) => (v > 0 ? "+" : "") + fmt(v);
|
|
3070
3095
|
const BAR_W = 14;
|
|
@@ -3335,6 +3360,7 @@ window.__ModuleLoader__.load({
|
|
|
3335
3360
|
const dnaOn = dnaBands !== null;
|
|
3336
3361
|
const delta = props.mode === "delta";
|
|
3337
3362
|
const dnaDeltaOn = dnaOn && delta;
|
|
3363
|
+
const durationOn = props.durationCurve === true;
|
|
3338
3364
|
const focus = !dnaOn && props.focusCat !== null && props.focusCat !== void 0 && CATS.some((c) => c.key === props.focusCat) ? props.focusCat : null;
|
|
3339
3365
|
const [dnaHit, setDnaHit] = (0, react.useState)(null);
|
|
3340
3366
|
const dnaDeltas = (0, react.useMemo)(() => dnaDeltaOn ? dnaBands.map((bands, i) => deltaBandsOf(bands, i > 0 ? dnaBands[i - 1] : null)) : null, [dnaDeltaOn, dnaBands]);
|
|
@@ -3359,12 +3385,15 @@ window.__ModuleLoader__.load({
|
|
|
3359
3385
|
let total = 0;
|
|
3360
3386
|
let up = 0;
|
|
3361
3387
|
let down = 0;
|
|
3388
|
+
let activeMs = 0;
|
|
3362
3389
|
const from = Math.max(0, Math.floor(sl / pitch));
|
|
3363
3390
|
const to = Math.min(n - 1, Math.max(from, Math.floor((vr - 1) / pitch)));
|
|
3364
3391
|
for (let i = from; i <= to; i++) {
|
|
3365
3392
|
const col = i * pitch;
|
|
3366
3393
|
if (col >= vr || col + BAR_W <= sl) continue;
|
|
3367
3394
|
const req = requests[i];
|
|
3395
|
+
const m = req.activeMs ?? 0;
|
|
3396
|
+
if (m > activeMs) activeMs = m;
|
|
3368
3397
|
if (dnaDeltaOn && dnaDeltas !== null) {
|
|
3369
3398
|
let bu = 0;
|
|
3370
3399
|
let bd = 0;
|
|
@@ -3384,10 +3413,11 @@ window.__ModuleLoader__.load({
|
|
|
3384
3413
|
if (bd > down) down = bd;
|
|
3385
3414
|
} else if (req.total > total) total = req.total;
|
|
3386
3415
|
}
|
|
3387
|
-
setVisMax((prev) => prev !== null && prev.total === total && prev.up === up && prev.down === down ? prev : {
|
|
3416
|
+
setVisMax((prev) => prev !== null && prev.total === total && prev.up === up && prev.down === down && prev.activeMs === activeMs ? prev : {
|
|
3388
3417
|
total,
|
|
3389
3418
|
up,
|
|
3390
|
-
down
|
|
3419
|
+
down,
|
|
3420
|
+
activeMs
|
|
3391
3421
|
});
|
|
3392
3422
|
};
|
|
3393
3423
|
let maxTotal = 1;
|
|
@@ -3421,6 +3451,34 @@ window.__ModuleLoader__.load({
|
|
|
3421
3451
|
}
|
|
3422
3452
|
} else maxTotal = Math.max(1, visMax.total);
|
|
3423
3453
|
}
|
|
3454
|
+
let maxActiveMs = 0;
|
|
3455
|
+
if (durationOn) {
|
|
3456
|
+
for (const req of requests) {
|
|
3457
|
+
const m = req.activeMs ?? 0;
|
|
3458
|
+
if (m > maxActiveMs) maxActiveMs = m;
|
|
3459
|
+
}
|
|
3460
|
+
if (adaptive && visMax !== null) maxActiveMs = visMax.activeMs;
|
|
3461
|
+
}
|
|
3462
|
+
const durationRuns = [];
|
|
3463
|
+
const durationDots = [];
|
|
3464
|
+
if (durationOn) {
|
|
3465
|
+
const scale = CHART_H / Math.max(1, maxActiveMs);
|
|
3466
|
+
let run = [];
|
|
3467
|
+
const flushRun = () => {
|
|
3468
|
+
if (run.length >= 2) durationRuns.push(run.map((p) => `${p[0]},${p[1]}`).join(" "));
|
|
3469
|
+
else if (run.length === 1) durationDots.push(run[0]);
|
|
3470
|
+
run = [];
|
|
3471
|
+
};
|
|
3472
|
+
for (let i = 0; i < requests.length; i++) {
|
|
3473
|
+
const m = requests[i].activeMs;
|
|
3474
|
+
if (m === void 0) {
|
|
3475
|
+
flushRun();
|
|
3476
|
+
continue;
|
|
3477
|
+
}
|
|
3478
|
+
run.push([i * 16 + BAR_W / 2, Math.round((CHART_H - m * scale) * 100) / 100]);
|
|
3479
|
+
}
|
|
3480
|
+
flushRun();
|
|
3481
|
+
}
|
|
3424
3482
|
const span = Math.max(1, maxUp + maxDown);
|
|
3425
3483
|
const deltaScale = CHART_H / span;
|
|
3426
3484
|
const upPx = Math.round(maxUp * deltaScale);
|
|
@@ -3624,18 +3682,22 @@ window.__ModuleLoader__.load({
|
|
|
3624
3682
|
n: stepsOf(req.turn)
|
|
3625
3683
|
});
|
|
3626
3684
|
const band = hitBand ?? hoveredDelta;
|
|
3627
|
-
|
|
3685
|
+
let metric;
|
|
3686
|
+
if (band !== null) metric = t("trend.dnaItem", {
|
|
3628
3687
|
label: dnaBaseLabel(band, t, catLabel),
|
|
3629
3688
|
n: dnaDeltaOn ? fmtSigned(band.tokens) : fmt(band.tokens)
|
|
3630
|
-
})
|
|
3631
|
-
if (delta)
|
|
3689
|
+
});
|
|
3690
|
+
else if (delta)
|
|
3632
3691
|
/* v8 ignore next 1 -- delta mode only receives records from
|
|
3633
3692
|
deltaOf, which always assigns net; the fallback is defensive. */
|
|
3634
|
-
|
|
3635
|
-
|
|
3693
|
+
metric = t("tip.delta", { n: fmtSigned(req.net ?? 0) });
|
|
3694
|
+
else metric = focus !== null ? t("tip.cat", {
|
|
3636
3695
|
cat: catLabel(focus),
|
|
3637
3696
|
n: fmt(req.total)
|
|
3638
|
-
}) : t("tip.total", { n: fmt(req.total) })
|
|
3697
|
+
}) : t("tip.total", { n: fmt(req.total) });
|
|
3698
|
+
const rows = [head, metric];
|
|
3699
|
+
if (durationOn && req.activeMs !== void 0) rows.push(t("tip.duration", { n: fmtDuration(req.activeMs) }));
|
|
3700
|
+
return rows;
|
|
3639
3701
|
};
|
|
3640
3702
|
const tipColRef = (0, react.useRef)(0);
|
|
3641
3703
|
/**
|
|
@@ -3665,134 +3727,173 @@ window.__ModuleLoader__.load({
|
|
|
3665
3727
|
});
|
|
3666
3728
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3667
3729
|
className: "lc-chartrow",
|
|
3668
|
-
children: [
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
className: "lc-chart-
|
|
3718
|
-
ref: scrollRef,
|
|
3719
|
-
onScroll: (e) => {
|
|
3720
|
-
updateTurnLabels(e.currentTarget);
|
|
3721
|
-
syncTip(e.currentTarget);
|
|
3722
|
-
measureVisible(e.currentTarget);
|
|
3723
|
-
},
|
|
3730
|
+
children: [
|
|
3731
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3732
|
+
className: "lc-axis",
|
|
3733
|
+
children: delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3734
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3735
|
+
className: "lc-axis-top",
|
|
3736
|
+
children: fmtSigned(maxUp)
|
|
3737
|
+
}),
|
|
3738
|
+
q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3739
|
+
className: "lc-axis-q3",
|
|
3740
|
+
children: fmtSigned(Math.round(maxUp - span / 4))
|
|
3741
|
+
}) : null,
|
|
3742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3743
|
+
className: "lc-axis-mid",
|
|
3744
|
+
style: { top: `${13 + upPx}px` },
|
|
3745
|
+
children: "0"
|
|
3746
|
+
}),
|
|
3747
|
+
q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3748
|
+
className: "lc-axis-q1",
|
|
3749
|
+
children: fmtSigned(Math.round(maxUp - 3 * span / 4))
|
|
3750
|
+
}) : null,
|
|
3751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3752
|
+
className: "lc-axis-bot",
|
|
3753
|
+
children: fmtSigned(-maxDown)
|
|
3754
|
+
})
|
|
3755
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
3756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3757
|
+
className: "lc-axis-top",
|
|
3758
|
+
children: fmt(maxTotal)
|
|
3759
|
+
}),
|
|
3760
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3761
|
+
className: "lc-axis-q3",
|
|
3762
|
+
children: fmt(Math.round(maxTotal * 3 / 4))
|
|
3763
|
+
}),
|
|
3764
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3765
|
+
className: "lc-axis-mid",
|
|
3766
|
+
children: fmt(Math.round(maxTotal / 2))
|
|
3767
|
+
}),
|
|
3768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3769
|
+
className: "lc-axis-q1",
|
|
3770
|
+
children: fmt(Math.round(maxTotal / 4))
|
|
3771
|
+
}),
|
|
3772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3773
|
+
className: "lc-axis-bot",
|
|
3774
|
+
children: "0"
|
|
3775
|
+
})
|
|
3776
|
+
] })
|
|
3777
|
+
}),
|
|
3778
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3779
|
+
className: "lc-chart-wrap",
|
|
3724
3780
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3725
|
-
className: "lc-chart",
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
children: [
|
|
3732
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-top" }),
|
|
3733
|
-
!delta || q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q3" }) : null,
|
|
3734
|
-
!delta || q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q1" }) : null,
|
|
3735
|
-
!delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-mid" }) : null,
|
|
3736
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3737
|
-
className: "lc-grid lc-grid-zero",
|
|
3738
|
-
style: delta ? { top: `${18 + upPx}px` } : void 0
|
|
3739
|
-
}),
|
|
3740
|
-
requests.map((req, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartBar, {
|
|
3741
|
-
req,
|
|
3742
|
-
marker: markers[i],
|
|
3743
|
-
selected: props.selectedSeq === req.seq,
|
|
3744
|
-
hovered: props.hoveredSeq === req.seq,
|
|
3745
|
-
inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
|
|
3746
|
-
flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
|
|
3747
|
-
maxTotal,
|
|
3748
|
-
upPx: delta ? upPx : void 0,
|
|
3749
|
-
downPx: delta ? downPx : void 0,
|
|
3750
|
-
deltaScale: delta ? deltaScale : void 0,
|
|
3751
|
-
enterIndex: i,
|
|
3752
|
-
dna: dnaOn ? dnaBands[i] : null,
|
|
3753
|
-
dnaDelta: dnaDeltaOn && dnaDeltas !== null ? dnaDeltas[i] : null,
|
|
3754
|
-
dnaScale: dnaDeltaOn ? deltaScale : void 0,
|
|
3755
|
-
zeroBottom: dnaDeltaOn ? downPx : void 0,
|
|
3756
|
-
onDnaHit: setDnaHit,
|
|
3757
|
-
onPickBand: props.onPickBand,
|
|
3758
|
-
onSelect: props.onSelect,
|
|
3759
|
-
onHover: props.onHover
|
|
3760
|
-
}, `${req.seq}:${props.granularity}`)),
|
|
3761
|
-
dnaOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DnaHighlights, { slices: dnaSlices }) : null
|
|
3762
|
-
]
|
|
3763
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3764
|
-
className: "lc-turns",
|
|
3765
|
-
style: labelFont !== "" ? { fontSize: labelFont } : void 0,
|
|
3766
|
-
onMouseLeave: () => {
|
|
3767
|
-
props.onHoverTurn(null);
|
|
3781
|
+
className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
|
|
3782
|
+
ref: scrollRef,
|
|
3783
|
+
onScroll: (e) => {
|
|
3784
|
+
updateTurnLabels(e.currentTarget);
|
|
3785
|
+
syncTip(e.currentTarget);
|
|
3786
|
+
measureVisible(e.currentTarget);
|
|
3768
3787
|
},
|
|
3769
|
-
children:
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
},
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3788
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3789
|
+
className: "lc-chart",
|
|
3790
|
+
"data-catdim": props.hoverCat ?? void 0,
|
|
3791
|
+
onMouseLeave: () => {
|
|
3792
|
+
props.onHover(null);
|
|
3793
|
+
setDnaHit(null);
|
|
3794
|
+
},
|
|
3795
|
+
children: [
|
|
3796
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-top" }),
|
|
3797
|
+
!delta || q3Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q3" }) : null,
|
|
3798
|
+
!delta || q1Clear ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-q1" }) : null,
|
|
3799
|
+
!delta ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "lc-grid lc-grid-mid" }) : null,
|
|
3800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3801
|
+
className: "lc-grid lc-grid-zero",
|
|
3802
|
+
style: delta ? { top: `${18 + upPx}px` } : void 0
|
|
3803
|
+
}),
|
|
3804
|
+
requests.map((req, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChartBar, {
|
|
3805
|
+
req,
|
|
3806
|
+
marker: markers[i],
|
|
3807
|
+
selected: props.selectedSeq === req.seq,
|
|
3808
|
+
hovered: props.hoveredSeq === req.seq,
|
|
3809
|
+
inTurn: props.activeTurn !== null && (req.turn ?? 0) === props.activeTurn,
|
|
3810
|
+
flag: props.granularity === "step" && (i + 1) % STEP_FLAG_EVERY === 0 ? i + 1 : null,
|
|
3811
|
+
maxTotal,
|
|
3812
|
+
upPx: delta ? upPx : void 0,
|
|
3813
|
+
downPx: delta ? downPx : void 0,
|
|
3814
|
+
deltaScale: delta ? deltaScale : void 0,
|
|
3815
|
+
enterIndex: i,
|
|
3816
|
+
dna: dnaOn ? dnaBands[i] : null,
|
|
3817
|
+
dnaDelta: dnaDeltaOn && dnaDeltas !== null ? dnaDeltas[i] : null,
|
|
3818
|
+
dnaScale: dnaDeltaOn ? deltaScale : void 0,
|
|
3819
|
+
zeroBottom: dnaDeltaOn ? downPx : void 0,
|
|
3820
|
+
onDnaHit: setDnaHit,
|
|
3821
|
+
onPickBand: props.onPickBand,
|
|
3822
|
+
onSelect: props.onSelect,
|
|
3823
|
+
onHover: props.onHover
|
|
3824
|
+
}, `${req.seq}:${props.granularity}`)),
|
|
3825
|
+
dnaOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DnaHighlights, { slices: dnaSlices }) : null,
|
|
3826
|
+
durationOn && (durationRuns.length > 0 || durationDots.length > 0) ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
3827
|
+
className: "lc-duration",
|
|
3828
|
+
width: requests.length * 16 - BAR_GAP,
|
|
3829
|
+
height: CHART_H,
|
|
3830
|
+
"aria-hidden": "true",
|
|
3831
|
+
children: [durationRuns.map((pts, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polyline", { points: pts }, i)), durationDots.map((p, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
3832
|
+
cx: p[0],
|
|
3833
|
+
cy: p[1],
|
|
3834
|
+
r: 1.5
|
|
3835
|
+
}, i))]
|
|
3836
|
+
}) : null
|
|
3837
|
+
]
|
|
3838
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3839
|
+
className: "lc-turns",
|
|
3840
|
+
style: labelFont !== "" ? { fontSize: labelFont } : void 0,
|
|
3841
|
+
onMouseLeave: () => {
|
|
3842
|
+
props.onHoverTurn(null);
|
|
3843
|
+
},
|
|
3844
|
+
children: groups.map((grp, gi) => {
|
|
3845
|
+
const on = props.activeTurn === grp.turn;
|
|
3846
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3847
|
+
className: "lc-turn" + (on ? " lc-turn-on" : ""),
|
|
3848
|
+
style: {
|
|
3849
|
+
width: `${turnWidths[gi]}px`,
|
|
3850
|
+
background: TURN_FILLS[gi % TURN_FILLS.length]
|
|
3851
|
+
},
|
|
3852
|
+
title: `T${grp.turn}`,
|
|
3853
|
+
onMouseEnter: () => {
|
|
3854
|
+
props.onHoverTurn(grp.turn);
|
|
3855
|
+
},
|
|
3856
|
+
onClick: () => {
|
|
3857
|
+
props.onPickTurn(grp.turn);
|
|
3858
|
+
},
|
|
3859
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3860
|
+
className: "lc-turn-label",
|
|
3861
|
+
children: `${grp.turn}`
|
|
3862
|
+
})
|
|
3863
|
+
}, `turn-${gi}`);
|
|
3864
|
+
})
|
|
3865
|
+
})]
|
|
3866
|
+
}), hoveredReq !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3867
|
+
className: "lc-chart-tip",
|
|
3868
|
+
children: tipRowsOf(hoveredReq).map((row, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row }, i))
|
|
3869
|
+
}) : null]
|
|
3870
|
+
}),
|
|
3871
|
+
durationOn ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3872
|
+
className: "lc-axis lc-axis-r",
|
|
3873
|
+
children: [
|
|
3874
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3875
|
+
className: "lc-axis-top",
|
|
3876
|
+
children: fmtDuration(maxActiveMs)
|
|
3877
|
+
}),
|
|
3878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3879
|
+
className: "lc-axis-q3",
|
|
3880
|
+
children: fmtDuration(Math.round(maxActiveMs * 3 / 4))
|
|
3881
|
+
}),
|
|
3882
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3883
|
+
className: "lc-axis-mid",
|
|
3884
|
+
children: fmtDuration(Math.round(maxActiveMs / 2))
|
|
3885
|
+
}),
|
|
3886
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3887
|
+
className: "lc-axis-q1",
|
|
3888
|
+
children: fmtDuration(Math.round(maxActiveMs / 4))
|
|
3889
|
+
}),
|
|
3890
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3891
|
+
className: "lc-axis-bot",
|
|
3892
|
+
children: "0"
|
|
3789
3893
|
})
|
|
3790
|
-
|
|
3791
|
-
})
|
|
3792
|
-
|
|
3793
|
-
children: tipRowsOf(hoveredReq).map((row, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: row }, i))
|
|
3794
|
-
}) : null]
|
|
3795
|
-
})]
|
|
3894
|
+
]
|
|
3895
|
+
}) : null
|
|
3896
|
+
]
|
|
3796
3897
|
});
|
|
3797
3898
|
};
|
|
3798
3899
|
}
|
|
@@ -8915,7 +9016,7 @@ window.__ModuleLoader__.load({
|
|
|
8915
9016
|
//#endregion
|
|
8916
9017
|
//#region src/client/components/settingsCard.tsx
|
|
8917
9018
|
/**
|
|
8918
|
-
* The dsh-context preference cards — two seats over the same
|
|
9019
|
+
* The dsh-context preference cards — two seats over the same eight rows. The
|
|
8919
9020
|
* settings-section card (`settings.plugin.item`, the older harness lines)
|
|
8920
9021
|
* renders a collapsible list item in Settings → Plugins → Plugin
|
|
8921
9022
|
* configuration; the Plugins-page card (`plugins.bundle.config`, the
|
|
@@ -9034,6 +9135,21 @@ window.__ModuleLoader__.load({
|
|
|
9034
9135
|
set?.("defaultTrendMode", id);
|
|
9035
9136
|
}
|
|
9036
9137
|
}),
|
|
9138
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
|
|
9139
|
+
label: t("settings.durationCurve"),
|
|
9140
|
+
value: state.durationCurve,
|
|
9141
|
+
disabled,
|
|
9142
|
+
options: [{
|
|
9143
|
+
id: "show",
|
|
9144
|
+
label: t("insightsEntry.show")
|
|
9145
|
+
}, {
|
|
9146
|
+
id: "hide",
|
|
9147
|
+
label: t("insightsEntry.hide")
|
|
9148
|
+
}],
|
|
9149
|
+
onPick: (id) => {
|
|
9150
|
+
set?.("defaultDurationCurve", id);
|
|
9151
|
+
}
|
|
9152
|
+
}),
|
|
9037
9153
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PrefRow, {
|
|
9038
9154
|
label: t("settings.deltaBase"),
|
|
9039
9155
|
value: state.deltaBase,
|
|
@@ -9182,7 +9298,8 @@ window.__ModuleLoader__.load({
|
|
|
9182
9298
|
...v.defaultDeltaBase === "step" || v.defaultDeltaBase === "turn" ? { deltaBase: v.defaultDeltaBase } : {},
|
|
9183
9299
|
...v.defaultToolSort === "size" || v.defaultToolSort === "count" || v.defaultToolSort === "name" ? { toolSort: v.defaultToolSort } : {},
|
|
9184
9300
|
...v.defaultFileSort === "count" || v.defaultFileSort === "latest" || v.defaultFileSort === "path" ? { fileSort: v.defaultFileSort } : {},
|
|
9185
|
-
...v.insightsEntry === "show" || v.insightsEntry === "hide" ? { insightsEntry: v.insightsEntry } : {}
|
|
9301
|
+
...v.insightsEntry === "show" || v.insightsEntry === "hide" ? { insightsEntry: v.insightsEntry } : {},
|
|
9302
|
+
...v.defaultDurationCurve === "show" || v.defaultDurationCurve === "hide" ? { durationCurve: v.defaultDurationCurve } : {}
|
|
9186
9303
|
};
|
|
9187
9304
|
}
|
|
9188
9305
|
function createContextSettings() {
|
|
@@ -9195,12 +9312,13 @@ window.__ModuleLoader__.load({
|
|
|
9195
9312
|
toolSort: "count",
|
|
9196
9313
|
fileSort: "count",
|
|
9197
9314
|
insightsEntry: "show",
|
|
9315
|
+
durationCurve: "show",
|
|
9198
9316
|
writable: false
|
|
9199
9317
|
};
|
|
9200
9318
|
let scope;
|
|
9201
9319
|
const listeners = /* @__PURE__ */ new Set();
|
|
9202
9320
|
const publish = (next) => {
|
|
9203
|
-
if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.deltaBase === state.deltaBase && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.insightsEntry === state.insightsEntry && next.writable === state.writable) return;
|
|
9321
|
+
if (next.status === state.status && next.placement === state.placement && next.granularity === state.granularity && next.mode === state.mode && next.deltaBase === state.deltaBase && next.toolSort === state.toolSort && next.fileSort === state.fileSort && next.insightsEntry === state.insightsEntry && next.durationCurve === state.durationCurve && next.writable === state.writable) return;
|
|
9204
9322
|
state = next;
|
|
9205
9323
|
for (const listener of listeners) listener();
|
|
9206
9324
|
};
|
|
@@ -9217,6 +9335,7 @@ window.__ModuleLoader__.load({
|
|
|
9217
9335
|
toolSort: prefs.toolSort ?? state.toolSort,
|
|
9218
9336
|
fileSort: prefs.fileSort ?? state.fileSort,
|
|
9219
9337
|
insightsEntry: prefs.insightsEntry ?? (raw?.insightsEntry === void 0 ? state.insightsEntry : "show"),
|
|
9338
|
+
durationCurve: prefs.durationCurve ?? (raw?.defaultDurationCurve === void 0 ? state.durationCurve : "show"),
|
|
9220
9339
|
writable: snap.writable
|
|
9221
9340
|
});
|
|
9222
9341
|
return {
|
|
@@ -9241,6 +9360,7 @@ window.__ModuleLoader__.load({
|
|
|
9241
9360
|
defaultToolSort: () => state.toolSort,
|
|
9242
9361
|
defaultFileSort: () => state.fileSort,
|
|
9243
9362
|
insightsEntry: () => state.insightsEntry,
|
|
9363
|
+
defaultDurationCurve: () => state.durationCurve,
|
|
9244
9364
|
attach(bound) {
|
|
9245
9365
|
scope = bound;
|
|
9246
9366
|
sync(bound);
|
|
@@ -11404,7 +11524,7 @@ window.__ModuleLoader__.load({
|
|
|
11404
11524
|
return function PluginInfo() {
|
|
11405
11525
|
const [latest, setLatest] = (0, react.useState)(null);
|
|
11406
11526
|
(0, react.useEffect)(() => {
|
|
11407
|
-
if ("0.
|
|
11527
|
+
if ("0.63.0".includes("-dev")) return;
|
|
11408
11528
|
let on = true;
|
|
11409
11529
|
fetchLatestVersion().then((v) => {
|
|
11410
11530
|
if (on && v) setLatest(v);
|
|
@@ -11413,8 +11533,8 @@ window.__ModuleLoader__.load({
|
|
|
11413
11533
|
on = false;
|
|
11414
11534
|
};
|
|
11415
11535
|
}, []);
|
|
11416
|
-
const update = latest !== null && isNewerVersion(latest, "0.
|
|
11417
|
-
const nameText = "dsh-context (v0.
|
|
11536
|
+
const update = latest !== null && isNewerVersion(latest, "0.63.0") ? latest : null;
|
|
11537
|
+
const nameText = "dsh-context (v0.63.0)";
|
|
11418
11538
|
const nameValue = [nameText];
|
|
11419
11539
|
if (update) nameValue.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11420
11540
|
className: "lc-pi-update",
|
|
@@ -12379,6 +12499,7 @@ window.__ModuleLoader__.load({
|
|
|
12379
12499
|
const [trendMode, setTrendMode] = (0, react.useState)(() => settings.defaultTrendMode());
|
|
12380
12500
|
const [dna, setDna] = (0, react.useState)(false);
|
|
12381
12501
|
const [adaptive, setAdaptive] = (0, react.useState)(false);
|
|
12502
|
+
const [durationCurve, setDurationCurve] = (0, react.useState)(() => settings.defaultDurationCurve() === "show");
|
|
12382
12503
|
const [focusTurn, setFocusTurn] = (0, react.useState)(null);
|
|
12383
12504
|
const [jumpSeq, setJumpSeq] = (0, react.useState)(null);
|
|
12384
12505
|
const [hoverCat, setHoverCat] = (0, react.useState)(null);
|
|
@@ -12645,6 +12766,19 @@ window.__ModuleLoader__.load({
|
|
|
12645
12766
|
children: t("trend.adaptive")
|
|
12646
12767
|
})
|
|
12647
12768
|
}),
|
|
12769
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
12770
|
+
className: "lc-gran lc-trend-duration",
|
|
12771
|
+
role: "group",
|
|
12772
|
+
title: t("trend.durationTip"),
|
|
12773
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
12774
|
+
type: "button",
|
|
12775
|
+
className: "lc-gran-btn" + (durationCurve ? " lc-gran-on" : ""),
|
|
12776
|
+
onClick: () => {
|
|
12777
|
+
setDurationCurve((on) => !on);
|
|
12778
|
+
},
|
|
12779
|
+
children: t("trend.duration")
|
|
12780
|
+
})
|
|
12781
|
+
}),
|
|
12648
12782
|
focusCat !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
12649
12783
|
className: "lc-card-sub",
|
|
12650
12784
|
children: t("trend.focus", { cat: kit.catLabel(focusCat) })
|
|
@@ -12703,6 +12837,7 @@ window.__ModuleLoader__.load({
|
|
|
12703
12837
|
hoverCat: trendHoverCat,
|
|
12704
12838
|
focusCat,
|
|
12705
12839
|
adaptive,
|
|
12840
|
+
durationCurve,
|
|
12706
12841
|
dna: dnaBands,
|
|
12707
12842
|
onPickBand: revealBand,
|
|
12708
12843
|
onSelect: setSelectedSeq,
|
|
@@ -13060,7 +13195,7 @@ window.__ModuleLoader__.load({
|
|
|
13060
13195
|
}
|
|
13061
13196
|
//#endregion
|
|
13062
13197
|
//#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles/trendChart.css.mjs
|
|
13063
|
-
const css$9 = ".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:18px 2px 0;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:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);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-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:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-dna{border-radius:2px;width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down,.lc-bar-dna{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 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=skill] .lc-cat-seg[data-cat=skill],.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-dna-hl{z-index:1;pointer-events:none;align-items:stretch;gap:2px;display:flex;position:absolute;inset:18px 2px 0}.lc-dna-cell{flex:none;width:14px;position:relative}.lc-dna-slice{box-sizing:border-box;border:1px solid var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);border-radius:1px;width:100%;position:absolute;left:0}.lc-bar-marker{z-index:2;color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{z-index:2;background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;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}";
|
|
13198
|
+
const css$9 = ".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-r{flex:none;width:34px}.lc-axis-r span{left:0;right:auto}.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:18px 2px 0;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:color-mix(in srgb, var(--color-neutral-500) 50%, transparent);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-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:color-mix(in srgb, var(--color-neutral-500) 14%, transparent)}.lc-bar-stack{flex-direction:column-reverse;width:100%;display:flex}.lc-bar-stack>div{width:100%}.lc-bar-dna{border-radius:2px;width:100%}.lc-bar-stack,.lc-bar-up,.lc-bar-down,.lc-bar-dna{transform-origin:50% 100%}.lc-bar-down{transform-origin:50% 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=skill] .lc-cat-seg[data-cat=skill],.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-dna-hl{z-index:1;pointer-events:none;align-items:stretch;gap:2px;display:flex;position:absolute;inset:18px 2px 0}.lc-dna-cell{flex:none;width:14px;position:relative}.lc-dna-slice{box-sizing:border-box;border:1px solid var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 26%, transparent);border-radius:1px;width:100%;position:absolute;left:0}.lc-duration{z-index:1;pointer-events:none;position:absolute;top:18px;left:2px;overflow:visible}.lc-duration polyline,.lc-duration circle{fill:none;stroke:var(--dsw-alias-brand-primary);stroke-width:1.5px;stroke-linejoin:round;stroke-linecap:round}.lc-duration circle{fill:var(--dsw-alias-brand-primary)}.lc-chart-dim .lc-duration{opacity:.35}.lc-bar-marker{z-index:2;color:var(--dsw-alias-state-warn-primary);font-size:11px;position:absolute;top:-16px;left:50%;transform:translate(-50%)}.lc-step-flag{z-index:2;background:color-mix(in srgb, var(--color-neutral-500) 45%, transparent);pointer-events:none;width:1px;position:absolute;top:-17px;bottom:0;right:-1px}.lc-step-flag-label{box-sizing:border-box;border:1px solid color-mix(in srgb, var(--color-neutral-500) 45%, transparent);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:3px;padding:1px 3px;font-size:9px;font-weight:600;line-height:1;position:absolute;top:0;right:0}.lc-turns{box-sizing:border-box;gap:2px;width:max-content;min-width:100%;margin-top:4px;padding:0 2px;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}";
|
|
13064
13199
|
const tagId$9 = "dsh-context/trendChart.css";
|
|
13065
13200
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$9) + "]") === null) {
|
|
13066
13201
|
const tag = document.createElement("style");
|
package/lib/index.d.ts
CHANGED
|
@@ -157,6 +157,39 @@ interface TimelineState {
|
|
|
157
157
|
time: number;
|
|
158
158
|
firstToken?: number;
|
|
159
159
|
};
|
|
160
|
+
/**
|
|
161
|
+
* The open step's CLOSED user-wait intervals (real log instants), pushed
|
|
162
|
+
* replace-style as each wait settles: an approval decision (its asked →
|
|
163
|
+
* decided pair closes via `stepApprovals`) and an `ask_user_question`
|
|
164
|
+
* call's whole tool window (its body is the answer wait itself, in both
|
|
165
|
+
* the blocking and the timed schema's foreground phase). `step/end`
|
|
166
|
+
* subtracts their clamped union from the step window to price the
|
|
167
|
+
* request's `activeMs`, then DELETES the slot. Same arm/remove lifecycle
|
|
168
|
+
* as `stepStart` — never written in place, so no CloneKey covers it.
|
|
169
|
+
*/
|
|
170
|
+
stepWaits?: {
|
|
171
|
+
start: number;
|
|
172
|
+
end: number;
|
|
173
|
+
}[];
|
|
174
|
+
/**
|
|
175
|
+
* The open step's PENDING approval waits (request id → its `approval/asked`
|
|
176
|
+
* instant), armed only while a step is open and disarmed by the matching
|
|
177
|
+
* `approval/decided`. A crash mid-decision leaves the pair dangling —
|
|
178
|
+
* resume repair appends no synthetic decision — so `step/end` closes the
|
|
179
|
+
* survivors at its own instant. One record, not a map: pending approvals
|
|
180
|
+
* are a handful at most, and the rebuild-on-change copy stays trivial.
|
|
181
|
+
* Deleted at `step/start` (a superseded step's leftovers) and `step/end`.
|
|
182
|
+
*/
|
|
183
|
+
stepApprovals?: Record<string, number>;
|
|
184
|
+
/**
|
|
185
|
+
* The seq of the request record committed by the open step's
|
|
186
|
+
* `assistant/message` — the back-pointer `step/end` stamps `activeMs`
|
|
187
|
+
* onto. It stays the requests TAIL until then (nothing pushes between a
|
|
188
|
+
* step's message and its end, and the turn trim only drops the oldest),
|
|
189
|
+
* so the stamp is an O(1) tail write guarded by the seq. Same arm/remove
|
|
190
|
+
* lifecycle as `stepStart`.
|
|
191
|
+
*/
|
|
192
|
+
stepRequestSeq?: number;
|
|
160
193
|
/**
|
|
161
194
|
* The timing strip's painted spans (shared/types.ts TimingSpan): every
|
|
162
195
|
* completed step's time slices in log order, stamped with their REAL
|
|
@@ -364,6 +397,8 @@ type DefaultToolSort = 'size' | 'count' | 'name';
|
|
|
364
397
|
type DefaultPlacement = 'all' | 'tab' | 'sidebar';
|
|
365
398
|
/** Whether the Context Insights panel's sidebar entry is offered at all. */
|
|
366
399
|
type InsightsEntry = 'show' | 'hide';
|
|
400
|
+
/** Whether the trend chart mounts with the per-request duration curve overlaid. */
|
|
401
|
+
type DefaultDurationCurve = 'show' | 'hide';
|
|
367
402
|
interface Snapshot {
|
|
368
403
|
ok: boolean;
|
|
369
404
|
/**
|
|
@@ -790,6 +825,15 @@ interface RequestRecord {
|
|
|
790
825
|
*/
|
|
791
826
|
cacheRead?: number;
|
|
792
827
|
output?: number;
|
|
828
|
+
/**
|
|
829
|
+
* The request's step ACTIVE milliseconds (`step/start` → `step/end`: the
|
|
830
|
+
* model call plus its tool runs) minus the in-step user waits — approval
|
|
831
|
+
* decisions and `ask_user_question` answer windows — the trend chart's
|
|
832
|
+
* duration overlay. Stamped at `step/end` onto the step's committed record;
|
|
833
|
+
* absent while that step is still open (the live tail) or when the step
|
|
834
|
+
* closed without one (an unpaired/hostile log).
|
|
835
|
+
*/
|
|
836
|
+
activeMs?: number;
|
|
793
837
|
/**
|
|
794
838
|
* Turn-mode aggregate marker, set by the Client's aggregateByTurn (one bar
|
|
795
839
|
* per turn shows its LAST step's record). The Host never sets it.
|
|
@@ -913,6 +957,8 @@ export interface Config {
|
|
|
913
957
|
defaultFileSort?: DefaultFileSort;
|
|
914
958
|
/** Whether the Context Insights panel's sidebar entry is offered. */
|
|
915
959
|
insightsEntry?: InsightsEntry;
|
|
960
|
+
/** Whether the trend chart mounts with the duration curve overlaid. */
|
|
961
|
+
defaultDurationCurve?: DefaultDurationCurve;
|
|
916
962
|
}
|
|
917
963
|
/** The cordis `Config` validator and the Config-form generation's served schema. */
|
|
918
964
|
export declare const Config: z$1<Schemastery.ObjectS<NoInfer<{
|
|
@@ -929,6 +975,7 @@ export declare const Config: z$1<Schemastery.ObjectS<NoInfer<{
|
|
|
929
975
|
defaultToolSort: z$1<"count" | "name" | "size", "count" | "name" | "size", Mode>;
|
|
930
976
|
defaultFileSort: z$1<"count" | "latest" | "path", "count" | "latest" | "path", Mode>;
|
|
931
977
|
insightsEntry: z$1<"hide" | "show", "hide" | "show", Mode>;
|
|
978
|
+
defaultDurationCurve: z$1<"hide" | "show", "hide" | "show", Mode>;
|
|
932
979
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
933
980
|
maxRequestSteps: z$1<number, number, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
934
981
|
maxKeptTurns: z$1<number, number, Mode extends "volatile" | "volatile-defined" ? "volatile-defined" : "defined">;
|
|
@@ -943,6 +990,7 @@ export declare const Config: z$1<Schemastery.ObjectS<NoInfer<{
|
|
|
943
990
|
defaultToolSort: z$1<"count" | "name" | "size", "count" | "name" | "size", Mode>;
|
|
944
991
|
defaultFileSort: z$1<"count" | "latest" | "path", "count" | "latest" | "path", Mode>;
|
|
945
992
|
insightsEntry: z$1<"hide" | "show", "hide" | "show", Mode>;
|
|
993
|
+
defaultDurationCurve: z$1<"hide" | "show", "hide" | "show", Mode>;
|
|
946
994
|
}>>, "plain">;
|
|
947
995
|
//#endregion
|
|
948
996
|
//#region src/host/index.d.ts
|
package/lib/index.js
CHANGED
|
@@ -259,8 +259,8 @@ function estimateBlocks(blocks) {
|
|
|
259
259
|
}
|
|
260
260
|
/**
|
|
261
261
|
* Price one surface message exactly like dsh's token-meter estimate:
|
|
262
|
-
* an empty-content assistant/
|
|
263
|
-
*
|
|
262
|
+
* an empty-content assistant/developer event projects to NO message, so it
|
|
263
|
+
* prices 0; every other message pays content + role framing.
|
|
264
264
|
*/
|
|
265
265
|
function estimateMessage(message, emptyIsZero = false) {
|
|
266
266
|
if (emptyIsZero && (message === null || message === void 0 || !Array.isArray(message.content) || message.content.length === 0)) return 0;
|
|
@@ -844,6 +844,7 @@ function createTimelineState() {
|
|
|
844
844
|
function categoryOf(type, message) {
|
|
845
845
|
if (type === "assistant/message") return "assistant";
|
|
846
846
|
if (type === "tool/result") return "tool";
|
|
847
|
+
if (type === "developer/message") return "inject";
|
|
847
848
|
const kind = message?.source?.kind;
|
|
848
849
|
if (kind === "skill-invocation" || kind === "skill-catalog") return "skill";
|
|
849
850
|
if (isInjection(message?.source)) return "inject";
|
|
@@ -943,12 +944,9 @@ function removeSurfaceSeqs(st, claimed, goneSeq) {
|
|
|
943
944
|
return removed;
|
|
944
945
|
}
|
|
945
946
|
/**
|
|
946
|
-
*
|
|
947
|
-
*
|
|
948
|
-
*
|
|
949
|
-
* message reads null here, never throws or guesses a shape. Purely a type
|
|
950
|
-
* cast: `deriveEventMessage` returns `event.data` for a user/message (no
|
|
951
|
-
* `data.message` indirection), so its result feeds every other case.
|
|
947
|
+
* A message nested under data.message. A structural read keeps malformed
|
|
948
|
+
* payloads total and handles developer messages absent from the V3
|
|
949
|
+
* dependency's deriveEventMessage. User messages instead live in data itself.
|
|
952
950
|
*/
|
|
953
951
|
function messageOf$1(data) {
|
|
954
952
|
const message = data?.message;
|
|
@@ -988,7 +986,7 @@ function applySurface(st, ev, type, data, message) {
|
|
|
988
986
|
seq: ev.seq,
|
|
989
987
|
time: ev.time,
|
|
990
988
|
cat,
|
|
991
|
-
tokens: estimateMessage(message, type === "assistant/message")
|
|
989
|
+
tokens: estimateMessage(message, type === "assistant/message" || type === "developer/message")
|
|
992
990
|
};
|
|
993
991
|
const imgs = imageCountOf(message?.content);
|
|
994
992
|
if (imgs > 0) node.imgs = imgs;
|
|
@@ -1397,13 +1395,38 @@ function applyTimeline(state, event, bounds) {
|
|
|
1397
1395
|
const s = ensure([]);
|
|
1398
1396
|
s.stepStart = { time: event.time };
|
|
1399
1397
|
delete s.stepSpans;
|
|
1398
|
+
delete s.stepWaits;
|
|
1399
|
+
delete s.stepApprovals;
|
|
1400
|
+
delete s.stepRequestSeq;
|
|
1400
1401
|
break;
|
|
1401
1402
|
}
|
|
1402
1403
|
case "step/end": {
|
|
1403
1404
|
const start = state.stepStart;
|
|
1404
1405
|
if (start === void 0) return state;
|
|
1405
|
-
const s = ensure(["timing"]);
|
|
1406
|
+
const s = ensure(state.stepRequestSeq !== void 0 ? ["timing", "requests"] : ["timing"]);
|
|
1406
1407
|
ensureTiming(s).wallMs += durOf(start.time, event.time);
|
|
1408
|
+
const waits = [...state.stepWaits ?? []];
|
|
1409
|
+
for (const id in state.stepApprovals) waits.push({
|
|
1410
|
+
start: state.stepApprovals[id],
|
|
1411
|
+
end: event.time
|
|
1412
|
+
});
|
|
1413
|
+
waits.sort((a, b) => a.start - b.start || a.end - b.end);
|
|
1414
|
+
let waitMs = 0;
|
|
1415
|
+
let waitCursor = start.time;
|
|
1416
|
+
for (const wait of waits) {
|
|
1417
|
+
const from = Math.max(waitCursor, wait.start);
|
|
1418
|
+
const to = Math.min(wait.end, event.time);
|
|
1419
|
+
if (to > from) {
|
|
1420
|
+
waitMs += to - from;
|
|
1421
|
+
waitCursor = to;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const activeMs = durOf(start.time, event.time) - waitMs;
|
|
1425
|
+
const last = state.requests.at(-1);
|
|
1426
|
+
if (last !== void 0 && last.seq === state.stepRequestSeq) s.requests[s.requests.length - 1] = {
|
|
1427
|
+
...last,
|
|
1428
|
+
activeMs
|
|
1429
|
+
};
|
|
1407
1430
|
const painted = [];
|
|
1408
1431
|
for (const span of state.stepSpans ?? []) {
|
|
1409
1432
|
const from = Math.max(start.time, span.start);
|
|
@@ -1440,11 +1463,43 @@ function applyTimeline(state, event, bounds) {
|
|
|
1440
1463
|
s.spans = [...state.spans, ...flushed];
|
|
1441
1464
|
delete s.stepStart;
|
|
1442
1465
|
delete s.stepSpans;
|
|
1466
|
+
delete s.stepWaits;
|
|
1467
|
+
delete s.stepApprovals;
|
|
1468
|
+
delete s.stepRequestSeq;
|
|
1443
1469
|
bumpDetailRev(s);
|
|
1444
1470
|
break;
|
|
1445
1471
|
}
|
|
1446
|
-
case "
|
|
1447
|
-
const
|
|
1472
|
+
case "approval/asked": {
|
|
1473
|
+
const id = data?.id;
|
|
1474
|
+
if (state.stepStart === void 0 || typeof id !== "string") return state;
|
|
1475
|
+
const s = ensure([]);
|
|
1476
|
+
s.stepApprovals = {
|
|
1477
|
+
...state.stepApprovals,
|
|
1478
|
+
[id]: event.time
|
|
1479
|
+
};
|
|
1480
|
+
break;
|
|
1481
|
+
}
|
|
1482
|
+
case "approval/decided": {
|
|
1483
|
+
const id = data?.id;
|
|
1484
|
+
const open = state.stepApprovals;
|
|
1485
|
+
const askedAt = typeof id === "string" ? open?.[id] : void 0;
|
|
1486
|
+
if (open === void 0 || askedAt === void 0) return state;
|
|
1487
|
+
const s = ensure([]);
|
|
1488
|
+
s.stepWaits = [...state.stepWaits ?? [], {
|
|
1489
|
+
start: askedAt,
|
|
1490
|
+
end: event.time
|
|
1491
|
+
}];
|
|
1492
|
+
const kept = {};
|
|
1493
|
+
for (const k in open) if (k !== id) kept[k] = open[k];
|
|
1494
|
+
if (Object.keys(kept).length > 0) s.stepApprovals = kept;
|
|
1495
|
+
else delete s.stepApprovals;
|
|
1496
|
+
break;
|
|
1497
|
+
}
|
|
1498
|
+
case "user/message":
|
|
1499
|
+
case "developer/message": {
|
|
1500
|
+
const developer = event.type === "developer/message";
|
|
1501
|
+
const msg = developer ? messageOf$1(data) : deriveEventMessage(event);
|
|
1502
|
+
if (developer && (msg === null || !Array.isArray(msg.content))) return state;
|
|
1448
1503
|
const s = ensure([
|
|
1449
1504
|
"surface",
|
|
1450
1505
|
"sums",
|
|
@@ -1454,15 +1509,15 @@ function applyTimeline(state, event, bounds) {
|
|
|
1454
1509
|
bumpDetailRev(s);
|
|
1455
1510
|
const node = applySurface(s, event, event.type, data, msg);
|
|
1456
1511
|
const source = msg?.source;
|
|
1457
|
-
if (isInjection(source)) {
|
|
1512
|
+
if (developer || isInjection(source)) {
|
|
1458
1513
|
const rec = {
|
|
1459
1514
|
seq: event.seq,
|
|
1460
1515
|
time: event.time,
|
|
1461
1516
|
kind: "inject",
|
|
1462
|
-
form: typeof source
|
|
1517
|
+
form: typeof source?.form === "string" && source.form !== "" ? source.form : "context",
|
|
1463
1518
|
tokens: node.tokens
|
|
1464
1519
|
};
|
|
1465
|
-
if (source
|
|
1520
|
+
if (source?.kind === "skill-invocation") {
|
|
1466
1521
|
rec.sub = "skill";
|
|
1467
1522
|
rec.name = typeof source.name === "string" ? source.name : "?";
|
|
1468
1523
|
} else {
|
|
@@ -1471,7 +1526,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1471
1526
|
rec.name = label;
|
|
1472
1527
|
node.name = label;
|
|
1473
1528
|
}
|
|
1474
|
-
if (source
|
|
1529
|
+
if (source?.form === "notice" && typeof source.summary === "string" && source.summary !== "") rec.detail = source.summary;
|
|
1475
1530
|
}
|
|
1476
1531
|
s.events.push(rec);
|
|
1477
1532
|
} else {
|
|
@@ -1498,6 +1553,10 @@ function applyTimeline(state, event, bounds) {
|
|
|
1498
1553
|
bumpDetailRev(s);
|
|
1499
1554
|
const node = applySurface(s, event, event.type, data, toolMsg);
|
|
1500
1555
|
if (node.tool === ASK_USER_TOOL) s.humanInputs = (s.humanInputs ?? 0) + 1;
|
|
1556
|
+
if (pendingEntry !== void 0 && pendingEntry.name === ASK_USER_TOOL && state.stepStart !== void 0) s.stepWaits = [...state.stepWaits ?? [], {
|
|
1557
|
+
start: pendingEntry.start,
|
|
1558
|
+
end: event.time
|
|
1559
|
+
}];
|
|
1501
1560
|
if (pendingEntry !== void 0) pushFileOps(s, opsOfCall({
|
|
1502
1561
|
seq: event.seq,
|
|
1503
1562
|
time: event.time,
|
|
@@ -1565,6 +1624,7 @@ function applyTimeline(state, event, bounds) {
|
|
|
1565
1624
|
};
|
|
1566
1625
|
if (data && typeof data.turn === "number") record.turn = data.turn;
|
|
1567
1626
|
if (data && typeof data.step === "number") record.step = data.step;
|
|
1627
|
+
if (state.stepStart !== void 0) s.stepRequestSeq = record.seq;
|
|
1568
1628
|
let output = null;
|
|
1569
1629
|
if (usage !== null && typeof usage === "object") {
|
|
1570
1630
|
const input = tokenCountOf(usage.inputTokens);
|
|
@@ -2155,12 +2215,15 @@ const FIRST_PARTY_SOURCES = Object.freeze({
|
|
|
2155
2215
|
* first argument, so `reader.fiber.name` identifies the plugin that is about
|
|
2156
2216
|
* to call `register()`.
|
|
2157
2217
|
*
|
|
2158
|
-
* - The `internal/get` handler records
|
|
2159
|
-
*
|
|
2160
|
-
*
|
|
2161
|
-
*
|
|
2162
|
-
*
|
|
2163
|
-
*
|
|
2218
|
+
* - The `internal/get` handler records the reading fiber's NAME — a scalar,
|
|
2219
|
+
* never the reader context, which would pin the caller's whole agent — and
|
|
2220
|
+
* wraps the tools service's `register` (once per underlying instance —
|
|
2221
|
+
* cordis hands out a fresh traced proxy per read of a tracked service, so
|
|
2222
|
+
* the proxy is peeled to the stable instance first; an earlier wrapper of a
|
|
2223
|
+
* previous hook incarnation is peeled back to the original, so a plugin
|
|
2224
|
+
* reload re-wraps without stacking) to capture the reader at registration
|
|
2225
|
+
* time into a live map. Every wrapper is undone when the plugin unloads:
|
|
2226
|
+
* the original `register` goes back on the instance, unless a newer hook
|
|
2164
2227
|
* incarnation re-wrapped it first (that incarnation's own cleanup then
|
|
2165
2228
|
* owns the restore).
|
|
2166
2229
|
* - When the reader slot is missing, root-named, or this plugin's own (e.g.
|
|
@@ -2221,6 +2284,20 @@ function packageNameFrom(file) {
|
|
|
2221
2284
|
}
|
|
2222
2285
|
}
|
|
2223
2286
|
const FRAME_POSITION = /:\d+:\d+$/;
|
|
2287
|
+
/** Symbol cordis registers (`Symbol.for`) on its traced proxies: reading it
|
|
2288
|
+
* yields the wrapped target — the stable underlying service instance. */
|
|
2289
|
+
const CORDIS_ORIGINAL = Symbol.for("cordis.original");
|
|
2290
|
+
/**
|
|
2291
|
+
* Peel cordis's per-read traced proxy of a tracked service (dsh's ToolRuntime
|
|
2292
|
+
* is one — every `ctx.tools` read returns a fresh proxy closing over the
|
|
2293
|
+
* reader's context) down to the instance it wraps, so identity-keyed
|
|
2294
|
+
* bookkeeping sees one stable object. Plain service objects pass through
|
|
2295
|
+
* unchanged.
|
|
2296
|
+
* @param tools - the value as handed out by a context read.
|
|
2297
|
+
*/
|
|
2298
|
+
function rawInstanceOf(tools) {
|
|
2299
|
+
return tools?.[CORDIS_ORIGINAL] ?? tools;
|
|
2300
|
+
}
|
|
2224
2301
|
/** Package name of this module's own package (self-fallbacks are filtered). */
|
|
2225
2302
|
const selfPackage = packageNameFrom(selfUrl);
|
|
2226
2303
|
/**
|
|
@@ -2279,7 +2356,7 @@ function createToolAttribution(ctx) {
|
|
|
2279
2356
|
const instance = tools;
|
|
2280
2357
|
const wrappedRegister = function(definition) {
|
|
2281
2358
|
const toolName = definition?.name;
|
|
2282
|
-
let owner = lastReader
|
|
2359
|
+
let owner = lastReader;
|
|
2283
2360
|
if (!owner || owner === "root" || owner === self) owner = callerPackageFrom((/* @__PURE__ */ new Error()).stack);
|
|
2284
2361
|
const dispose = original.call(this, definition);
|
|
2285
2362
|
if (typeof toolName === "string" && owner && owner !== "root" && owner !== self && owner !== selfPackage) {
|
|
@@ -2303,14 +2380,14 @@ function createToolAttribution(ctx) {
|
|
|
2303
2380
|
ctx.on("internal/get", (reader, name, _error, next) => {
|
|
2304
2381
|
if (name !== "tools") return next();
|
|
2305
2382
|
const tools = next();
|
|
2306
|
-
lastReader = reader;
|
|
2307
|
-
wrapInstance(tools);
|
|
2383
|
+
lastReader = reader.fiber.name;
|
|
2384
|
+
wrapInstance(rawInstanceOf(tools));
|
|
2308
2385
|
return tools;
|
|
2309
2386
|
});
|
|
2310
2387
|
ctx.effect(() => () => {
|
|
2311
2388
|
for (const restore of patched.splice(0)) restore();
|
|
2312
2389
|
}, "tools.register attribution");
|
|
2313
|
-
const toolsService = ctx.get("tools", false);
|
|
2390
|
+
const toolsService = rawInstanceOf(ctx.get("tools", false));
|
|
2314
2391
|
wrapInstance(toolsService);
|
|
2315
2392
|
const boot = /* @__PURE__ */ new Set();
|
|
2316
2393
|
try {
|
|
@@ -2777,7 +2854,8 @@ const Config = z$1.object({
|
|
|
2777
2854
|
"latest",
|
|
2778
2855
|
"path"
|
|
2779
2856
|
]).default("count").loose()),
|
|
2780
|
-
insightsEntry: volatileField(z$1.union(["show", "hide"]).default("show").loose())
|
|
2857
|
+
insightsEntry: volatileField(z$1.union(["show", "hide"]).default("show").loose()),
|
|
2858
|
+
defaultDurationCurve: volatileField(z$1.union(["show", "hide"]).default("show").loose())
|
|
2781
2859
|
});
|
|
2782
2860
|
/** Resolve the fold's retention bounds (the schema fills every default). */
|
|
2783
2861
|
function resolveBounds(config) {
|
|
@@ -3157,6 +3235,11 @@ const requestRecordSchema = z.object({
|
|
|
3157
3235
|
skill: z.number().int().nonnegative().optional(),
|
|
3158
3236
|
cacheRead: z.number().int().nonnegative().optional(),
|
|
3159
3237
|
output: z.number().int().nonnegative().optional(),
|
|
3238
|
+
/**
|
|
3239
|
+
* The request's step active milliseconds (see RequestRecord.activeMs).
|
|
3240
|
+
* Optional so a record whose step never closed (the live tail) still parses.
|
|
3241
|
+
*/
|
|
3242
|
+
activeMs: z.number().nonnegative().optional(),
|
|
3160
3243
|
stepCount: z.number().int().positive().optional()
|
|
3161
3244
|
}).strict();
|
|
3162
3245
|
const contextEventSchema = z.object({
|
|
@@ -3356,6 +3439,12 @@ const timelineStateSchema = z.object({
|
|
|
3356
3439
|
time: z.number(),
|
|
3357
3440
|
firstToken: z.number().optional()
|
|
3358
3441
|
}).strict().optional(),
|
|
3442
|
+
stepWaits: z.array(z.object({
|
|
3443
|
+
start: z.number(),
|
|
3444
|
+
end: z.number()
|
|
3445
|
+
}).strict()).optional(),
|
|
3446
|
+
stepApprovals: z.record(z.string(), z.number()).optional(),
|
|
3447
|
+
stepRequestSeq: z.number().optional(),
|
|
3359
3448
|
callNames: z.record(z.string(), z.object({
|
|
3360
3449
|
name: z.string(),
|
|
3361
3450
|
start: z.number(),
|
|
@@ -3415,7 +3504,7 @@ function createContextTimelineDefinition(config, slim) {
|
|
|
3415
3504
|
},
|
|
3416
3505
|
init: () => createTimelineState(),
|
|
3417
3506
|
apply: (state, event) => applyTimeline(state, event, bounds),
|
|
3418
|
-
stateVersion:
|
|
3507
|
+
stateVersion: 26
|
|
3419
3508
|
};
|
|
3420
3509
|
}
|
|
3421
3510
|
//#endregion
|
|
@@ -3538,7 +3627,8 @@ const SettingsSchema = z$1.object({
|
|
|
3538
3627
|
"latest",
|
|
3539
3628
|
"path"
|
|
3540
3629
|
]).default("count").loose(),
|
|
3541
|
-
insightsEntry: z$1.union(["show", "hide"]).default("show").loose()
|
|
3630
|
+
insightsEntry: z$1.union(["show", "hide"]).default("show").loose(),
|
|
3631
|
+
defaultDurationCurve: z$1.union(["show", "hide"]).default("show").loose()
|
|
3542
3632
|
});
|
|
3543
3633
|
/** Serve the namespace while a settings provider with the register face is composed; inert otherwise. */
|
|
3544
3634
|
function installSettings(ctx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.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
|
"icon": "icon.svg",
|
|
6
6
|
"author": "bowenliang123",
|