dsh-context 0.33.1 → 0.34.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.
Files changed (3) hide show
  1. package/README.md +15 -2
  2. package/lib/client.js +786 -119
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -66,10 +66,11 @@ window.__ModuleLoader__.load({
66
66
  "brief.input": "输入",
67
67
  "brief.reply": "回复",
68
68
  "brief.more": "+{n}",
69
+ "brief.noInputs": "(无新增)",
69
70
  "brief.locate": "在上下文浏览器中查看",
70
- "brief.turnTip": "这一轮开场时用户发送的消息",
71
- "brief.inputTip": "这一步新进入上下文的内容(通常是上一步工具调用的结果)",
72
- "brief.replyTip": "这一步模型返回的内容(文本回复或工具调用)",
71
+ "brief.turnTip": "这一轮开场时用户发送的消息;回合内的每一步都会回显同一条",
72
+ "brief.inputTip": "上一步回复之后、本次请求发出前新进入上下文的内容——通常是上一步所调工具的结果;每轮的第一步无新增",
73
+ "brief.replyTip": "这一步模型返回的内容(文本回复或工具调用);此步发起的调用,其结果会出现在下一柱的「输入」行",
73
74
  "events.title": "上下文事件",
74
75
  "events.empty": "暂无上下文事件(压缩、注入、模型切换会出现在这里)",
75
76
  "events.at": "第 {t} 轮 · 第 {s} 步",
@@ -95,15 +96,28 @@ window.__ModuleLoader__.load({
95
96
  "files.sortTip": "文件排序:按操作次数(选中分类时按该分类的次数)、最近操作时间或路径",
96
97
  "files.files": "{n} 个文件",
97
98
  "files.deltaTip": "按 edit/write 调用参数估算的累计行数变化:+ 新增 / − 删除",
98
- "files.unresolved": "… 另有 {n} 次文件操作的路径超出保留窗口",
99
99
  "files.empty": "该范围内没有文件读取、写入或搜索操作",
100
100
  "files.noMatch": "没有匹配当前过滤条件的文件",
101
101
  "files.locate": "在上下文浏览器中查看此次操作",
102
- "files.moreOps": "+{n} 次更早操作",
103
102
  "files.errs": "{n} 次失败",
104
103
  "files.form.image": "图片",
105
104
  "files.form.dir": "目录",
106
105
  "files.form.text": "文件",
106
+ "agents.title": "Agent 网络",
107
+ "agents.sub": "当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话",
108
+ "agents.chip.count": "{n} 个 Agent",
109
+ "agents.chip.running": "{n} 个运行中",
110
+ "agents.chip.tokens": "上下文合计 {n}",
111
+ "agents.more": "另有 {n} 个未显示",
112
+ "agents.solo": "还没有子 Agent — 当 Agent 通过 subagent 工具委派任务时,协作网络会在这里展开",
113
+ "agents.self": "当前",
114
+ "agents.running": "运行中",
115
+ "agents.legend.free": "空闲窗口",
116
+ "agents.oneshot": "一次性",
117
+ "agents.continuable": "多轮",
118
+ "agents.requests": "{n} 次请求",
119
+ "agents.billed": "累计计费 {n}",
120
+ "agents.open": "点击打开会话 →",
107
121
  "loading": "正在读取会话日志…",
108
122
  "error": "上下文数据读取失败:",
109
123
  "error.retry": "重试",
@@ -242,10 +256,11 @@ window.__ModuleLoader__.load({
242
256
  "brief.input": "In",
243
257
  "brief.reply": "Response",
244
258
  "brief.more": "+{n}",
259
+ "brief.noInputs": "(nothing new)",
245
260
  "brief.locate": "Reveal in Context Browser",
246
- "brief.turnTip": "The user message that opened this turn",
247
- "brief.inputTip": "What newly entered the context for this step (usually results of the previous tool calls)",
248
- "brief.replyTip": "What the model returned on this step (a text reply or tool calls)",
261
+ "brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
262
+ "brief.inputTip": "What entered after the previous reply and before this request went out usually the previous tool-call results; a turn opener has nothing new",
263
+ "brief.replyTip": "What the model returned on this step (a text reply or tool calls) — results of calls made here appear in the In row of the next bar",
249
264
  "events.title": "Context Events",
250
265
  "events.empty": "No context events yet (compaction, injections, model switches appear here)",
251
266
  "events.at": "Turn {t} · Step {s}",
@@ -271,15 +286,28 @@ window.__ModuleLoader__.load({
271
286
  "files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
272
287
  "files.files": "{n} files",
273
288
  "files.deltaTip": "Cumulative line change estimated from edit/write call arguments: + added / − removed",
274
- "files.unresolved": "… {n} more file ops have paths outside the retained window",
275
289
  "files.empty": "No file reads, writes, or searches in this range",
276
290
  "files.noMatch": "No files match the current filters",
277
291
  "files.locate": "Reveal this operation in the Context Browser",
278
- "files.moreOps": "+{n} earlier ops",
279
292
  "files.errs": "{n} failed",
280
293
  "files.form.image": "Image",
281
294
  "files.form.dir": "Directory",
282
295
  "files.form.text": "File",
296
+ "agents.title": "Agent Network",
297
+ "agents.sub": "Live context of this agent and its subagents · click a node to open its session",
298
+ "agents.chip.count": "{n} agents",
299
+ "agents.chip.running": "{n} running",
300
+ "agents.chip.tokens": "{n} tokens in context",
301
+ "agents.more": "{n} more not shown",
302
+ "agents.solo": "No subagents yet — when the agent delegates work through the subagent tool, the network unfolds here",
303
+ "agents.self": "current",
304
+ "agents.running": "running",
305
+ "agents.legend.free": "free window",
306
+ "agents.oneshot": "one-shot",
307
+ "agents.continuable": "continuable",
308
+ "agents.requests": "{n} requests",
309
+ "agents.billed": "{n} billed",
310
+ "agents.open": "click to open →",
283
311
  "loading": "Reading the session log…",
284
312
  "error": "Failed to read context data: ",
285
313
  "error.retry": "Retry",
@@ -578,7 +606,7 @@ window.__ModuleLoader__.load({
578
606
  * every field read below must re-prove itself (the no-white-screen
579
607
  * guarantee), so no field may borrow the wire type before its check.
580
608
  */
581
- function asRecord(value) {
609
+ function asRecord$1(value) {
582
610
  if (value === null || value === void 0 || typeof value !== "object") return null;
583
611
  return value;
584
612
  }
@@ -607,7 +635,7 @@ window.__ModuleLoader__.load({
607
635
  * during render and unmounting the conversation view.
608
636
  */
609
637
  function timelineOf(value) {
610
- const data = asRecord(value);
638
+ const data = asRecord$1(value);
611
639
  if (data === null) return null;
612
640
  const current = data.current;
613
641
  if (current !== null && typeof current === "object" && [
@@ -655,7 +683,7 @@ window.__ModuleLoader__.load({
655
683
  * back to their derived anchor, so the UI degrades gracefully.
656
684
  */
657
685
  function contextPressureOf(value) {
658
- return asRecord(value);
686
+ return asRecord$1(value);
659
687
  }
660
688
  /**
661
689
  * Narrow a delivered projection value to the official token-meter
@@ -665,7 +693,7 @@ window.__ModuleLoader__.load({
665
693
  * own sums instead of mixing sources.
666
694
  */
667
695
  function contextBreakdownOf(value) {
668
- const data = asRecord(value);
696
+ const data = asRecord$1(value);
669
697
  if (data === null) return null;
670
698
  const { systemTokens, toolsTokens, messageTokens } = data;
671
699
  if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
@@ -684,7 +712,7 @@ window.__ModuleLoader__.load({
684
712
  * usage yet) — callers drop the cache-hit cell to a dash.
685
713
  */
686
714
  function tokenUsageOf(value) {
687
- return asRecord(value);
715
+ return asRecord$1(value);
688
716
  }
689
717
  /**
690
718
  * Narrow a delivered projection value to the plugin's `contextHeaders`
@@ -698,7 +726,7 @@ window.__ModuleLoader__.load({
698
726
  * and the card falls back to its tokens-only note.
699
727
  */
700
728
  function headersOf(value) {
701
- const headers = asRecord(value);
729
+ const headers = asRecord$1(value);
702
730
  if (headers === null || !Array.isArray(headers.headers)) return null;
703
731
  for (const h of headers.headers) {
704
732
  if (h === null || typeof h !== "object") return null;
@@ -2512,21 +2540,17 @@ window.__ModuleLoader__.load({
2512
2540
  removed: 0
2513
2541
  };
2514
2542
  const byPath = /* @__PURE__ */ new Map();
2515
- let unresolved = 0;
2516
2543
  for (const n of nodes) {
2517
2544
  if (n.cat !== "tool") continue;
2518
2545
  if (before !== null && n.seq >= before) continue;
2519
2546
  const kind = kindOfTool(n.tool);
2520
2547
  if (kind === null) continue;
2521
2548
  const tool = n.tool;
2522
- totals[kind].ops++;
2523
2549
  const conv = convOf(n.seq);
2524
2550
  const args = parseCallArgs(conv?.call?.argsRaw);
2525
2551
  const path = pathOfArgs(tool, args);
2526
- if (args === null || path === null) {
2527
- unresolved++;
2528
- continue;
2529
- }
2552
+ if (path === null || args === null) continue;
2553
+ totals[kind].ops++;
2530
2554
  const { added, removed } = deltaOf(tool, args);
2531
2555
  const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
2532
2556
  const op = {
@@ -2579,8 +2603,7 @@ window.__ModuleLoader__.load({
2579
2603
  entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
2580
2604
  return {
2581
2605
  entries,
2582
- totals,
2583
- unresolved
2606
+ totals
2584
2607
  };
2585
2608
  }
2586
2609
  /**
@@ -2594,6 +2617,588 @@ window.__ModuleLoader__.load({
2594
2617
  for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
2595
2618
  return gone === void 0 ? "live" : null;
2596
2619
  }
2620
+ const SLOT_MAX = 184;
2621
+ const SLOT_MIN = 112;
2622
+ const CAPTION_GUTTER = 8;
2623
+ const LEVEL_H = 154;
2624
+ const CELL_H = 90;
2625
+ const PAD_Y = 56;
2626
+ function asRecord(value) {
2627
+ if (value === null || value === void 0 || typeof value !== "object") return null;
2628
+ return value;
2629
+ }
2630
+ /** Narrow one list-row value; null when it is not a row at all. */
2631
+ function agentRowOf(value) {
2632
+ const rec = asRecord(value);
2633
+ if (rec === null) return null;
2634
+ const projections = asRecord(rec.projectionValues);
2635
+ return {
2636
+ ...typeof rec.displayTitle === "string" ? { displayTitle: rec.displayTitle } : {},
2637
+ ...typeof rec.title === "string" ? { title: rec.title } : {},
2638
+ ...typeof rec.parentId === "string" ? { parentId: rec.parentId } : {},
2639
+ ...typeof rec.origin === "string" ? { origin: rec.origin } : {},
2640
+ running: rec.running === true,
2641
+ completed: rec.completed === true,
2642
+ blank: rec.blank === true,
2643
+ updatedAt: numOf(rec.updatedAt),
2644
+ ...projections !== null ? { projections } : {}
2645
+ };
2646
+ }
2647
+ /** Narrow the subagent identity projection value (null = not a descriptor-backed subagent). */
2648
+ function agentIdentityOf(value) {
2649
+ const rec = asRecord(value);
2650
+ if (rec === null) return null;
2651
+ if (rec.mode !== "one-shot" && rec.mode !== "continuable") return null;
2652
+ return {
2653
+ mode: rec.mode,
2654
+ ...typeof rec.label === "string" && rec.label !== "" ? { label: rec.label } : {}
2655
+ };
2656
+ }
2657
+ /** Narrow the subagent timing projection value into a single duration (null = absent/malformed). */
2658
+ function agentDurationOf(value) {
2659
+ const rec = asRecord(value);
2660
+ if (rec === null) return null;
2661
+ const settled = numOf(rec.settledMs);
2662
+ const active = asRecord(rec.active);
2663
+ const total = settled + (active !== null ? Math.max(0, numOf(active.through) - numOf(active.since)) : 0);
2664
+ return total > 0 ? total : null;
2665
+ }
2666
+ /**
2667
+ * Fold one row's projection values into render-ready stats. The composition
2668
+ * prefers the plugin's own `contextTimeline` (six buckets, provider-anchored
2669
+ * by token-meter's pressure/breakdown — the exact headlineOf derivation the
2670
+ * overview card shows); a pressure-only row (plugin host half absent for that
2671
+ * session) still yields an occupancy ring without slices.
2672
+ */
2673
+ function agentStatsOf(values) {
2674
+ const timeline = timelineOf(values?.contextTimeline);
2675
+ const pressure = contextPressureOf(values?.contextPressure);
2676
+ const breakdown = contextBreakdownOf(values?.contextBreakdown);
2677
+ const usage = tokenUsageOf(values?.tokenUsage);
2678
+ let head = null;
2679
+ if (timeline !== null) head = headlineOf(timeline, pressure, breakdown);
2680
+ else if (pressure !== null) {
2681
+ const tokens = typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : typeof pressure.pressureTokens === "number" ? pressure.pressureTokens : null;
2682
+ if (tokens !== null) {
2683
+ const window = typeof pressure.contextWindow === "number" && pressure.contextWindow > 0 ? pressure.contextWindow : void 0;
2684
+ head = {
2685
+ tokens,
2686
+ window,
2687
+ pct: window !== void 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
2688
+ parts: []
2689
+ };
2690
+ }
2691
+ }
2692
+ const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
2693
+ return {
2694
+ head,
2695
+ requests: timeline !== null ? timeline.requests.length : 0,
2696
+ billed,
2697
+ durationMs: agentDurationOf(values?.subagentTiming),
2698
+ identity: agentIdentityOf(values?.subagent)
2699
+ };
2700
+ }
2701
+ /**
2702
+ * Build the current session's agent family from a session-list snapshot:
2703
+ * walk up `parentId` to the topmost known ancestor, then DFS its whole
2704
+ * subtree (blank placeholder rows excluded). Null when there is no anchor —
2705
+ * no current session, or a snapshot without a `byId` table (older harness).
2706
+ * The current session synthesizes a row when the list has not delivered it
2707
+ * yet, so the card can still show its live self stats.
2708
+ */
2709
+ function agentForestOf(snapshot, currentId, self) {
2710
+ const byId = asRecord(asRecord(snapshot)?.byId);
2711
+ if (byId === null || currentId === void 0 || currentId === "") return null;
2712
+ const rows = /* @__PURE__ */ new Map();
2713
+ for (const key of Object.keys(byId)) {
2714
+ const row = agentRowOf(byId[key]);
2715
+ if (row !== null && (!row.blank || key === currentId)) rows.set(key, row);
2716
+ }
2717
+ if (!rows.has(currentId)) rows.set(currentId, {
2718
+ running: false,
2719
+ completed: false,
2720
+ blank: false,
2721
+ updatedAt: 0
2722
+ });
2723
+ let root = currentId;
2724
+ const chain = /* @__PURE__ */ new Set([currentId]);
2725
+ for (;;) {
2726
+ const parent = rows.get(root)?.parentId;
2727
+ if (parent === void 0 || !rows.has(parent) || chain.has(parent)) break;
2728
+ chain.add(parent);
2729
+ root = parent;
2730
+ }
2731
+ const rootRow = rows.get(root);
2732
+ /* v8 ignore next 2 -- root is currentId (inserted above) or a parent
2733
+ verified with rows.has, so its row always exists. */
2734
+ if (rootRow === void 0) return null;
2735
+ const childrenOf = /* @__PURE__ */ new Map();
2736
+ for (const [id, row] of rows) {
2737
+ if (row.parentId === void 0 || !rows.has(row.parentId)) continue;
2738
+ const list = childrenOf.get(row.parentId) ?? [];
2739
+ list.push({
2740
+ id,
2741
+ row
2742
+ });
2743
+ childrenOf.set(row.parentId, list);
2744
+ }
2745
+ for (const kids of childrenOf.values()) kids.sort((a, b) => {
2746
+ const runDelta = Number(b.row.running) - Number(a.row.running);
2747
+ if (runDelta !== 0) return runDelta;
2748
+ const timeDelta = b.row.updatedAt - a.row.updatedAt;
2749
+ return timeDelta !== 0 ? timeDelta : a.id < b.id ? -1 : 1;
2750
+ });
2751
+ const measure = (id, seen) => {
2752
+ if (seen.has(id)) return 0;
2753
+ seen.add(id);
2754
+ let total = 1;
2755
+ for (const kid of childrenOf.get(id) ?? []) total += measure(kid.id, seen);
2756
+ return total;
2757
+ };
2758
+ const total = measure(root, /* @__PURE__ */ new Set());
2759
+ const nodes = [];
2760
+ const edges = [];
2761
+ const visit = (id, row, parentId, depth, seen, family) => {
2762
+ if (seen.has(id) || nodes.length >= 25) return;
2763
+ seen.add(id);
2764
+ const stats = agentStatsOf(row.projections);
2765
+ const identity = stats.identity;
2766
+ const node = {
2767
+ ...stats,
2768
+ id,
2769
+ label: identity?.label ?? row.title ?? row.displayTitle ?? id,
2770
+ ...parentId !== void 0 ? { parentId } : {},
2771
+ depth,
2772
+ family,
2773
+ isCurrent: id === currentId,
2774
+ running: row.running,
2775
+ completed: row.completed,
2776
+ subagent: row.origin === "subagent" || identity !== null
2777
+ };
2778
+ if (node.isCurrent && self !== void 0) {
2779
+ node.head = self.head ?? node.head;
2780
+ node.billed = self.billed ?? node.billed;
2781
+ node.requests = self.requests > 0 ? self.requests : node.requests;
2782
+ }
2783
+ nodes.push(node);
2784
+ if (parentId !== void 0) edges.push({
2785
+ from: parentId,
2786
+ to: id
2787
+ });
2788
+ (childrenOf.get(id) ?? []).forEach((kid, ki) => {
2789
+ visit(kid.id, kid.row, id, depth + 1, seen, depth === 0 ? ki : family);
2790
+ });
2791
+ };
2792
+ visit(root, rootRow, void 0, 0, /* @__PURE__ */ new Set(), -1);
2793
+ return {
2794
+ nodes,
2795
+ edges,
2796
+ overflow: Math.max(0, total - nodes.length),
2797
+ solo: nodes.length === 1
2798
+ };
2799
+ }
2800
+ /**
2801
+ * Tidy top-down tree layout: one row per depth level, siblings claim leaf
2802
+ * slots, parents center over their children. Fully responsive to the stage's
2803
+ * visible width: the slot pitch stretches up to SLOT_MAX and compresses down
2804
+ * to SLOT_MIN (captions wrap tighter); a level that still overflows wraps
2805
+ * into bands of at most a per-level node count derived from the stage width —
2806
+ * vertical room is cheaper than horizontal scrolling. Links exit a parent at
2807
+ * its cell bottom (below the caption zone) and enter the child at its top,
2808
+ * so a connector never crosses a label.
2809
+ */
2810
+ function layoutForest(forest, stageWidth = 0) {
2811
+ const childrenOf = /* @__PURE__ */ new Map();
2812
+ for (const n of forest.nodes) {
2813
+ if (n.parentId === void 0) continue;
2814
+ const kids = childrenOf.get(n.parentId) ?? [];
2815
+ kids.push(n);
2816
+ childrenOf.set(n.parentId, kids);
2817
+ }
2818
+ const slotOf = /* @__PURE__ */ new Map();
2819
+ let leafSlots = 0;
2820
+ const place = (node) => {
2821
+ const kids = childrenOf.get(node.id) ?? [];
2822
+ if (kids.length === 0) {
2823
+ const slot = leafSlots;
2824
+ leafSlots++;
2825
+ slotOf.set(node.id, slot);
2826
+ return slot;
2827
+ }
2828
+ let first = 0;
2829
+ let last = 0;
2830
+ kids.forEach((kid, index) => {
2831
+ const slot = place(kid);
2832
+ if (index === 0) first = slot;
2833
+ last = slot;
2834
+ });
2835
+ const slot = (first + last) / 2;
2836
+ slotOf.set(node.id, slot);
2837
+ return slot;
2838
+ };
2839
+ /* v8 ignore next 1 -- a forest always holds at least the (possibly
2840
+ synthesized) current node. */
2841
+ if (forest.nodes.length > 0) place(forest.nodes[0]);
2842
+ const perLevel = stageWidth > 0 ? Math.max(2, Math.floor(stageWidth / SLOT_MIN)) : 0;
2843
+ if (perLevel > 0 && leafSlots > perLevel) {
2844
+ const bandSlot = Math.min(SLOT_MAX, stageWidth / perLevel);
2845
+ const width = perLevel * bandSlot;
2846
+ const points = [];
2847
+ let row = 0;
2848
+ const emitBand = (nodes, depth) => {
2849
+ const inset = (width - nodes.length * bandSlot) / 2;
2850
+ nodes.forEach((node, i) => {
2851
+ points.push({
2852
+ id: node.id,
2853
+ x: inset + (i + .5) * bandSlot,
2854
+ y: PAD_Y + row * LEVEL_H,
2855
+ depth
2856
+ });
2857
+ });
2858
+ row++;
2859
+ let band = [];
2860
+ const flush = () => {
2861
+ if (band.length === 0) return;
2862
+ const packed = band;
2863
+ band = [];
2864
+ emitBand(packed, depth + 1);
2865
+ };
2866
+ for (const node of nodes) {
2867
+ const kids = childrenOf.get(node.id) ?? [];
2868
+ for (let start = 0; start < kids.length; start += perLevel) {
2869
+ const group = kids.slice(start, start + perLevel);
2870
+ if (band.length + group.length > perLevel) flush();
2871
+ band.push(...group);
2872
+ if (band.length === perLevel) flush();
2873
+ }
2874
+ }
2875
+ flush();
2876
+ };
2877
+ /* v8 ignore next 1 -- a forest always holds at least the current node. */
2878
+ if (forest.nodes.length > 0) emitBand([forest.nodes[0]], 0);
2879
+ return {
2880
+ width,
2881
+ height: PAD_Y + (row - 1) * LEVEL_H + CELL_H + 28,
2882
+ captionW: bandSlot - CAPTION_GUTTER,
2883
+ points,
2884
+ links: linksOf(forest, points)
2885
+ };
2886
+ }
2887
+ const slot = stageWidth > 0 && leafSlots > 1 ? Math.min(SLOT_MAX, stageWidth / leafSlots) : SLOT_MAX;
2888
+ const points = forest.nodes.map((node) => ({
2889
+ id: node.id,
2890
+ /* v8 ignore next 1 -- place() visits every node: the forest is exactly
2891
+ the root's subtree by construction. */
2892
+ x: (slotOf.get(node.id) ?? 0) * slot + slot / 2,
2893
+ y: PAD_Y + node.depth * LEVEL_H,
2894
+ depth: node.depth
2895
+ }));
2896
+ const maxDepth = points.reduce((max, p) => Math.max(max, p.depth), 0);
2897
+ return {
2898
+ width: leafSlots * slot,
2899
+ height: PAD_Y + maxDepth * LEVEL_H + CELL_H + 28,
2900
+ captionW: slot - CAPTION_GUTTER,
2901
+ points,
2902
+ links: linksOf(forest, points)
2903
+ };
2904
+ }
2905
+ /**
2906
+ * Family hue by level-1 subtree index: the golden angle keeps consecutive
2907
+ * families maximally separated on the color wheel without a hand-tuned palette.
2908
+ */
2909
+ function familyHue(index) {
2910
+ return `hsl(${Math.round(index * 137.508) % 360} 58% 52%)`;
2911
+ }
2912
+ /** Parent→child links: exit the parent's cell bottom, enter the child's top. */
2913
+ function linksOf(forest, points) {
2914
+ const pointOf = new Map(points.map((p) => [p.id, p]));
2915
+ const nodeOf = new Map(forest.nodes.map((n) => [n.id, n]));
2916
+ const runningIds = new Set(forest.nodes.filter((n) => n.running).map((n) => n.id));
2917
+ const links = [];
2918
+ for (const edge of forest.edges) {
2919
+ const from = pointOf.get(edge.from);
2920
+ const to = pointOf.get(edge.to);
2921
+ /* v8 ignore next 2 -- edges are emitted only for visited parent/child
2922
+ pairs, so both points always exist. */
2923
+ if (from === void 0 || to === void 0) continue;
2924
+ links.push({
2925
+ to: edge.to,
2926
+ running: runningIds.has(edge.to),
2927
+ /* v8 ignore next 1 -- edges only connect visited nodes. */
2928
+ color: familyHue(nodeOf.get(edge.to)?.family ?? 0),
2929
+ x1: from.x,
2930
+ y1: from.y + CELL_H,
2931
+ x2: to.x,
2932
+ y2: to.y - 26 - 10
2933
+ });
2934
+ }
2935
+ return links;
2936
+ }
2937
+ /**
2938
+ * One fused ring per agent — the exact semantics of the chat composer's own
2939
+ * context ring: the composition parts, scaled to the occupancy share of the
2940
+ * window, fill the circle, and a neutral remainder marks the free window.
2941
+ * With no known window the composition fills the whole circle; with no
2942
+ * composition (pressure-only rows) a single threshold-colored arc carries
2943
+ * the occupancy; a known window with zero occupancy draws the free outline.
2944
+ */
2945
+ function ringSegments(parts, pct, radius, fallbackColor) {
2946
+ const circumference = 2 * Math.PI * radius;
2947
+ const occ = pct === null ? 1 : Math.min(100, Math.max(0, pct)) / 100;
2948
+ let total = 0;
2949
+ for (const p of parts) total += p.value > 0 ? p.value : 0;
2950
+ const segs = [];
2951
+ let offset = 0;
2952
+ if (total > 0) for (const p of parts) {
2953
+ if (p.value <= 0) continue;
2954
+ const len = circumference * (p.value / total) * occ;
2955
+ if (len <= 0) continue;
2956
+ segs.push({
2957
+ key: p.key,
2958
+ color: p.color,
2959
+ len,
2960
+ offset,
2961
+ free: false
2962
+ });
2963
+ offset += len;
2964
+ }
2965
+ else if (pct !== null && occ > 0) {
2966
+ segs.push({
2967
+ key: "fill",
2968
+ color: fallbackColor,
2969
+ len: circumference * occ,
2970
+ offset: 0,
2971
+ free: false
2972
+ });
2973
+ offset = circumference * occ;
2974
+ }
2975
+ if (pct !== null && offset < circumference) segs.push({
2976
+ key: "free",
2977
+ color: "",
2978
+ len: circumference - offset,
2979
+ offset,
2980
+ free: true
2981
+ });
2982
+ return segs;
2983
+ }
2984
+ /** Session-switch navigation, fail-soft: a stale row (list rebuilt between snapshot and click) loses its open() race and is ignored. */
2985
+ function openAgentSession(face, id) {
2986
+ if (face === null || typeof face.open !== "function") return;
2987
+ try {
2988
+ face.open(id);
2989
+ } catch {}
2990
+ }
2991
+ /** Narrow `ctx.get('sessions')` to the card's face (null = harness without the outward sessions service). */
2992
+ function sessionsFaceOf(ctx) {
2993
+ const rec = asRecord(ctx.get("sessions"));
2994
+ if (rec === null) return null;
2995
+ const list = asRecord(rec.list);
2996
+ if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
2997
+ return rec;
2998
+ }
2999
+ /** Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales). */
3000
+ function fmtDuration(ms) {
3001
+ if (!Number.isFinite(ms) || ms < 0) return "—";
3002
+ const s = Math.round(ms / 1e3);
3003
+ if (s < 60) return `${s}s`;
3004
+ const m = Math.floor(s / 60);
3005
+ if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
3006
+ return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
3007
+ }
3008
+ //#endregion
3009
+ //#region src/client/components/agentGraph.tsx
3010
+ /** Caption box height under a node (3 wrapped label lines + the tokens line). */
3011
+ const CAPTION_H = 60;
3012
+ /** Fallback arc color for pressure-only nodes (no composition data), by fill ratio. */
3013
+ function ringColorOf(pct) {
3014
+ if (pct === null) return "var(--dsw-alias-border-l1)";
3015
+ if (pct >= 90) return "#ef4444";
3016
+ if (pct >= 70) return "#f59e0b";
3017
+ return "#22c55e";
3018
+ }
3019
+ function makeAgentGraph(ctx, kit) {
3020
+ const { t, fmt, catLabel } = kit;
3021
+ function AgentGraph(props) {
3022
+ const face = React.useMemo(() => sessionsFaceOf(ctx), []);
3023
+ const subscribe = React.useCallback((fn) => {
3024
+ if (face === null) return () => {};
3025
+ /* v8 ignore next 2 -- sessionsFaceOf returns a face only after proving list.subscribe. */
3026
+ if (face.list === void 0) return () => {};
3027
+ return face.list.subscribe(fn);
3028
+ }, [face]);
3029
+ const getSnapshot = React.useCallback(() => {
3030
+ if (face === null) return null;
3031
+ /* v8 ignore next 2 -- sessionsFaceOf proves list before returning the face. */
3032
+ if (face.list === void 0) return null;
3033
+ return face.list.getSnapshot();
3034
+ }, [face]);
3035
+ const snapshot = React.useSyncExternalStore(subscribe, getSnapshot);
3036
+ const sessionId = props.sessionId;
3037
+ const [hoverId, setHoverId] = React.useState(null);
3038
+ const stageRef = React.useRef(null);
3039
+ const [stageWidth, setStageWidth] = React.useState(0);
3040
+ React.useEffect(() => {
3041
+ /* v8 ignore start -- jsdom has neither ResizeObserver nor layout; tests exercise the natural-pitch fallback (stageWidth 0). */
3042
+ const el = stageRef.current;
3043
+ if (el === null || typeof ResizeObserver !== "function") return;
3044
+ setStageWidth(el.clientWidth);
3045
+ const observer = new ResizeObserver(() => {
3046
+ setStageWidth(el.clientWidth);
3047
+ });
3048
+ observer.observe(el);
3049
+ return () => {
3050
+ observer.disconnect();
3051
+ };
3052
+ /* v8 ignore stop */
3053
+ }, []);
3054
+ React.useEffect(() => {
3055
+ if (face === null || typeof sessionId !== "string" || sessionId === "") return;
3056
+ if (typeof face.refreshSubagents !== "function") return;
3057
+ face.refreshSubagents(sessionId).catch(() => {});
3058
+ }, [face, sessionId]);
3059
+ const built = React.useMemo(() => {
3060
+ const forest = agentForestOf(snapshot, sessionId, props.self);
3061
+ return forest !== null ? {
3062
+ forest,
3063
+ layout: layoutForest(forest, stageWidth)
3064
+ } : null;
3065
+ }, [
3066
+ snapshot,
3067
+ sessionId,
3068
+ props.self,
3069
+ stageWidth
3070
+ ]);
3071
+ if (built === null) return null;
3072
+ const { forest, layout } = built;
3073
+ const byId = new Map(forest.nodes.map((n) => [n.id, n]));
3074
+ /* v8 ignore next 1 -- agentForestOf anchors the forest at the current
3075
+ session, so a current node always exists. */
3076
+ const current = forest.nodes.find((n) => n.isCurrent) ?? forest.nodes[0];
3077
+ const inspected = (hoverId !== null ? byId.get(hoverId) : void 0) ?? current;
3078
+ const runningCount = forest.nodes.filter((n) => n.running).length;
3079
+ let totalTokens = 0;
3080
+ for (const n of forest.nodes) totalTokens += n.head !== null ? n.head.tokens : 0;
3081
+ const open = (id) => {
3082
+ if (id === current.id) return;
3083
+ openAgentSession(face, id);
3084
+ };
3085
+ const keyOpen = (id) => (ev) => {
3086
+ if (ev.key !== "Enter" && ev.key !== " ") return;
3087
+ ev.preventDefault();
3088
+ open(id);
3089
+ };
3090
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-card lc-agents" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("agents.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("agents.sub"))), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-chips" }, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.count", { n: forest.nodes.length + forest.overflow })), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" + (runningCount > 0 ? " lc-agents-chip-on" : "") }, t("agents.chip.running", { n: runningCount })), totalTokens > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.chip.tokens", { n: fmt(totalTokens) })) : null, forest.overflow > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-chip" }, t("agents.more", { n: forest.overflow })) : null), /* @__PURE__ */ React.createElement("div", {
3091
+ className: "lc-agents-stage",
3092
+ ref: stageRef
3093
+ }, /* @__PURE__ */ React.createElement("svg", {
3094
+ className: "lc-agents-svg",
3095
+ width: layout.width,
3096
+ height: layout.height,
3097
+ viewBox: `0 0 ${layout.width} ${layout.height}`
3098
+ }, layout.links.map((link) => {
3099
+ const d = `M ${link.x1} ${link.y1} L ${link.x2} ${link.y2}`;
3100
+ return /* @__PURE__ */ React.createElement("g", { key: link.to }, /* @__PURE__ */ React.createElement("path", {
3101
+ className: "lc-agents-link" + (link.running ? " lc-agents-link-live" : ""),
3102
+ d,
3103
+ stroke: link.color,
3104
+ fill: "none"
3105
+ }), link.running ? /* @__PURE__ */ React.createElement("path", {
3106
+ className: "lc-agents-flow",
3107
+ d,
3108
+ stroke: link.color,
3109
+ fill: "none"
3110
+ }) : null);
3111
+ }), forest.nodes.map((node) => {
3112
+ const point = layout.points.find((p) => p.id === node.id);
3113
+ /* v8 ignore next 2 -- layoutForest positions every forest node,
3114
+ so the lookup never misses. */
3115
+ if (point === void 0) return null;
3116
+ return /* @__PURE__ */ React.createElement(AgentNodeView, {
3117
+ key: node.id,
3118
+ node,
3119
+ x: point.x,
3120
+ y: point.y,
3121
+ captionW: layout.captionW,
3122
+ hovered: hoverId === node.id,
3123
+ onHover: setHoverId,
3124
+ onOpen: open,
3125
+ onKeyOpen: keyOpen(node.id),
3126
+ t,
3127
+ fmt
3128
+ });
3129
+ }))), forest.solo ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-agents-solo" }, t("agents.solo")) : null, /* @__PURE__ */ React.createElement(Inspector, {
3130
+ node: inspected,
3131
+ t,
3132
+ fmt
3133
+ }), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-legend" }, CATS.map((c) => /* @__PURE__ */ React.createElement("span", {
3134
+ key: c.key,
3135
+ className: "lc-agents-legend-item"
3136
+ }, /* @__PURE__ */ React.createElement("i", { style: { background: c.color } }), catLabel(c.key))), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-free" }), t("agents.legend.free")), /* @__PURE__ */ React.createElement("span", { className: "lc-agents-legend-item" }, /* @__PURE__ */ React.createElement("i", { className: "lc-agents-legend-edge" }), t("agents.running"))));
3137
+ }
3138
+ return AgentGraph;
3139
+ }
3140
+ function AgentNodeView(props) {
3141
+ const { node, x, y, captionW } = props;
3142
+ const pct = node.head !== null ? node.head.pct : null;
3143
+ const ring = 2 * Math.PI * 20;
3144
+ const segs = node.head !== null ? ringSegments(node.head.parts, pct, 20, ringColorOf(pct)) : [];
3145
+ const cls = "lc-agent-node" + (node.isCurrent ? " lc-agent-self" : "") + (node.running ? " lc-agent-running" : "") + (node.completed && !node.running ? " lc-agent-done" : "") + (props.hovered ? " lc-agent-hover" : "") + (node.isCurrent ? "" : " lc-agent-clickable");
3146
+ return /* @__PURE__ */ React.createElement("g", {
3147
+ className: cls,
3148
+ transform: `translate(${x}, ${y})`,
3149
+ "data-agent": node.id,
3150
+ role: node.isCurrent ? "img" : "button",
3151
+ tabIndex: node.isCurrent ? void 0 : 0,
3152
+ onClick: () => {
3153
+ props.onOpen(node.id);
3154
+ },
3155
+ onKeyDown: props.onKeyOpen,
3156
+ onMouseEnter: () => {
3157
+ props.onHover(node.id);
3158
+ },
3159
+ onMouseLeave: () => {
3160
+ props.onHover(null);
3161
+ }
3162
+ }, /* @__PURE__ */ React.createElement("circle", {
3163
+ className: "lc-agent-halo",
3164
+ r: 35
3165
+ }), /* @__PURE__ */ React.createElement("circle", {
3166
+ className: "lc-agent-track",
3167
+ r: 26
3168
+ }), segs.map((seg) => /* @__PURE__ */ React.createElement("circle", {
3169
+ key: seg.key,
3170
+ className: "lc-agent-seg" + (seg.free ? " lc-agent-free" : ""),
3171
+ r: 20,
3172
+ stroke: seg.free ? void 0 : seg.color,
3173
+ strokeDasharray: `${seg.len} ${ring - seg.len}`,
3174
+ strokeDashoffset: -seg.offset,
3175
+ transform: "rotate(-90)"
3176
+ })), /* @__PURE__ */ React.createElement("text", {
3177
+ className: "lc-agent-pct",
3178
+ textAnchor: "middle",
3179
+ dy: "0.32em"
3180
+ }, pct !== null ? `${pct}%` : node.head !== null ? props.fmt(node.head.tokens) : "—"), /* @__PURE__ */ React.createElement("foreignObject", {
3181
+ x: -captionW / 2,
3182
+ y: 34,
3183
+ width: captionW,
3184
+ height: CAPTION_H
3185
+ }, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-caption" }, /* @__PURE__ */ React.createElement("div", { className: "lc-agent-label" }, node.label, node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agent-self-badge" }, props.t("agents.self")) : null), /* @__PURE__ */ React.createElement("div", { className: "lc-agent-tokens" }, node.head !== null ? props.fmt(node.head.tokens) : "—"))));
3186
+ }
3187
+ /** The detail strip mirroring the hovered (or current) node: identity, occupancy, activity, and the open hint. */
3188
+ function Inspector(props) {
3189
+ const { node, t, fmt } = props;
3190
+ const bits = [];
3191
+ if (node.head !== null) {
3192
+ const head = node.head;
3193
+ const window = head.window !== void 0 ? ` / ${fmt(head.window)}` : "";
3194
+ const pct = head.pct !== null ? ` · ${head.pct}%` : "";
3195
+ bits.push(`${fmt(head.tokens)}${window}${pct}`);
3196
+ }
3197
+ if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
3198
+ if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
3199
+ if (node.durationMs !== null) bits.push(fmtDuration(node.durationMs));
3200
+ return /* @__PURE__ */ React.createElement("div", { className: "lc-agents-inspector" }, /* @__PURE__ */ React.createElement("b", { className: "lc-agents-inspector-name" }, node.label), node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t("agents.self")) : null, node.running ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge lc-agents-badge-on" }, t("agents.running")) : null, node.identity !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-badge" }, t(node.identity.mode === "one-shot" ? "agents.oneshot" : "agents.continuable")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-stats" }, bits.join(" · ")), !node.isCurrent ? /* @__PURE__ */ React.createElement("span", { className: "lc-agents-inspector-open" }, t("agents.open")) : null);
3201
+ }
2597
3202
  //#endregion
2598
3203
  //#region src/client/components/events.tsx
2599
3204
  const EVENT_ICONS = {
@@ -2656,6 +3261,14 @@ window.__ModuleLoader__.load({
2656
3261
  eventAt
2657
3262
  };
2658
3263
  }
3264
+ /**
3265
+ * Set the native `title` on every label whose text overflows its box (the styled tips cover cards, but a plain
3266
+ * ellipsis row still needs the native fallback); reads (scrollWidth/clientWidth) and writes (title) stay separate
3267
+ * from layout-affecting work, and callers gate WHEN this runs so it never becomes a per-render forced layout.
3268
+ */
3269
+ function syncTitles(root) {
3270
+ for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
3271
+ }
2659
3272
  function makeEventList(kit) {
2660
3273
  const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
2661
3274
  return function EventList(props) {
@@ -2663,26 +3276,29 @@ window.__ModuleLoader__.load({
2663
3276
  React.useLayoutEffect(() => {
2664
3277
  const root = rootRef.current;
2665
3278
  if (!root) return;
2666
- const sync = () => {
2667
- for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
3279
+ syncTitles(root);
3280
+ }, [props.events]);
3281
+ React.useEffect(() => {
3282
+ const onResize = () => {
3283
+ const root = rootRef.current;
3284
+ if (root !== null) syncTitles(root);
2668
3285
  };
2669
- sync();
2670
- window.addEventListener("resize", sync);
3286
+ window.addEventListener("resize", onResize);
2671
3287
  return () => {
2672
- window.removeEventListener("resize", sync);
3288
+ window.removeEventListener("resize", onResize);
2673
3289
  };
2674
- });
3290
+ }, []);
2675
3291
  if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
2676
3292
  const sorted = props.events.slice().reverse();
2677
3293
  return /* @__PURE__ */ React.createElement("div", {
2678
3294
  className: "lc-events",
2679
3295
  ref: rootRef
2680
- }, sorted.map((ev, i) => {
3296
+ }, sorted.map((ev) => {
2681
3297
  const label = eventLabel(ev);
2682
3298
  const at = eventAt(ev);
2683
3299
  const glyph = ev.kind === "inject" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconPlusOutline16, null) : ev.kind === "model" ? /* @__PURE__ */ React.createElement(_deepseek_ai_dsh_client_ui_primitives.IconBranchOutline16, null) : EVENT_ICONS[ev.kind] || "•";
2684
3300
  return /* @__PURE__ */ React.createElement("div", {
2685
- key: `${ev.seq}-${i}`,
3301
+ key: ev.seq,
2686
3302
  className: "lc-event"
2687
3303
  }, /* @__PURE__ */ React.createElement("span", { className: "lc-event-icon lc-event-" + ev.kind }, glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-kind lc-kind-" + ev.kind }, t("kind." + ev.kind)), /* @__PURE__ */ React.createElement("span", { className: "lc-event-label" }, label), at !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-at" }, at) : null, ev.tokens ? /* @__PURE__ */ React.createElement("span", { className: "lc-event-tokens" + (ev.kind === "inject" ? " lc-up" : " lc-down") }, (ev.kind === "inject" ? "+" : "−") + fmt(ev.tokens)) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-event-time" }, fmtTime(ev.time)));
2688
3304
  }));
@@ -2690,8 +3306,6 @@ window.__ModuleLoader__.load({
2690
3306
  }
2691
3307
  //#endregion
2692
3308
  //#region src/client/components/fileCard.tsx
2693
- /** An expanded file lists at most this many ops before a "+n earlier" line. */
2694
- const MAX_OPS = 8;
2695
3309
  function makeFileCard(kit, settings) {
2696
3310
  const { t, fmt, fmtTime } = kit;
2697
3311
  function formGlyph(form) {
@@ -2708,7 +3322,7 @@ window.__ModuleLoader__.load({
2708
3322
  function DeltaPair(props) {
2709
3323
  return /* @__PURE__ */ React.createElement("span", { className: "lc-fa-delta" }, props.added > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-up" }, "+" + fmt(props.added)) : null, props.removed > 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-down" }, "−" + fmt(props.removed)) : null);
2710
3324
  }
2711
- return function FileCard(props) {
3325
+ return React.memo(function FileCard(props) {
2712
3326
  const { activity } = props;
2713
3327
  const [filter, setFilter] = React.useState("all");
2714
3328
  const [sort, setSort] = React.useState(() => settings.defaultFileSort());
@@ -2824,7 +3438,7 @@ window.__ModuleLoader__.load({
2824
3438
  }) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
2825
3439
  className: "lc-br-err-dot",
2826
3440
  title: t("files.errs", { n: e.errs })
2827
- }) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.slice(0, MAX_OPS).map((op) => {
3441
+ }) : null, e.ops[0].time !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-time" }, fmtTime(e.ops[0].time)) : null), open ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-ops" }, e.ops.map((op) => {
2828
3442
  const onLocate = props.onLocate;
2829
3443
  return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
2830
3444
  key: op.seq,
@@ -2838,25 +3452,38 @@ window.__ModuleLoader__.load({
2838
3452
  key: op.seq,
2839
3453
  className: "lc-fa-op"
2840
3454
  }, opLine(op));
2841
- }), e.ops.length > MAX_OPS ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-more" }, t("files.moreOps", { n: e.ops.length - MAX_OPS })) : null) : null);
2842
- }))), activity.unresolved > 0 ? /* @__PURE__ */ React.createElement("div", { className: "lc-fa-note" }, t("files.unresolved", { n: activity.unresolved })) : null);
2843
- };
3455
+ })) : null);
3456
+ }))));
3457
+ });
2844
3458
  }
2845
3459
  const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
2846
3460
  const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
2847
3461
  //#endregion
2848
3462
  //#region src/client/latestVersion.ts
2849
3463
  /**
2850
- * One live check for the Plugin-info card: lazy with 1h TTL; every failure
2851
- * mode narrows to null so the card silently keeps its static version.
3464
+ * One live check for the Plugin-info card: lazy with 1h TTL; registries
3465
+ * are tried strictly in order official npm first, npmmirror only as a
3466
+ * fallback — and one-at-a-time, never in parallel. Each source's failure
3467
+ * (non-ok response, missing/non-string version, network error) advances
3468
+ * to the next one; only an all-source failure resolves to null so the
3469
+ * card silently keeps its static version.
2852
3470
  */
2853
- const REGISTRY_URL = "https://registry.npmjs.org/dsh-context/latest";
3471
+ const REGISTRY_HOSTS = ["https://registry.npmjs.org", "https://registry.npmmirror.com"];
2854
3472
  const TTL_MS = 36e5;
2855
3473
  let cached = null;
3474
+ function readVersion(host) {
3475
+ return fetch(host + "/dsh-context/latest").then((res) => res.ok ? res.json() : null).then((body) => body !== null && typeof body.version === "string" ? body.version : null).catch(() => null);
3476
+ }
2856
3477
  function fetchLatestVersion() {
2857
3478
  if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
2858
3479
  at: Date.now(),
2859
- promise: fetch(REGISTRY_URL).then((res) => res.ok ? res.json() : null).then((body) => body !== null && typeof body.version === "string" ? body.version : null).catch(() => null)
3480
+ promise: (async () => {
3481
+ for (const host of REGISTRY_HOSTS) {
3482
+ const v = await readVersion(host);
3483
+ if (v !== null) return v;
3484
+ }
3485
+ return null;
3486
+ })()
2860
3487
  };
2861
3488
  return cached.promise;
2862
3489
  }
@@ -2885,7 +3512,7 @@ window.__ModuleLoader__.load({
2885
3512
  return function PluginInfo() {
2886
3513
  const [latest, setLatest] = React.useState(null);
2887
3514
  React.useEffect(() => {
2888
- if ("0.33.1".includes("-dev")) return;
3515
+ if ("0.34.0".includes("-dev")) return;
2889
3516
  let on = true;
2890
3517
  fetchLatestVersion().then((v) => {
2891
3518
  if (on && v) setLatest(v);
@@ -2894,8 +3521,8 @@ window.__ModuleLoader__.load({
2894
3521
  on = false;
2895
3522
  };
2896
3523
  }, []);
2897
- const update = latest !== null && isNewerVersion(latest, "0.33.1") ? latest : null;
2898
- const nameValue = ["dsh-context (v0.33.1)"];
3524
+ const update = latest !== null && isNewerVersion(latest, "0.34.0") ? latest : null;
3525
+ const nameValue = ["dsh-context (v0.34.0)"];
2899
3526
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2900
3527
  key: "update",
2901
3528
  className: "lc-pi-update"
@@ -2910,21 +3537,22 @@ window.__ModuleLoader__.load({
2910
3537
  /**
2911
3538
  * One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
2912
3539
  * `.lc-stat-tip` pattern); the content span keeps the native title (preview + locate hint), so the two never stack.
2913
- * Clickable when the browser linkage is wired.
3540
+ * Clickable when the browser linkage is wired AND the row carries a node (the always-present In row's empty state stays inert).
2914
3541
  */
2915
3542
  function BriefRow(props) {
2916
- const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
2917
3543
  const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
2918
3544
  className: "lc-brief-tip",
2919
3545
  role: "tooltip"
2920
3546
  }, props.tagTip)), props.children);
2921
- if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: cls }, inner);
3547
+ if (props.node === void 0 || props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-brief-row" }, inner);
3548
+ const node = props.node;
3549
+ const onLocate = props.onLocate;
2922
3550
  const locate = () => {
2923
- props.onLocate?.(props.node, props.isResponse);
3551
+ onLocate(node, props.isResponse);
2924
3552
  };
2925
3553
  return /* @__PURE__ */ React.createElement("button", {
2926
3554
  type: "button",
2927
- className: cls,
3555
+ className: "lc-brief-row lc-brief-row-link",
2928
3556
  onClick: locate
2929
3557
  }, inner);
2930
3558
  }
@@ -2998,9 +3626,12 @@ window.__ModuleLoader__.load({
2998
3626
  function factTitle(tag, text) {
2999
3627
  return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
3000
3628
  }
3629
+ /**
3630
+ * The brief's container ALWAYS renders — a fixed three-row lane (`.lc-brief`'s min-height) so scrubbing the chart never
3631
+ * changes the panel's height; unknown or empty steps just leave parts of the lane blank instead of collapsing it.
3632
+ */
3001
3633
  function BriefSection(props) {
3002
- const { opener, inputs, response } = props.brief;
3003
- if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
3634
+ const { opener, inputs, response } = props.brief ?? { inputs: [] };
3004
3635
  const convOf = props.convOf ?? (() => void 0);
3005
3636
  const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
3006
3637
  const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
@@ -3032,13 +3663,13 @@ window.__ModuleLoader__.load({
3032
3663
  }, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
3033
3664
  className: "lc-brief-text",
3034
3665
  title: factTitle(openerParts.tag, openerParts.text) + hint
3035
- }, openerParts.text) : null) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3666
+ }, openerParts.text) : null) : null, /* @__PURE__ */ React.createElement(BriefRow, {
3036
3667
  tag: t("brief.input"),
3037
3668
  tagTip: t("brief.inputTip"),
3038
3669
  node: inputs[0],
3039
3670
  isResponse: false,
3040
3671
  onLocate: props.onLocate
3041
- }, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : null, response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3672
+ }, inputs.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, inputs.slice(0, MAX_CHIPS).map((n) => nodeChip(n, locateChip?.(n))), inputs.length > MAX_CHIPS ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-more" }, t("brief.more", { n: inputs.length - MAX_CHIPS })) : null) : /* @__PURE__ */ React.createElement("span", { className: "lc-brief-empty" }, t("brief.noInputs"))), response !== void 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
3042
3673
  tag: t("brief.reply"),
3043
3674
  tagTip: t("brief.replyTip"),
3044
3675
  node: response,
@@ -3046,7 +3677,7 @@ window.__ModuleLoader__.load({
3046
3677
  onLocate: props.onLocate
3047
3678
  }, nodeChip(response, void 0, true)) : null);
3048
3679
  }
3049
- return function RequestDetail(props) {
3680
+ return React.memo(function RequestDetail(props) {
3050
3681
  const req = props.request;
3051
3682
  if (!req) return null;
3052
3683
  const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
@@ -3078,11 +3709,11 @@ window.__ModuleLoader__.load({
3078
3709
  return /* @__PURE__ */ React.createElement("div", { className: "lc-detail" }, /* @__PURE__ */ React.createElement("div", { className: "lc-detail-head" }, /* @__PURE__ */ React.createElement("b", null, head), marker !== null && markerAt !== null ? /* @__PURE__ */ React.createElement("span", {
3079
3710
  className: "lc-detail-marker",
3080
3711
  title: eventLabel(marker)
3081
- }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), props.brief !== null && props.brief !== void 0 ? /* @__PURE__ */ React.createElement(BriefSection, {
3712
+ }, "✂ " + markerAt) : null, isTurn ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("detail.lastStep")) : null, delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-tag" }, t("gran.delta")) : null, /* @__PURE__ */ React.createElement("span", { className: "lc-detail-time" }, fmtTime(req.time)), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" + (net > 0 ? " lc-detail-metric-up" : net < 0 ? " lc-detail-metric-down" : "") }, t("tip.delta", { n: (net > 0 ? "+" : "") + fmt(net) })) : null, !delta && req.prompt !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.actual", { n: fmt(req.prompt) })) : null, !delta && req.output !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.output", { n: fmt(req.output) })) : null, !delta && req.prompt !== void 0 && req.cacheRead !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-metric" }, t("detail.cache", { n: cacheHitPercent$1(req.cacheRead, req.prompt) ?? "—" })) : null), /* @__PURE__ */ React.createElement(BriefSection, {
3082
3713
  brief: props.brief,
3083
3714
  convOf: props.convOf,
3084
3715
  onLocate: props.onLocate
3085
- }) : null, /* @__PURE__ */ React.createElement(StackedBar, {
3716
+ }), /* @__PURE__ */ React.createElement(StackedBar, {
3086
3717
  parts,
3087
3718
  height: 10
3088
3719
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
@@ -3105,7 +3736,7 @@ window.__ModuleLoader__.load({
3105
3736
  }
3106
3737
  })), delta ? /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" + (v > 0 ? " lc-detail-num-up" : v < 0 ? " lc-detail-num-down" : "") }, (v > 0 ? "+" : "") + fmt(v)) : /* @__PURE__ */ React.createElement("span", { className: "lc-detail-num" }, "≈" + fmt(v)), /* @__PURE__ */ React.createElement("span", { className: "lc-detail-pct" }, !delta && req.total > 0 ? `${Math.round(v / req.total * 100)}%` : ""));
3107
3738
  })));
3108
- };
3739
+ });
3109
3740
  }
3110
3741
  //#endregion
3111
3742
  //#region src/client/cost.ts
@@ -3435,25 +4066,8 @@ window.__ModuleLoader__.load({
3435
4066
  const scrollRef = React.useRef(null);
3436
4067
  const scrolledOnce = React.useRef(false);
3437
4068
  const lastGranRef = React.useRef(props.granularity);
3438
- const [edges, setEdges] = React.useState({
3439
- left: false,
3440
- right: false
3441
- });
3442
- const edgesRef = React.useRef(edges);
3443
- const updateEdges = (el) => {
3444
- const left = el.scrollLeft > 4;
3445
- const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 4;
3446
- const prev = edgesRef.current;
3447
- if (prev.left === left && prev.right === right) return;
3448
- edgesRef.current = {
3449
- left,
3450
- right
3451
- };
3452
- setEdges({
3453
- left,
3454
- right
3455
- });
3456
- };
4069
+ const lastSeqRef = React.useRef(0);
4070
+ const prevScrollWidthRef = React.useRef(0);
3457
4071
  /**
3458
4072
  * Keep each turn label centered within its block's VISIBLE slice so it never scrolls out while any part of the block is on screen
3459
4073
  * (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
@@ -3488,6 +4102,9 @@ window.__ModuleLoader__.load({
3488
4102
  /* v8 ignore next 1 -- the scroll div renders unconditionally and React
3489
4103
  attaches refs before layout effects run; el is never null here. */
3490
4104
  if (el === null) return;
4105
+ const newestSeq = requests.length === 0 ? 0 : requests[requests.length - 1].seq;
4106
+ const grew = newestSeq !== lastSeqRef.current;
4107
+ const widthBeforeAppend = prevScrollWidthRef.current;
3491
4108
  if (props.granularity !== lastGranRef.current) {
3492
4109
  lastGranRef.current = props.granularity;
3493
4110
  scrolledOnce.current = false;
@@ -3499,14 +4116,19 @@ window.__ModuleLoader__.load({
3499
4116
  el.scrollLeft = Math.max(0, gi * 16 + BAR_W / 2 - el.clientWidth / 2);
3500
4117
  }
3501
4118
  props.onFocusTurnHandled();
3502
- }
3503
- if (!scrolledOnce.current) {
4119
+ } else if (!scrolledOnce.current) {
3504
4120
  scrolledOnce.current = true;
3505
4121
  el.scrollLeft = el.scrollWidth;
3506
- } else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) el.scrollLeft = el.scrollWidth;
3507
- updateEdges(el);
4122
+ } else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
4123
+ lastSeqRef.current = newestSeq;
4124
+ prevScrollWidthRef.current = el.scrollWidth;
3508
4125
  updateTurnLabels(el);
3509
- });
4126
+ syncTip(el);
4127
+ }, [
4128
+ props.granularity,
4129
+ props.focusTurn,
4130
+ requests
4131
+ ]);
3510
4132
  const tipOf = (req) => {
3511
4133
  const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
3512
4134
  t: req.turn ?? 0,
@@ -3527,17 +4149,43 @@ window.__ModuleLoader__.load({
3527
4149
  };
3528
4150
  const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
3529
4151
  const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
4152
+ const tipColRef = React.useRef(0);
4153
+ /**
4154
+ * Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
4155
+ * content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
4156
+ * reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
4157
+ * scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
4158
+ * style write; unchanged transforms write nothing.
4159
+ */
4160
+ const syncTip = (el) => {
4161
+ /* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
4162
+ const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
4163
+ if (tip === null) return;
4164
+ const lw = tip.offsetWidth;
4165
+ const cw = el.clientWidth;
4166
+ const half = Math.min(lw / 2, cw / 2);
4167
+ const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
4168
+ const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
4169
+ if (tip.style.transform !== next) tip.style.transform = next;
4170
+ };
4171
+ React.useLayoutEffect(() => {
4172
+ /* v8 ignore next 1 -- the scroll div renders unconditionally and React attaches refs before
4173
+ layout effects run; el is never null here. */
4174
+ if (scrollRef.current === null) return;
4175
+ tipColRef.current = hoveredIdx >= 0 ? hoveredIdx * 16 + BAR_W / 2 : 0;
4176
+ syncTip(scrollRef.current);
4177
+ });
3530
4178
  return /* @__PURE__ */ React.createElement("div", { className: "lc-chartrow" }, /* @__PURE__ */ React.createElement("div", { className: "lc-axis" }, delta ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, (maxUp > 0 ? "+" : "") + fmt(maxUp)), /* @__PURE__ */ React.createElement("span", {
3531
4179
  className: "lc-axis-mid",
3532
4180
  style: { top: `${13 + upPx}px` }
3533
- }, "0"), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, (maxDown > 0 ? "-" : "") + fmt(maxDown))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0"))), /* @__PURE__ */ React.createElement("div", {
4181
+ }, "0"), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, (maxDown > 0 ? "-" : "") + fmt(maxDown))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-axis-top" }, fmt(maxTotal)), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-mid" }, fmt(Math.round(maxTotal / 2))), /* @__PURE__ */ React.createElement("span", { className: "lc-axis-bot" }, "0"))), /* @__PURE__ */ React.createElement("div", { className: "lc-chart-wrap" }, /* @__PURE__ */ React.createElement("div", {
3534
4182
  className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
3535
4183
  ref: scrollRef,
3536
4184
  onScroll: (e) => {
3537
- updateEdges(e.currentTarget);
3538
4185
  updateTurnLabels(e.currentTarget);
4186
+ syncTip(e.currentTarget);
3539
4187
  }
3540
- }, edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null, /* @__PURE__ */ React.createElement("div", {
4188
+ }, /* @__PURE__ */ React.createElement("div", {
3541
4189
  className: "lc-chart",
3542
4190
  onMouseLeave: () => {
3543
4191
  props.onHover(null);
@@ -3558,10 +4206,7 @@ window.__ModuleLoader__.load({
3558
4206
  deltaScale: delta ? deltaScale : void 0,
3559
4207
  onSelect: props.onSelect,
3560
4208
  onHover: props.onHover
3561
- }))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", {
3562
- className: "lc-chart-tip",
3563
- style: { left: `${hoveredIdx * 16 + BAR_W / 2}px` }
3564
- }, tipOf(hoveredReq)) : null, /* @__PURE__ */ React.createElement("div", {
4209
+ }))), /* @__PURE__ */ React.createElement("div", {
3565
4210
  className: "lc-turns",
3566
4211
  onMouseLeave: () => {
3567
4212
  props.onHoverTurn(null);
@@ -3583,7 +4228,7 @@ window.__ModuleLoader__.load({
3583
4228
  props.onPickTurn(grp.turn);
3584
4229
  }
3585
4230
  }, /* @__PURE__ */ React.createElement("span", { className: "lc-turn-label" }, `T${grp.turn}`));
3586
- }))), edges.right ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-r" }) : null);
4231
+ }))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-tip" }, tipOf(hoveredReq)) : null));
3587
4232
  };
3588
4233
  }
3589
4234
  //#endregion
@@ -3607,6 +4252,7 @@ window.__ModuleLoader__.load({
3607
4252
  const StatsBoard = makeStatsBoard(kit);
3608
4253
  const PluginInfo = makePluginInfo(kit);
3609
4254
  const ContextBrowser = makeContextBrowser(kit, StackedBar);
4255
+ const AgentGraph = makeAgentGraph(ctx, kit);
3610
4256
  const ErrorBoundary = makeErrorBoundary(t);
3611
4257
  function ContextViewBody(props) {
3612
4258
  const sessionId = props.sessionId;
@@ -3676,16 +4322,6 @@ window.__ModuleLoader__.load({
3676
4322
  for (const n of convNodes ?? []) m.set(n.seq, n);
3677
4323
  return m;
3678
4324
  }, [convNodes]);
3679
- if (!data) return /* @__PURE__ */ React.createElement("div", {
3680
- className: "lc-root",
3681
- ref: rootRef
3682
- }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
3683
- const markerOf = (req) => {
3684
- const i = displayRequests.indexOf(req);
3685
- /* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
3686
- an element of the very array indexOf scans. */
3687
- return i >= 0 ? markers[i] : void 0;
3688
- };
3689
4325
  let pinnedIdx = -1;
3690
4326
  for (let i = 0; i < displayRequests.length; i++) if (displayRequests[i].seq === selectedSeq) pinnedIdx = i;
3691
4327
  const pinnedReq = pinnedIdx >= 0 ? displayRequests[pinnedIdx] : null;
@@ -3699,23 +4335,24 @@ window.__ModuleLoader__.load({
3699
4335
  if (activeIdx < 0) activeIdx = pinnedIdx;
3700
4336
  if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
3701
4337
  const activeReq = activeIdx >= 0 ? displayRequests[activeIdx] : null;
3702
- const brief = activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null;
3703
- const convOf = (seq) => bySeq.get(seq);
3704
4338
  let filesBefore = null;
3705
4339
  if (activeReq !== null) {
3706
4340
  const ri = requests.findIndex((r) => r.seq === activeReq.seq);
3707
4341
  filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
3708
4342
  }
3709
- let fileScope = t("files.scopeLatest");
3710
- if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
3711
- t: activeReq.turn ?? 0,
3712
- n: activeReq.stepCount
3713
- }) : t("detail.step", {
3714
- t: activeReq.turn ?? 0,
3715
- s: activeReq.step ?? 0
3716
- });
3717
- const fileActivity = activityOf(briefList, convOf, filesBefore);
3718
- const locateFileOp = (op) => {
4343
+ const brief = React.useMemo(() => activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null, [
4344
+ activeReq,
4345
+ briefList,
4346
+ displayRequests,
4347
+ activeIdx
4348
+ ]);
4349
+ const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
4350
+ const fileActivity = React.useMemo(() => activityOf(briefList, convOf, filesBefore), [
4351
+ briefList,
4352
+ convOf,
4353
+ filesBefore
4354
+ ]);
4355
+ const locateFileOp = React.useCallback((op) => {
3719
4356
  const step = locateStepOf(requests, op.seq, op.gone);
3720
4357
  if (step === null) return;
3721
4358
  setNodeFocus({
@@ -3723,8 +4360,8 @@ window.__ModuleLoader__.load({
3723
4360
  seq: op.seq,
3724
4361
  cat: "tool"
3725
4362
  });
3726
- };
3727
- const locateNode = (node, isResponse) => {
4363
+ }, [requests]);
4364
+ const locateNode = React.useCallback((node, isResponse) => {
3728
4365
  /* v8 ignore next 1 -- locateNode is only wired to brief rows, and
3729
4366
  brief !== null guarantees activeReq !== null in the same closure. */
3730
4367
  if (activeReq === null) return;
@@ -3735,7 +4372,30 @@ window.__ModuleLoader__.load({
3735
4372
  seq: node.seq,
3736
4373
  cat: node.cat
3737
4374
  });
4375
+ }, [
4376
+ activeReq,
4377
+ activeIdx,
4378
+ displayRequests
4379
+ ]);
4380
+ if (!data) return /* @__PURE__ */ React.createElement("div", {
4381
+ className: "lc-root",
4382
+ ref: rootRef
4383
+ }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
4384
+ const markerOf = (req) => {
4385
+ const i = displayRequests.indexOf(req);
4386
+ /* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
4387
+ an element of the very array indexOf scans. */
4388
+ return i >= 0 ? markers[i] : void 0;
3738
4389
  };
4390
+ const head = headlineOf(data, pressure, breakdown);
4391
+ let fileScope = t("files.scopeLatest");
4392
+ if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
4393
+ t: activeReq.turn ?? 0,
4394
+ n: activeReq.stepCount
4395
+ }) : t("detail.step", {
4396
+ t: activeReq.turn ?? 0,
4397
+ s: activeReq.step ?? 0
4398
+ });
3739
4399
  let activeTurn = hoverTurn;
3740
4400
  if (activeTurn === null && hoveredSeq !== null) {
3741
4401
  for (const req of displayRequests) if (req.seq === hoveredSeq) {
@@ -3743,9 +4403,9 @@ window.__ModuleLoader__.load({
3743
4403
  break;
3744
4404
  }
3745
4405
  }
3746
- const head = headlineOf(data, pressure, breakdown);
3747
4406
  const localeSvc = ctx.get("locale");
3748
4407
  const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
4408
+ const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
3749
4409
  return /* @__PURE__ */ React.createElement("div", {
3750
4410
  className: "lc-root",
3751
4411
  ref: rootRef
@@ -3759,7 +4419,7 @@ window.__ModuleLoader__.load({
3759
4419
  locale: activeLocale
3760
4420
  }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
3761
4421
  head,
3762
- subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
4422
+ subtitle,
3763
4423
  hoverKey: hoverCat,
3764
4424
  onHoverKey: setHoverCat
3765
4425
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-card" }, /* @__PURE__ */ React.createElement("div", { className: "lc-card-title" }, /* @__PURE__ */ React.createElement("span", { className: "lc-card-title-text" }, t("trend.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("trend.hint")), /* @__PURE__ */ React.createElement("div", { className: "lc-trend-ctl" }, /* @__PURE__ */ React.createElement("div", { className: "lc-gran" }, /* @__PURE__ */ React.createElement("button", {
@@ -3838,7 +4498,14 @@ window.__ModuleLoader__.load({
3838
4498
  activity: fileActivity,
3839
4499
  scope: fileScope,
3840
4500
  onLocate: locateFileOp
3841
- })), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
4501
+ })), /* @__PURE__ */ React.createElement(AgentGraph, {
4502
+ sessionId: typeof sessionId === "string" ? sessionId : void 0,
4503
+ self: {
4504
+ head,
4505
+ billed: usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null,
4506
+ requests: requests.length
4507
+ }
4508
+ }), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
3842
4509
  }
3843
4510
  return function ContextView(props) {
3844
4511
  return h(ErrorBoundary, null, h(ContextViewBody, props));
@@ -3859,7 +4526,7 @@ window.__ModuleLoader__.load({
3859
4526
  }
3860
4527
  //#endregion
3861
4528
  //#region \0dsh-global-css:/home/runner/work/dsh-context/dsh-context/src/client/styles.css.mjs
3862
- const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.lc-card-title{align-items:baseline;gap:8px;margin-bottom:10px;font-weight:600;display:flex}.lc-card-title-text{white-space:nowrap;flex:none}.lc-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.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-l1);cursor:help;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{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.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-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);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-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;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-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.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-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);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.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;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;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 .12s}.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)}.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-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-scroll{scrollbar-width:thin;scrollbar-gutter:stable;flex:1;min-width:0;padding-bottom:8px;position:relative;overflow:auto hidden}.lc-chart-fade{pointer-events:none;z-index:2;width:26px;position:absolute;top:0;bottom:0}.lc-chart-fade-l{background:linear-gradient(to right, var(--dsw-alias-bg-layer-1), transparent);left:0}.lc-chart-fade-r{background:linear-gradient(to left, var(--dsw-alias-bg-layer-1), transparent);right:0}.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-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;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;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.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-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-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}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:1fr 1fr;gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.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>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.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-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-events,.lc-fa-list{flex-direction:column;gap:2px;max-height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.lc-fa-more{color:var(--dsw-alias-label-secondary);padding:2px 6px;font-size:11px}.lc-fa-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.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{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-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;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}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;flex:none;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:140px 90px 56px 1fr;align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}";
4529
+ const css = ".lc-root{box-sizing:border-box;height:100%;color:var(--dsw-alias-label-primary);padding:16px 20px 32px;font-size:13px;overflow-y:auto}.lc-card{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;margin-bottom:14px;padding:14px 16px}.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-stats{grid-template-columns:repeat(auto-fit,minmax(62px,1fr));gap:6px;display:grid}.lc-stat{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;flex-direction:column;gap:2px;min-width:0;padding:6px;display:flex}.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-l1);cursor:help;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{z-index:6;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:280px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.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-gran,.lc-kinds{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);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-gran-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;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-settings-card{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;transition:border-color .16s,background .16s}.lc-settings-card:hover{border-color:var(--dsw-alias-label-dimmed)}.lc-settings-open,.lc-settings-open:hover{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.lc-settings-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.lc-settings-headtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.lc-settings-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.lc-settings-desc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.lc-settings-chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.lc-settings-open .lc-settings-chevron{transform:rotate(180deg)}.lc-settings-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}.lc-settings-row{align-items:center;gap:8px;padding:8px 0;display:flex}.lc-settings-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:14px}.lc-settings-select{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.lc-settings-select:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.lc-settings-select:disabled{opacity:.5;cursor:default}.lc-settings-note{color:var(--dsw-alias-label-tertiary);margin:12px 0 4px;font-size:12px;line-height:1.5}.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-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);border-radius:5px;transition:opacity .12s;position:absolute;top:0;bottom:0;left:0}.lc-occupied-box-on{opacity:1}.lc-bar-tip{z-index:5;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:3px 8px;font-size:12px;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);transform:translate(-50%);box-shadow:0 2px 8px #0000002e}.lc-bar-tip-on{opacity:1}.lc-stacked>div{height:100%}.lc-stacked-seg{transition:filter .12s,opacity .12s}.lc-stacked-free{box-sizing:border-box;transition:box-shadow .12s,opacity .12s}.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;border-radius:6px;align-items:center;gap:5px;padding:1px 6px;transition:background-color .12s;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 .12s}.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)}.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-mid{top:69px}.lc-axis-bot{top:125px}.lc-chart-wrap{flex:1;min-width:0;position:relative}.lc-chart-scroll{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-l1);pointer-events:none;position:absolute;left:0;right:0}.lc-grid-top{top:18px}.lc-grid-mid{top:74px}.lc-grid-zero{border-top-style:solid;border-top-color:#80808080}.lc-bar{cursor:pointer;border-radius:2px;flex:none;align-items:flex-end;width:14px;height:100%;transition:opacity .12s,background-color .12s;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;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-primary);pointer-events:none;border-radius:6px;padding:3px 8px;font-size:12px;position:absolute;top:0;left:0;box-shadow:0 2px 8px #0000002e}.lc-bar:hover{background:var(--dsw-alias-bg-layer-2)}.lc-bar-hovered{outline:1px dashed var(--dsw-alias-brand-primary);outline-offset:1px}.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-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-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}.lc-turn{box-sizing:border-box;text-align:center;color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;cursor:pointer;border-radius:3px;flex:none;height:14px;font-size:10px;font-weight:600;line-height:14px;transition:filter .12s,opacity .12s;overflow:hidden}.lc-turn-on{filter:brightness(1.35);color:var(--dsw-alias-label-primary)}.lc-turn-label{text-overflow:ellipsis;max-width:100%;display:inline-block;overflow:hidden}.lc-detail{border-top:1px solid var(--dsw-alias-border-l1);margin-top:12px;padding-top:12px}.lc-detail-head{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:6px 8px;margin-bottom:10px;display:flex}.lc-detail-head b{color:var(--dsw-alias-label-primary);font-size:13px}.lc-detail-time{font-variant-numeric:tabular-nums;white-space:nowrap;margin-left:auto;font-size:12px}.lc-detail-marker{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);border-radius:6px;padding:1px 7px;font-size:11px}.lc-detail-tag{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:4px;padding:0 6px;font-size:11px}.lc-detail-metric{font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--dsw-alias-label-primary);background:color-mix(in srgb, var(--dsw-alias-label-secondary) 14%, transparent);border-radius:6px;padding:2px 8px;font-size:12px;font-weight:600}.lc-detail-metric-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-detail-metric-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-detail-rows{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:4px 24px;margin-top:10px;display:grid}.lc-brief{flex-direction:column;gap:2px;min-height:76px;margin:-4px 0 10px;display:flex}.lc-brief-row{min-width:0;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;margin-left:-6px;margin-right:-6px;padding:3px 6px;font-family:inherit;font-size:12px;line-height:1.5;transition:background-color .12s;display:flex}.lc-brief-row-link:hover{background:var(--dsw-alias-interactive-bg-hover)}button.lc-brief-row-link{cursor:pointer}.lc-brief-tag{box-sizing:border-box;text-align:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:56px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;position:relative}.lc-brief-tip{z-index:6;white-space:normal;text-align:left;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:240px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;font-weight:400;line-height:1.5;transition:opacity .12s;position:absolute;bottom:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-brief-tag:hover .lc-brief-tip{opacity:1}.lc-brief-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-empty{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;overflow:hidden}.lc-brief-fact{box-sizing:border-box;text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);max-width:160px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);min-width:0;max-width:260px;color:var(--dsw-alias-label-primary);border-radius:5px;align-items:center;gap:5px;padding:0 7px;font-size:12px;transition:background-color .12s;display:inline-flex;overflow:hidden}.lc-brief-chip-grow{flex:1;max-width:none}.lc-brief-chip-tag{text-overflow:ellipsis;white-space:nowrap;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);max-width:120px;color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:0 6px;font-size:11px;overflow:hidden}.lc-brief-chip-text{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-brief-chip-link{cursor:pointer}.lc-brief-chip-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-brief-more{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-detail-row{align-items:center;gap:8px;display:flex}.lc-detail-label{white-space:nowrap;min-width:70px;color:var(--dsw-alias-label-secondary)}.lc-bar-track{background:#8080802e;border-radius:3px;flex:1;height:5px;display:block;position:relative;overflow:hidden}.lc-bar-fill{border-radius:3px;height:100%;display:block}.lc-bar-zero{border-left:1px solid #80808073;position:absolute;top:0;bottom:0;left:50%}.lc-bar-fill-up,.lc-bar-fill-down{height:100%;display:block;position:absolute;top:0}.lc-bar-fill-up{border-radius:0 3px 3px 0;left:50%}.lc-bar-fill-down{border-radius:3px 0 0 3px;right:50%}.lc-detail-num{text-align:right;font-variant-numeric:tabular-nums;width:52px}.lc-detail-num-up{color:var(--dsw-alias-state-success-primary)}.lc-detail-num-down{color:var(--dsw-alias-state-error-primary)}.lc-detail-pct{text-align:right;width:34px;color:var(--dsw-alias-label-secondary)}.lc-cols{flex-wrap:wrap;gap:14px;margin-bottom:14px;display:flex}.lc-col{flex:1;min-width:280px}.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>.lc-card:first-child{flex:7 1 0;min-width:360px}.lc-head>.lc-card:last-child{flex:3 1 0;min-width:220px}.lc-pi-grid{grid-template-columns:1fr;gap:8px;display:grid}.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-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-events,.lc-fa-list{flex-direction:column;gap:2px;height:320px;display:flex;overflow-y:auto}.lc-event{align-items:center;gap:8px;padding:3px 0;display:flex}.lc-event-icon{text-align:center;width:18px;color:var(--dsw-alias-state-warn-primary)}.lc-event-icon.lc-event-inject{color:#a855f7}.lc-event-icon.lc-event-model{color:var(--dsw-alias-brand-primary)}.lc-event-icon.lc-event-mode{color:var(--dsw-alias-label-secondary)}.lc-kind{white-space:nowrap;border-radius:4px;flex:none;padding:1px 6px;font-size:10px;font-weight:600}.lc-kind-inject{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-kind-compaction{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent);color:var(--dsw-alias-state-error-primary)}.lc-kind-prune{color:#8b5cf6;background:#8b5cf626}.lc-kind-model{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 15%, transparent);color:var(--dsw-alias-brand-primary)}.lc-kind-mode{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 15%, transparent);color:var(--dsw-alias-label-secondary)}.lc-event-label{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.lc-event-at{color:var(--dsw-alias-label-secondary);white-space:nowrap;flex:none;font-size:11px}.lc-event-tokens{color:var(--dsw-alias-state-success-primary);white-space:nowrap;font-weight:600}.lc-event-tokens.lc-up{color:var(--dsw-alias-state-warn-primary)}.lc-event-time{color:var(--dsw-alias-label-secondary);font-size:12px}.lc-fa-ctl{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:8px;display:flex}.lc-fa-ctl .lc-gran{margin-left:0}.lc-fa-n{opacity:.75;margin-left:4px;font-weight:700}.lc-fa-search{min-width:80px;font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;outline:none;flex:1;padding:3px 8px;font-size:12px}.lc-fa-search:focus{border-color:var(--dsw-alias-label-dimmed)}.lc-fa-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:6px;font-size:12px;display:flex}.lc-fa-meta .lc-fa-sort{flex:none}.lc-fa-meta-delta{position:relative}.lc-fa-meta-tip{z-index:6;white-space:normal;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);width:max-content;max-width:260px;color:var(--dsw-alias-label-primary);pointer-events:none;opacity:0;border-radius:6px;padding:6px 10px;font-size:12px;line-height:1.5;transition:opacity .12s;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 2px 8px #0000002e}.lc-fa-meta-delta:hover .lc-fa-meta-tip{opacity:1}.lc-fa-item{border:1px solid #0000;border-radius:8px;transition:background-color .12s,border-color .12s}.lc-fa-item-on{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-border-l1)}.lc-fa-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:7px;align-items:center;gap:7px;padding:6px 8px;font-size:12px;transition:background-color .12s;display:flex}.lc-fa-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-form{text-align:center;opacity:.85;flex:none;width:16px;font-size:11px}.lc-fa-path{word-break:break-all;flex:1;min-width:0}.lc-fa-path em{color:var(--dsw-alias-label-secondary);font-style:normal}.lc-fa-badge{font-variant-numeric:tabular-nums;white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:1px 7px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-badge i{background:currentColor;border-radius:50%;width:5px;height:5px;display:inline-block}.lc-fa-b-read{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent)}.lc-fa-b-write{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent)}.lc-fa-b-search{color:var(--dsw-alias-state-warn-primary);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent)}.lc-fa-chip-read,.lc-fa-chip-read:hover{color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write,.lc-fa-chip-write:hover{color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search,.lc-fa-chip-search:hover{color:var(--dsw-alias-state-warn-primary)}.lc-fa-chip-read.lc-gran-on,.lc-fa-chip-read.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent);color:var(--dsw-alias-brand-primary)}.lc-fa-chip-write.lc-gran-on,.lc-fa-chip-write.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);color:var(--dsw-alias-state-success-primary)}.lc-fa-chip-search.lc-gran-on,.lc-fa-chip-search.lc-gran-on:hover{background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-state-warn-primary)}.lc-fa-delta{font-variant-numeric:tabular-nums;white-space:nowrap;flex:none;gap:5px;font-size:11px;font-weight:600;display:inline-flex}.lc-fa-up{color:var(--dsw-alias-state-success-primary)}.lc-fa-down{color:var(--dsw-alias-state-error-primary)}.lc-fa-time{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none;font-size:11px}.lc-fa-ops{border-left:2px solid var(--dsw-alias-border-l1);flex-direction:column;gap:1px;margin:0 8px 8px 15px;padding:2px 0 2px 10px;display:flex}.lc-fa-op{min-width:0;color:var(--dsw-alias-label-primary);font:inherit;text-align:left;background:0 0;border:0;border-radius:5px;align-items:center;gap:8px;padding:3px 6px;font-size:11px;transition:background-color .12s;display:flex}.lc-fa-op-link{cursor:pointer}.lc-fa-op-link:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-fa-op-time{width:58px;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex:none}.lc-fa-op-tool{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:0 6px;font-size:11px}.lc-fa-op-detail{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--dsw-alias-label-secondary);flex:1;overflow:hidden}.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{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-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.lc-error-retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;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}.lc-modal-backdrop{z-index:200;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.lc-modal-card{box-sizing:border-box;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);width:min(720px,100vw - 48px);max-height:min(82vh,760px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);color:var(--dsw-alias-label-primary);border-radius:12px;padding:16px 18px 18px;font-size:13px;overflow-y:auto}.lc-modal-head{align-items:baseline;gap:8px;margin-bottom:12px;display:flex}.lc-modal-title{font-size:14px;font-weight:600}.lc-modal-close{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;margin-left:auto;padding:2px 6px;font-family:inherit;font-size:18px;line-height:1}.lc-modal-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}.lc-br-hint{color:var(--dsw-alias-label-secondary);white-space:nowrap;margin-left:auto;font-size:11px;font-weight:400}.lc-br-pick{font:inherit;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;max-width:240px;padding:3px 6px;font-size:12px}.lc-br-meta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:6px 16px;margin-bottom:8px;display:flex}.lc-br-meta b{color:var(--dsw-alias-label-primary)}.lc-br-meta .lc-actual{color:var(--dsw-alias-state-success-primary)}.lc-br-note{color:var(--dsw-alias-label-secondary);margin-top:8px;font-size:12px}.lc-br-retry{color:var(--dsw-alias-brand-primary);font:inherit;cursor:pointer;text-underline-offset:2px;background:0 0;border:0;padding:0;font-size:12px;text-decoration:underline}.lc-br-retry:hover{filter:brightness(1.15)}.lc-br-cats{flex-direction:column;gap:4px;margin-top:10px;display:flex}.lc-br-cat{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;overflow:hidden}.lc-br-cat-empty{opacity:.55}.lc-br-cat-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;align-items:center;gap:8px;padding:7px 10px;transition:background-color .12s;display:flex}.lc-br-cat-row:hover,.lc-br-cat-on{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-cat-on i{box-shadow:0 0 0 1px var(--dsw-alias-brand-primary)}.lc-br-cat-row i{border-radius:2px;flex:none;width:8px;height:8px;transition:box-shadow .12s;display:inline-block}.lc-br-cat-label{font-weight:600}.lc-br-cat-count{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-size:12px}.lc-br-count-grp{flex:1;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-chev{width:12px;color:var(--dsw-alias-label-secondary);flex:none;transition:transform .12s}.lc-br-chev-on{transform:rotate(90deg)}.lc-br-tokens{color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-delta,.lc-br-tdelta{white-space:nowrap;border-radius:4px;flex:none;padding:0 5px;font-size:11px;font-weight:600}.lc-br-delta-up,.lc-br-tdelta-up{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 15%, transparent)}.lc-br-delta-down,.lc-br-tdelta-down{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 15%, transparent)}.lc-br-tokens-grp{flex:none;align-items:center;gap:4px;min-width:0;display:inline-flex}.lc-br-pct{text-align:right;width:36px;color:var(--dsw-alias-label-secondary);flex:none;font-size:12px}.lc-br-body{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:2px;padding:4px 6px;display:flex}.lc-br-elem{border-radius:6px}.lc-br-elem-on{background:var(--dsw-alias-interactive-bg-active)}.lc-br-elem-row{width:100%;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;text-align:left;background:0 0;border:0;border-radius:6px;align-items:center;gap:8px;padding:5px 6px;font-size:12px;transition:background-color .12s;display:flex}.lc-br-elem-row:hover{background:var(--dsw-alias-interactive-bg-hover)}.lc-br-err-dot{background:var(--dsw-alias-state-error-primary);width:6px;height:6px;box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);border-radius:50%;flex:none}.lc-br-tag{min-width:0;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);text-overflow:ellipsis;white-space:nowrap;border-radius:4px;max-width:220px;padding:0 6px;font-size:11px;overflow:hidden}.lc-br-preview{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.lc-br-time{color:var(--dsw-alias-label-secondary);flex:none;font-size:11px}.lc-br-content{flex-direction:column;gap:6px;padding:2px 6px 8px 26px;display:flex}.lc-br-dim{color:var(--dsw-alias-label-secondary)}.lc-ts-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;min-width:0;overflow:hidden}.lc-ts-card-head{color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1);align-items:center;gap:8px;padding:6px 10px;font-size:11px;font-weight:600;display:flex}.lc-ts-card-head b{color:var(--dsw-alias-label-primary)}.lc-ts-call-name{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}.lc-ts-card-count{margin-left:auto}.lc-ts-card-right{align-items:center;gap:8px;margin-left:auto;display:inline-flex}.lc-ts-card-meta{color:var(--dsw-alias-label-secondary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.lc-ts-call-state{white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:4px;padding:2px 7px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;font-weight:600;line-height:1;display:inline-flex}.lc-ts-call-state i{background:currentColor;border-radius:50%;flex:none;width:6px;height:6px}.lc-ts-call-ok{color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent)}.lc-ts-call-err{color:var(--dsw-alias-state-error-primary);background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent)}.lc-ts-desc-body{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:320px;margin:0;padding:8px 10px;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-param-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,140px) minmax(0,90px) 56px minmax(0,1fr);align-items:baseline;gap:2px 10px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-param-row:first-of-type{border-top:0}.lc-ts-param-name{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.lc-ts-param-type{color:var(--dsw-alias-label-secondary);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow:hidden}.lc-ts-param-req{color:var(--dsw-alias-state-warn-primary);font-size:11px;font-weight:600}.lc-ts-param-req-off{color:var(--dsw-alias-label-secondary);font-size:11px}.lc-ts-param-desc{color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere;grid-column:1/-1;line-height:1.5}.lc-ts-params-empty{color:var(--dsw-alias-label-secondary);padding:8px 10px;font-size:12px}.lc-ts-arg-row{border-top:1px solid var(--dsw-alias-border-l1);grid-template-columns:minmax(0,96px) 1fr;align-items:start;column-gap:12px;padding:6px 10px;font-size:12px;display:grid}.lc-ts-arg-row:first-of-type{border-top:0}.lc-ts-arg-row .lc-ts-param-name{text-overflow:unset;white-space:normal;overflow-wrap:anywhere;overflow:visible}.lc-ts-arg-val{color:var(--dsw-alias-label-primary);white-space:pre-wrap;word-break:break-word;scrollbar-width:thin;max-height:200px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;overflow-y:auto}.lc-ts-json{flex-direction:column;gap:4px;display:flex}.lc-ts-json-toggle{color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;background:0 0;border:0;align-self:flex-start;padding:0;font-size:11px}.lc-ts-json-toggle:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.lc-rich-seg{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:6px;gap:2px;margin-left:auto;padding:1px;display:flex}.lc-rich-seg-btn{color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:5px;padding:3px 8px;font-family:inherit;font-size:11px;line-height:1}.lc-rich-seg-btn:hover{color:var(--dsw-alias-label-primary)}.lc-rich-seg-on,.lc-rich-seg-on:hover{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}.lc-ts-desc-md{color:var(--dsw-alias-label-primary);word-break:break-word;overflow-wrap:anywhere;white-space:pre-line;scrollbar-width:thin;min-width:0;max-height:320px;padding:4px 10px;font-size:12px;line-height:1.55;overflow:hidden auto}.lc-ts-desc-md>div{font-size:13px;line-height:1.55}.lc-ts-desc-md div :is(h1,h2,h3,h4,h5,h6){margin:10px 0 4px;font-size:13px;font-weight:600}.lc-ts-desc-md div p{margin:6px 0}.lc-ts-desc-md div pre{white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;margin:6px 0}.lc-ts-desc-md div :not(pre)>code{box-decoration-break:clone;padding:1px 5px;line-height:1.3;font-size:1em!important}.lc-ts-desc-md div table code{font-size:inherit}.lc-att-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;padding:8px 10px;display:grid}.lc-att-item{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l1);width:100%;min-width:0;font:inherit;text-align:left;cursor:pointer;background:0 0;border-radius:8px;align-items:center;gap:8px;padding:6px;display:flex}.lc-att-item:hover{border-color:var(--dsw-alias-border-l2,var(--dsw-alias-border-l1));background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-att-thumb{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:6px;flex:none;justify-content:center;align-items:center;width:64px;height:64px;padding:0;display:flex;overflow:hidden}.lc-att-thumb img{object-fit:cover;width:100%;height:100%;display:block}.lc-att-ph,.lc-att-err{color:var(--dsw-alias-label-secondary);text-align:center;padding:2px;font-size:10px}.lc-att-meta{flex-direction:column;gap:2px;min-width:0;display:flex}.lc-att-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere;word-break:break-word;font-size:12px;line-height:1.3}.lc-att-row{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:11px}.lc-att-row-label{min-width:3.2em;font-weight:400;display:inline-block}.lc-att-lightbox{z-index:1000;place-items:center;padding:40px;display:grid;position:fixed;inset:0}.lc-att-lightbox-mask{background:var(--dsw-alias-bg-mask-1,#00000073);backdrop-filter:var(--dsw-mask-blur,blur(2px));position:absolute;inset:0}.lc-att-lightbox-img{object-fit:contain;background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));max-width:min(100%,1600px);max-height:calc(100vh - 80px);box-shadow:var(--dsw-shadow-lv3,0 12px 32px #0006);border-radius:12px;position:relative}.lc-att-lightbox-close{z-index:1;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l1));background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));width:36px;height:36px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:999px;place-items:center;padding:0;display:grid;position:fixed;top:20px;right:20px}[data-conversation-scroll]:has(.lc-root)>[data-composer-seat]:not(:has([data-approval-key],[data-question-key],[data-plan-review-key])){display:none}.lc-agents-chips{flex-wrap:wrap;gap:6px;margin-bottom:10px;display:flex}.lc-agents-chip{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;border-radius:999px;padding:2px 8px;font-size:11px}.lc-agents-chip-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-stage{margin:0 -4px;padding:0 4px;overflow-x:auto}.lc-agents-svg{max-width:none;margin:0 auto;display:block}.lc-agents-link{stroke-width:1.5px;stroke-opacity:.45}.lc-agents-link-live{stroke-opacity:.85}.lc-agents-flow{stroke-width:2px;stroke-linecap:round;stroke-dasharray:2 7;animation:.9s linear infinite lc-agent-flow}@keyframes lc-agent-flow{to{stroke-dashoffset:-9px}}.lc-agent-node{outline:none}.lc-agent-clickable{cursor:pointer}.lc-agent-halo{fill:#0000;transition:fill .12s}.lc-agent-hover .lc-agent-halo,.lc-agent-node:focus-visible .lc-agent-halo{fill:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}.lc-agent-self .lc-agent-halo{fill:var(--dsw-alias-bg-layer-2)}.lc-agent-running .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 12%, transparent);animation:1.8s ease-in-out infinite lc-agent-glow}.lc-agent-done .lc-agent-halo{fill:color-mix(in srgb, var(--dsw-alias-state-success-primary) 7%, transparent)}@keyframes lc-agent-glow{0%,to{opacity:1}50%{opacity:.4}}.lc-agent-track{fill:var(--dsw-alias-bg-layer-1);stroke:var(--dsw-alias-border-l1);stroke-width:1.5px}.lc-agent-seg{fill:none;stroke-width:9px;transition:stroke-dasharray .3s,stroke-dashoffset .3s}.lc-agent-free{stroke:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agent-pct{fill:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;pointer-events:none;font-size:11px;font-weight:600}.lc-agent-caption{text-align:center;pointer-events:none;width:100%;line-height:1.25}.lc-agent-label{color:var(--dsw-alias-label-primary);overflow-wrap:break-word;-webkit-line-clamp:3;-webkit-box-orient:vertical;font-size:11px;display:-webkit-box;overflow:hidden}.lc-agent-tokens{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;font-size:10px}.lc-agents-solo{padding:4px 0 10px}.lc-agents-inspector{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);border-radius:8px;flex-wrap:wrap;align-items:baseline;gap:4px 8px;min-width:0;margin-top:10px;padding:7px 10px;font-size:12px;display:flex}.lc-agents-inspector-name{color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}.lc-agents-badge{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 6px;font-size:10px}.lc-agent-self-badge{vertical-align:1px;margin-left:5px}.lc-agents-badge-on{color:var(--dsw-alias-state-success-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent)}.lc-agents-inspector-stats{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.lc-agents-inspector-open{color:var(--dsw-alias-brand-primary,#3b82f6);flex:none;margin-left:auto}.lc-agents-legend{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:4px 14px;margin-top:8px;font-size:11px;display:flex}.lc-agents-legend-item{align-items:center;gap:5px;display:inline-flex}.lc-agents-legend-item i{border-radius:2px;width:8px;height:8px}.lc-agents-legend-free{background:color-mix(in srgb, var(--dsw-alias-label-secondary) 18%, transparent)}.lc-agents-legend-edge{border-top:2px dashed var(--dsw-alias-label-secondary);border-radius:0!important;width:14px!important;height:0!important}";
3863
4530
  const tagId = "dsh-context/styles.css";
3864
4531
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
3865
4532
  const tag = document.createElement("style");