dsh-context 0.33.1 → 0.34.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +17 -2
  2. package/lib/client.js +1407 -173
  3. package/lib/index.js +171 -143
  4. 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,57 @@ 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} 次更早操作",
102
+ "files.hits": "{n} 处命中",
103
103
  "files.errs": "{n} 次失败",
104
104
  "files.form.image": "图片",
105
105
  "files.form.dir": "目录",
106
106
  "files.form.text": "文件",
107
+ "files.glyph.root": "工作区根",
108
+ "files.glyph.tests": "测试",
109
+ "files.glyph.docs": "文档目录",
110
+ "files.glyph.deps": "依赖",
111
+ "files.glyph.build": "构建产物",
112
+ "files.glyph.scripts": "脚本",
113
+ "files.glyph.config": "配置",
114
+ "files.glyph.assets": "静态资源",
115
+ "files.glyph.hidden": "元数据目录",
116
+ "files.glyph.lock": "锁文件",
117
+ "files.glyph.docker": "Docker",
118
+ "files.glyph.ignore": "忽略规则",
119
+ "files.glyph.license": "许可证",
120
+ "files.glyph.python": "Python",
121
+ "files.glyph.notebook": "Notebook",
122
+ "files.glyph.shell": "Shell",
123
+ "files.glyph.web": "网页",
124
+ "files.glyph.style": "样式",
125
+ "files.glyph.database": "数据库",
126
+ "files.glyph.code": "代码",
127
+ "files.glyph.data": "数据清单",
128
+ "files.glyph.markdown": "标记文档",
129
+ "files.glyph.log": "日志",
130
+ "files.glyph.sheet": "表格",
131
+ "files.glyph.document": "文档",
132
+ "files.glyph.archive": "压缩包",
133
+ "files.glyph.font": "字体",
134
+ "files.glyph.media": "媒体",
135
+ "agents.title": "Agent 网络",
136
+ "agents.sub": "当前 Agent 与子 Agent 的实时上下文 · 点击节点跳转会话",
137
+ "agents.chip.count": "{n} 个 Agent",
138
+ "agents.chip.running": "{n} 个运行中",
139
+ "agents.chip.tokens": "上下文合计 {n}",
140
+ "agents.more": "另有 {n} 个未显示",
141
+ "agents.solo": "还没有子 Agent — 当 Agent 通过 subagent 工具委派任务时,协作网络会在这里展开",
142
+ "agents.self": "当前",
143
+ "agents.running": "运行中",
144
+ "agents.legend.free": "空闲窗口",
145
+ "agents.oneshot": "一次性",
146
+ "agents.continuable": "多轮",
147
+ "agents.requests": "{n} 次请求",
148
+ "agents.billed": "累计计费 {n}",
149
+ "agents.open": "点击打开会话 →",
107
150
  "loading": "正在读取会话日志…",
108
151
  "error": "上下文数据读取失败:",
109
152
  "error.retry": "重试",
@@ -242,10 +285,11 @@ window.__ModuleLoader__.load({
242
285
  "brief.input": "In",
243
286
  "brief.reply": "Response",
244
287
  "brief.more": "+{n}",
288
+ "brief.noInputs": "(nothing new)",
245
289
  "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)",
290
+ "brief.turnTip": "The user message that opened this turn — every step of the turn recalls it",
291
+ "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",
292
+ "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
293
  "events.title": "Context Events",
250
294
  "events.empty": "No context events yet (compaction, injections, model switches appear here)",
251
295
  "events.at": "Turn {t} · Step {s}",
@@ -271,15 +315,57 @@ window.__ModuleLoader__.load({
271
315
  "files.sortTip": "Sort files by operation counts (of the selected kind when a kind chip is active), most recent activity, or path",
272
316
  "files.files": "{n} files",
273
317
  "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
318
  "files.empty": "No file reads, writes, or searches in this range",
276
319
  "files.noMatch": "No files match the current filters",
277
320
  "files.locate": "Reveal this operation in the Context Browser",
278
- "files.moreOps": "+{n} earlier ops",
321
+ "files.hits": "{n} hits",
279
322
  "files.errs": "{n} failed",
280
323
  "files.form.image": "Image",
281
324
  "files.form.dir": "Directory",
282
325
  "files.form.text": "File",
326
+ "files.glyph.root": "Workspace root",
327
+ "files.glyph.tests": "Tests",
328
+ "files.glyph.docs": "Docs",
329
+ "files.glyph.deps": "Dependencies",
330
+ "files.glyph.build": "Build output",
331
+ "files.glyph.scripts": "Scripts",
332
+ "files.glyph.config": "Config",
333
+ "files.glyph.assets": "Assets",
334
+ "files.glyph.hidden": "Metadata directory",
335
+ "files.glyph.lock": "Lockfile",
336
+ "files.glyph.docker": "Docker",
337
+ "files.glyph.ignore": "Ignore rules",
338
+ "files.glyph.license": "License",
339
+ "files.glyph.python": "Python",
340
+ "files.glyph.notebook": "Notebook",
341
+ "files.glyph.shell": "Shell",
342
+ "files.glyph.web": "Web page",
343
+ "files.glyph.style": "Styles",
344
+ "files.glyph.database": "Database",
345
+ "files.glyph.code": "Code",
346
+ "files.glyph.data": "Data / manifest",
347
+ "files.glyph.markdown": "Markup document",
348
+ "files.glyph.log": "Log",
349
+ "files.glyph.sheet": "Spreadsheet",
350
+ "files.glyph.document": "Document",
351
+ "files.glyph.archive": "Archive",
352
+ "files.glyph.font": "Font",
353
+ "files.glyph.media": "Media",
354
+ "agents.title": "Agent Network",
355
+ "agents.sub": "Live context of this agent and its subagents · click a node to open its session",
356
+ "agents.chip.count": "{n} agents",
357
+ "agents.chip.running": "{n} running",
358
+ "agents.chip.tokens": "{n} tokens in context",
359
+ "agents.more": "{n} more not shown",
360
+ "agents.solo": "No subagents yet — when the agent delegates work through the subagent tool, the network unfolds here",
361
+ "agents.self": "current",
362
+ "agents.running": "running",
363
+ "agents.legend.free": "free window",
364
+ "agents.oneshot": "one-shot",
365
+ "agents.continuable": "continuable",
366
+ "agents.requests": "{n} requests",
367
+ "agents.billed": "{n} billed",
368
+ "agents.open": "click to open →",
283
369
  "loading": "Reading the session log…",
284
370
  "error": "Failed to read context data: ",
285
371
  "error.retry": "Retry",
@@ -578,7 +664,7 @@ window.__ModuleLoader__.load({
578
664
  * every field read below must re-prove itself (the no-white-screen
579
665
  * guarantee), so no field may borrow the wire type before its check.
580
666
  */
581
- function asRecord(value) {
667
+ function asRecord$1(value) {
582
668
  if (value === null || value === void 0 || typeof value !== "object") return null;
583
669
  return value;
584
670
  }
@@ -607,7 +693,7 @@ window.__ModuleLoader__.load({
607
693
  * during render and unmounting the conversation view.
608
694
  */
609
695
  function timelineOf(value) {
610
- const data = asRecord(value);
696
+ const data = asRecord$1(value);
611
697
  if (data === null) return null;
612
698
  const current = data.current;
613
699
  if (current !== null && typeof current === "object" && [
@@ -655,7 +741,7 @@ window.__ModuleLoader__.load({
655
741
  * back to their derived anchor, so the UI degrades gracefully.
656
742
  */
657
743
  function contextPressureOf(value) {
658
- return asRecord(value);
744
+ return asRecord$1(value);
659
745
  }
660
746
  /**
661
747
  * Narrow a delivered projection value to the official token-meter
@@ -665,7 +751,7 @@ window.__ModuleLoader__.load({
665
751
  * own sums instead of mixing sources.
666
752
  */
667
753
  function contextBreakdownOf(value) {
668
- const data = asRecord(value);
754
+ const data = asRecord$1(value);
669
755
  if (data === null) return null;
670
756
  const { systemTokens, toolsTokens, messageTokens } = data;
671
757
  if (typeof systemTokens !== "number" || !Number.isFinite(systemTokens)) return null;
@@ -684,7 +770,7 @@ window.__ModuleLoader__.load({
684
770
  * usage yet) — callers drop the cache-hit cell to a dash.
685
771
  */
686
772
  function tokenUsageOf(value) {
687
- return asRecord(value);
773
+ return asRecord$1(value);
688
774
  }
689
775
  /**
690
776
  * Narrow a delivered projection value to the plugin's `contextHeaders`
@@ -698,7 +784,7 @@ window.__ModuleLoader__.load({
698
784
  * and the card falls back to its tokens-only note.
699
785
  */
700
786
  function headersOf(value) {
701
- const headers = asRecord(value);
787
+ const headers = asRecord$1(value);
702
788
  if (headers === null || !Array.isArray(headers.headers)) return null;
703
789
  for (const h of headers.headers) {
704
790
  if (h === null || typeof h !== "object") return null;
@@ -2426,6 +2512,17 @@ window.__ModuleLoader__.load({
2426
2512
  return KIND_BY_TOOL[tool] ?? null;
2427
2513
  }
2428
2514
  /**
2515
+ * The file purpose of one executed call. Like {@link kindOfTool} except for
2516
+ * the one file tool whose purpose follows its arguments: `str_replace_editor`
2517
+ * reads on `view` and writes on every other command (create / str_replace /
2518
+ * insert — an unknown command writes too; the call failed and the row keeps
2519
+ * its error flag).
2520
+ */
2521
+ function kindOfCall(tool, args) {
2522
+ if (tool === "str_replace_editor") return args !== null && args.command === "view" ? "read" : "write";
2523
+ return kindOfTool(tool);
2524
+ }
2525
+ /**
2429
2526
  * The operation's target path: the path-ish argument of read/write tools;
2430
2527
  * for searches the narrowing `path`, else the pattern itself (a pathless
2431
2528
  * grep/glob's target IS the pattern — the workspace-wide search text).
@@ -2455,27 +2552,27 @@ window.__ModuleLoader__.load({
2455
2552
  for (let i = 0; i < s.length; i++) if (s[i] === "\n") n++;
2456
2553
  return s.endsWith("\n") ? n : n + 1;
2457
2554
  }
2555
+ /** The added/removed pair of one content-bearing argument set, or zeros. */
2556
+ function pairOf(added, removed) {
2557
+ return {
2558
+ added: typeof added === "string" ? linesOf(added) : 0,
2559
+ removed: typeof removed === "string" ? linesOf(removed) : 0
2560
+ };
2561
+ }
2458
2562
  /**
2459
2563
  * The signed line footprint of one call: an edit removes its old string and
2460
2564
  * adds its new one; a write adds its content (the pre-existing body, if any,
2461
- * is unknowable from the arguments — the estimate stays honest about that).
2462
- * Callers reach here only with parsed args (a null parse yields no path).
2565
+ * is unknowable from the arguments — the estimate stays honest about that);
2566
+ * `str_replace_editor` splits the same shapes across its commands. Callers
2567
+ * reach here only with parsed args (a null parse yields no path).
2463
2568
  */
2464
2569
  function deltaOf(tool, args) {
2465
- if (tool === "edit") {
2466
- const oldS = args.old_string;
2467
- const newS = args.new_string;
2468
- return {
2469
- added: typeof newS === "string" ? linesOf(newS) : 0,
2470
- removed: typeof oldS === "string" ? linesOf(oldS) : 0
2471
- };
2472
- }
2473
- if (tool === "write") {
2474
- const content = args.content;
2475
- return {
2476
- added: typeof content === "string" ? linesOf(content) : 0,
2477
- removed: 0
2478
- };
2570
+ if (tool === "edit") return pairOf(args.new_string, args.old_string);
2571
+ if (tool === "write") return pairOf(args.content, void 0);
2572
+ if (tool === "str_replace_editor") {
2573
+ if (args.command === "str_replace") return pairOf(args.new_str, args.old_str);
2574
+ if (args.command === "insert") return pairOf(args.new_str, void 0);
2575
+ if (args.command === "create") return pairOf(args.file_text, void 0);
2479
2576
  }
2480
2577
  return {
2481
2578
  added: 0,
@@ -2489,6 +2586,470 @@ window.__ModuleLoader__.load({
2489
2586
  if (path.endsWith("/")) return "dir";
2490
2587
  return "text";
2491
2588
  }
2589
+ /** Directory buckets, by last path segment (lowercased, explicit plurals). Checked in order, then hidden dirs, then the plain folder. */
2590
+ const DIR_BUCKETS = [
2591
+ [
2592
+ [
2593
+ "test",
2594
+ "tests",
2595
+ "__tests__",
2596
+ "spec",
2597
+ "specs",
2598
+ "e2e"
2599
+ ],
2600
+ "🧪",
2601
+ "files.glyph.tests"
2602
+ ],
2603
+ [
2604
+ [
2605
+ "doc",
2606
+ "docs",
2607
+ "documentation"
2608
+ ],
2609
+ "📚",
2610
+ "files.glyph.docs"
2611
+ ],
2612
+ [
2613
+ [
2614
+ "node_modules",
2615
+ "vendor",
2616
+ "third_party",
2617
+ "third-party",
2618
+ "packages"
2619
+ ],
2620
+ "📦",
2621
+ "files.glyph.deps"
2622
+ ],
2623
+ [
2624
+ [
2625
+ "dist",
2626
+ "build",
2627
+ "out",
2628
+ "target",
2629
+ "release",
2630
+ "debug",
2631
+ "coverage",
2632
+ "artifacts"
2633
+ ],
2634
+ "🏗️",
2635
+ "files.glyph.build"
2636
+ ],
2637
+ [
2638
+ [
2639
+ "scripts",
2640
+ "tools",
2641
+ "bin"
2642
+ ],
2643
+ "🛠️",
2644
+ "files.glyph.scripts"
2645
+ ],
2646
+ [
2647
+ [
2648
+ "config",
2649
+ "configs",
2650
+ "settings",
2651
+ ".config"
2652
+ ],
2653
+ "⚙️",
2654
+ "files.glyph.config"
2655
+ ],
2656
+ [
2657
+ [
2658
+ "assets",
2659
+ "static",
2660
+ "public",
2661
+ "images",
2662
+ "fonts",
2663
+ "icons",
2664
+ "media"
2665
+ ],
2666
+ "🎨",
2667
+ "files.glyph.assets"
2668
+ ]
2669
+ ];
2670
+ /** Lockfile base names that do not end in `.lock`. */
2671
+ const LOCK_NAMES = [
2672
+ "package-lock.json",
2673
+ "pnpm-lock.yaml",
2674
+ "npm-shrinkwrap.json"
2675
+ ];
2676
+ const MAKE_NAMES = [
2677
+ "makefile",
2678
+ "justfile",
2679
+ "cmakelists.txt"
2680
+ ];
2681
+ /** A test file by name: a standalone or delimited `test`, an inline `.test.`/`.spec.`, or their underscore forms. */
2682
+ const TEST_NAME = /(^|[^a-z0-9])test([^a-z0-9]|$)|\.(test|spec)\./;
2683
+ /** Extension buckets, most specific first; the last row catches every programming language. */
2684
+ const EXT_BUCKETS = [
2685
+ [
2686
+ [
2687
+ "py",
2688
+ "pyi",
2689
+ "pyw"
2690
+ ],
2691
+ "🐍",
2692
+ "files.glyph.python"
2693
+ ],
2694
+ [
2695
+ ["ipynb"],
2696
+ "📓",
2697
+ "files.glyph.notebook"
2698
+ ],
2699
+ [
2700
+ [
2701
+ "sh",
2702
+ "bash",
2703
+ "zsh",
2704
+ "fish",
2705
+ "ps1"
2706
+ ],
2707
+ "🐚",
2708
+ "files.glyph.shell"
2709
+ ],
2710
+ [
2711
+ [
2712
+ "html",
2713
+ "htm",
2714
+ "xhtml"
2715
+ ],
2716
+ "🌐",
2717
+ "files.glyph.web"
2718
+ ],
2719
+ [
2720
+ [
2721
+ "css",
2722
+ "scss",
2723
+ "sass",
2724
+ "less",
2725
+ "styl"
2726
+ ],
2727
+ "🎨",
2728
+ "files.glyph.style"
2729
+ ],
2730
+ [
2731
+ ["sql"],
2732
+ "🗃️",
2733
+ "files.glyph.database"
2734
+ ],
2735
+ [
2736
+ [
2737
+ "yaml",
2738
+ "yml",
2739
+ "toml",
2740
+ "ini",
2741
+ "conf",
2742
+ "cfg",
2743
+ "properties",
2744
+ "env"
2745
+ ],
2746
+ "⚙️",
2747
+ "files.glyph.config"
2748
+ ],
2749
+ [
2750
+ [
2751
+ "json",
2752
+ "jsonc",
2753
+ "json5",
2754
+ "jsonl",
2755
+ "ndjson",
2756
+ "xml"
2757
+ ],
2758
+ "🧾",
2759
+ "files.glyph.data"
2760
+ ],
2761
+ [
2762
+ [
2763
+ "md",
2764
+ "mdx",
2765
+ "markdown",
2766
+ "rst",
2767
+ "adoc",
2768
+ "org"
2769
+ ],
2770
+ "📝",
2771
+ "files.glyph.markdown"
2772
+ ],
2773
+ [
2774
+ ["log"],
2775
+ "📜",
2776
+ "files.glyph.log"
2777
+ ],
2778
+ [
2779
+ [
2780
+ "csv",
2781
+ "tsv",
2782
+ "xls",
2783
+ "xlsx",
2784
+ "ods"
2785
+ ],
2786
+ "📊",
2787
+ "files.glyph.sheet"
2788
+ ],
2789
+ [
2790
+ [
2791
+ "pdf",
2792
+ "doc",
2793
+ "docx",
2794
+ "odt",
2795
+ "rtf"
2796
+ ],
2797
+ "📕",
2798
+ "files.glyph.document"
2799
+ ],
2800
+ [
2801
+ [
2802
+ "zip",
2803
+ "gz",
2804
+ "tgz",
2805
+ "tar",
2806
+ "bz2",
2807
+ "xz",
2808
+ "7z",
2809
+ "rar",
2810
+ "jar"
2811
+ ],
2812
+ "🗜️",
2813
+ "files.glyph.archive"
2814
+ ],
2815
+ [
2816
+ [
2817
+ "ttf",
2818
+ "otf",
2819
+ "woff",
2820
+ "woff2",
2821
+ "eot"
2822
+ ],
2823
+ "🔤",
2824
+ "files.glyph.font"
2825
+ ],
2826
+ [
2827
+ [
2828
+ "mp4",
2829
+ "mov",
2830
+ "mkv",
2831
+ "webm",
2832
+ "mp3",
2833
+ "wav",
2834
+ "flac",
2835
+ "ogg"
2836
+ ],
2837
+ "🎬",
2838
+ "files.glyph.media"
2839
+ ],
2840
+ [
2841
+ [
2842
+ "ts",
2843
+ "tsx",
2844
+ "js",
2845
+ "jsx",
2846
+ "mjs",
2847
+ "cjs",
2848
+ "go",
2849
+ "rs",
2850
+ "java",
2851
+ "kt",
2852
+ "kts",
2853
+ "swift",
2854
+ "rb",
2855
+ "php",
2856
+ "c",
2857
+ "h",
2858
+ "cpp",
2859
+ "cc",
2860
+ "cxx",
2861
+ "hpp",
2862
+ "cs",
2863
+ "scala",
2864
+ "lua",
2865
+ "dart",
2866
+ "vue",
2867
+ "svelte"
2868
+ ],
2869
+ "🧩",
2870
+ "files.glyph.code"
2871
+ ]
2872
+ ];
2873
+ function dirGlyph(base) {
2874
+ if (base === "" || base === ".") return {
2875
+ glyph: "🏠",
2876
+ tip: "files.glyph.root"
2877
+ };
2878
+ const name = base.toLowerCase();
2879
+ for (const [names, glyph, tip] of DIR_BUCKETS) if (names.includes(name)) return {
2880
+ glyph,
2881
+ tip
2882
+ };
2883
+ if (name.startsWith(".")) return {
2884
+ glyph: "🗄️",
2885
+ tip: "files.glyph.hidden"
2886
+ };
2887
+ return {
2888
+ glyph: "📁",
2889
+ tip: "files.form.dir"
2890
+ };
2891
+ }
2892
+ function fileGlyph(base) {
2893
+ const name = base.toLowerCase();
2894
+ const dot = name.lastIndexOf(".");
2895
+ const ext = dot > 0 ? name.slice(dot + 1) : "";
2896
+ if (name.endsWith(".lock") || LOCK_NAMES.includes(name)) return {
2897
+ glyph: "🔒",
2898
+ tip: "files.glyph.lock"
2899
+ };
2900
+ if (TEST_NAME.test(name)) return {
2901
+ glyph: "🧪",
2902
+ tip: "files.glyph.tests"
2903
+ };
2904
+ if (name === "dockerfile" || name.startsWith("dockerfile.")) return {
2905
+ glyph: "🐳",
2906
+ tip: "files.glyph.docker"
2907
+ };
2908
+ if (MAKE_NAMES.includes(name)) return {
2909
+ glyph: "🛠️",
2910
+ tip: "files.glyph.scripts"
2911
+ };
2912
+ if (name === ".gitignore" || name === ".gitattributes") return {
2913
+ glyph: "🚫",
2914
+ tip: "files.glyph.ignore"
2915
+ };
2916
+ if (name === "license" || name.startsWith("license.") || name === "copying") return {
2917
+ glyph: "⚖️",
2918
+ tip: "files.glyph.license"
2919
+ };
2920
+ if (name === ".env" || name.startsWith(".env.")) return {
2921
+ glyph: "⚙️",
2922
+ tip: "files.glyph.config"
2923
+ };
2924
+ if (ext !== "") {
2925
+ for (const [exts, glyph, tip] of EXT_BUCKETS) if (exts.includes(ext)) return {
2926
+ glyph,
2927
+ tip
2928
+ };
2929
+ }
2930
+ return {
2931
+ glyph: "📄",
2932
+ tip: "files.form.text"
2933
+ };
2934
+ }
2935
+ /** The row icon for one file entry: form first (image/dir), then the file-name tables. */
2936
+ function glyphOf(path, form) {
2937
+ if (form === "image") return {
2938
+ glyph: "🖼",
2939
+ tip: "files.form.image"
2940
+ };
2941
+ const trimmed = path.endsWith("/") ? path.slice(0, -1) : path;
2942
+ const base = trimmed.slice(trimmed.lastIndexOf("/") + 1);
2943
+ return form === "dir" ? dirGlyph(base) : fileGlyph(base);
2944
+ }
2945
+ /**
2946
+ * A search op's detail: the pattern, with the include filter appended when
2947
+ * one narrowed the call. A patternless (malformed) search has no detail.
2948
+ */
2949
+ function searchDetailOf(args) {
2950
+ const pattern = args?.pattern;
2951
+ if (typeof pattern !== "string" || pattern === "") return void 0;
2952
+ const include = args?.include;
2953
+ return typeof include === "string" && include !== "" ? `${pattern} (${include})` : pattern;
2954
+ }
2955
+ /**
2956
+ * The files a search demonstrably reached, read off the result's bounded
2957
+ * presentation meta (grep groups matched lines by file; glob lists paths).
2958
+ * Only the COMPLETE list attributes: a capped search (`truncated`) names a
2959
+ * partial file set, and a malformed meta names none — both fall back to the
2960
+ * call's own target. Each entry carries the reported match count.
2961
+ */
2962
+ function searchFilesOf(meta) {
2963
+ if (meta === null || typeof meta !== "object") return null;
2964
+ const m = meta;
2965
+ if (m.truncated !== false) return null;
2966
+ const files = [];
2967
+ if (m.shape === "matches" && Array.isArray(m.files)) for (const f of m.files) {
2968
+ if (f === null || typeof f !== "object") continue;
2969
+ const group = f;
2970
+ if (typeof group.path === "string" && group.path !== "" && Array.isArray(group.matches)) files.push({
2971
+ path: group.path,
2972
+ hits: group.matches.length
2973
+ });
2974
+ }
2975
+ else if (m.shape === "paths" && Array.isArray(m.paths)) {
2976
+ for (const p of m.paths) if (typeof p === "string" && p !== "") files.push({
2977
+ path: p,
2978
+ hits: 0
2979
+ });
2980
+ }
2981
+ return files.length > 0 ? files : null;
2982
+ }
2983
+ /**
2984
+ * Narrow one block of a conversation node's `subCalls` tree to a settled
2985
+ * nested call, or null. The join is defensive — a running call has no result
2986
+ * kind yet, and any malformed block is dropped, never thrown. (Null and
2987
+ * non-object blocks never reach here: the folding loop pre-filters them.)
2988
+ */
2989
+ function subCallOf(block) {
2990
+ const b = block;
2991
+ if (b.kind !== "tool-result") return null;
2992
+ if (b.call === null || typeof b.call !== "object") return null;
2993
+ const call = b.call;
2994
+ if (typeof call.name !== "string" || typeof call.argsRaw !== "string") return null;
2995
+ if (typeof b.seq !== "number" || !Number.isFinite(b.seq)) return null;
2996
+ return {
2997
+ name: call.name,
2998
+ argsRaw: call.argsRaw,
2999
+ seq: b.seq,
3000
+ ...typeof b.time === "number" ? { time: b.time } : {},
3001
+ ...b.isError === true ? { err: true } : { err: false },
3002
+ ...Array.isArray(b.subCalls) ? { subCalls: b.subCalls } : {}
3003
+ };
3004
+ }
3005
+ /** The run_code call's model-authored description — the nested ops' "why". */
3006
+ function programOf(conv) {
3007
+ const description = parseCallArgs(conv?.call?.argsRaw)?.description;
3008
+ return typeof description === "string" && description !== "" ? description : void 0;
3009
+ }
3010
+ /**
3011
+ * Depth guard for nested Code-Mode trees. The SDK bindings exclude `run_code`
3012
+ * itself, so a real tree is one level deep; the cap only bounds defensive
3013
+ * re-entry over a malformed join.
3014
+ */
3015
+ const SUBCALL_MAX_DEPTH = 8;
3016
+ /**
3017
+ * Fold one nested Code-Mode tree: every settled sub-dispatch whose arguments
3018
+ * resolve to a file target books one op, attributed to the nested tool and
3019
+ * located on the parent run_code result. `seen` holds the already-visited
3020
+ * blocks, so a malformed (cyclic) join cannot loop.
3021
+ */
3022
+ function foldSubCalls(blocks, parent, program, add, seen, depth) {
3023
+ if (depth > SUBCALL_MAX_DEPTH) return;
3024
+ for (const block of blocks) {
3025
+ if (block === null || typeof block !== "object" || seen.has(block)) continue;
3026
+ seen.add(block);
3027
+ const sub = subCallOf(block);
3028
+ if (sub !== null) {
3029
+ const args = parseCallArgs(sub.argsRaw);
3030
+ const kind = args !== null ? kindOfCall(sub.name, args) : null;
3031
+ const path = kind !== null ? pathOfArgs(sub.name, args) : null;
3032
+ if (args !== null && kind !== null && path !== null) {
3033
+ const { added, removed } = deltaOf(sub.name, args);
3034
+ const detail = kind === "search" && typeof args.path === "string" && args.path !== "" ? searchDetailOf(args) : void 0;
3035
+ add({
3036
+ seq: sub.seq,
3037
+ ...parent.gone !== void 0 ? { gone: parent.gone } : {},
3038
+ kind,
3039
+ tool: sub.name,
3040
+ ...sub.time !== void 0 ? { time: sub.time } : {},
3041
+ err: sub.err,
3042
+ added,
3043
+ removed,
3044
+ ...detail !== void 0 ? { detail } : {},
3045
+ parent: parent.seq,
3046
+ ...program !== void 0 ? { program } : {}
3047
+ }, path);
3048
+ }
3049
+ if (sub.subCalls !== void 0) foldSubCalls(sub.subCalls, parent, program, add, seen, depth + 1);
3050
+ }
3051
+ }
3052
+ }
2492
3053
  /** Fold every served tool-result node into per-file activity. */
2493
3054
  function activityOf(nodes, convOf, before) {
2494
3055
  const totals = {
@@ -2512,39 +3073,14 @@ window.__ModuleLoader__.load({
2512
3073
  removed: 0
2513
3074
  };
2514
3075
  const byPath = /* @__PURE__ */ new Map();
2515
- let unresolved = 0;
2516
- for (const n of nodes) {
2517
- if (n.cat !== "tool") continue;
2518
- if (before !== null && n.seq >= before) continue;
2519
- const kind = kindOfTool(n.tool);
2520
- if (kind === null) continue;
2521
- const tool = n.tool;
2522
- totals[kind].ops++;
2523
- const conv = convOf(n.seq);
2524
- const args = parseCallArgs(conv?.call?.argsRaw);
2525
- const path = pathOfArgs(tool, args);
2526
- if (args === null || path === null) {
2527
- unresolved++;
2528
- continue;
2529
- }
2530
- const { added, removed } = deltaOf(tool, args);
2531
- const detail = kind === "search" && typeof args.path === "string" && args.path !== "" && typeof args.pattern === "string" && args.pattern !== "" ? args.pattern : void 0;
2532
- const op = {
2533
- seq: n.seq,
2534
- ...n.gone !== void 0 ? { gone: n.gone } : {},
2535
- kind,
2536
- tool,
2537
- ...n.time !== void 0 ? { time: n.time } : {},
2538
- err: n.err === true || conv.isError === true,
2539
- added,
2540
- removed,
2541
- ...detail !== void 0 ? { detail } : {}
2542
- };
3076
+ /** Book one executed op under its file; the totals and the entry move together. */
3077
+ const add = (op, path) => {
3078
+ totals[op.kind].ops++;
2543
3079
  let entry = byPath.get(path);
2544
3080
  if (entry === void 0) {
2545
3081
  entry = {
2546
3082
  path,
2547
- form: formOf(tool, path),
3083
+ form: formOf(op.tool, path),
2548
3084
  reads: 0,
2549
3085
  writes: 0,
2550
3086
  searches: 0,
@@ -2555,13 +3091,63 @@ window.__ModuleLoader__.load({
2555
3091
  };
2556
3092
  byPath.set(path, entry);
2557
3093
  }
2558
- if (kind === "read") entry.reads++;
2559
- else if (kind === "write") entry.writes++;
3094
+ if (op.kind === "read") entry.reads++;
3095
+ else if (op.kind === "write") entry.writes++;
2560
3096
  else entry.searches++;
2561
- entry.added += added;
2562
- entry.removed += removed;
3097
+ entry.added += op.added;
3098
+ entry.removed += op.removed;
2563
3099
  if (op.err) entry.errs++;
2564
3100
  entry.ops.push(op);
3101
+ };
3102
+ for (const n of nodes) {
3103
+ if (n.cat !== "tool") continue;
3104
+ if (before !== null && n.seq >= before) continue;
3105
+ const tool = n.tool;
3106
+ if (tool === void 0) continue;
3107
+ try {
3108
+ const conv = convOf(n.seq);
3109
+ const staticKind = kindOfTool(tool);
3110
+ const args = staticKind !== null || tool === "str_replace_editor" ? parseCallArgs(conv?.call?.argsRaw) : null;
3111
+ const kind = staticKind ?? kindOfCall(tool, args);
3112
+ if (kind !== null) {
3113
+ const err = n.err === true || conv?.isError === true;
3114
+ const files = kind === "search" ? searchFilesOf(conv?.meta) : null;
3115
+ if (files !== null) {
3116
+ const detail = searchDetailOf(args);
3117
+ for (const f of files) add({
3118
+ seq: n.seq,
3119
+ ...n.gone !== void 0 ? { gone: n.gone } : {},
3120
+ kind,
3121
+ tool,
3122
+ ...n.time !== void 0 ? { time: n.time } : {},
3123
+ err,
3124
+ added: 0,
3125
+ removed: 0,
3126
+ ...detail !== void 0 ? { detail } : {},
3127
+ ...f.hits > 0 ? { hits: f.hits } : {}
3128
+ }, f.path);
3129
+ } else if (args !== null) {
3130
+ const path = pathOfArgs(tool, args);
3131
+ if (path !== null) {
3132
+ const { added, removed } = deltaOf(tool, args);
3133
+ const detail = kind === "search" && typeof args.path === "string" && args.path !== "" ? searchDetailOf(args) : void 0;
3134
+ add({
3135
+ seq: n.seq,
3136
+ ...n.gone !== void 0 ? { gone: n.gone } : {},
3137
+ kind,
3138
+ tool,
3139
+ ...n.time !== void 0 ? { time: n.time } : {},
3140
+ err,
3141
+ added,
3142
+ removed,
3143
+ ...detail !== void 0 ? { detail } : {}
3144
+ }, path);
3145
+ }
3146
+ }
3147
+ }
3148
+ const subCalls = conv?.subCalls;
3149
+ if (subCalls !== void 0 && subCalls.length > 0) foldSubCalls(subCalls, n, programOf(conv), add, /* @__PURE__ */ new Set(), 1);
3150
+ } catch {}
2565
3151
  }
2566
3152
  const entries = [...byPath.values()];
2567
3153
  for (const e of entries) {
@@ -2579,8 +3165,7 @@ window.__ModuleLoader__.load({
2579
3165
  entries.sort((a, b) => b.ops[0].seq - a.ops[0].seq);
2580
3166
  return {
2581
3167
  entries,
2582
- totals,
2583
- unresolved
3168
+ totals
2584
3169
  };
2585
3170
  }
2586
3171
  /**
@@ -2594,6 +3179,588 @@ window.__ModuleLoader__.load({
2594
3179
  for (const r of requests) if (r.seq > seq && (gone === void 0 || gone > r.seq)) return r.seq;
2595
3180
  return gone === void 0 ? "live" : null;
2596
3181
  }
3182
+ const SLOT_MAX = 184;
3183
+ const SLOT_MIN = 112;
3184
+ const CAPTION_GUTTER = 8;
3185
+ const LEVEL_H = 154;
3186
+ const CELL_H = 90;
3187
+ const PAD_Y = 56;
3188
+ function asRecord(value) {
3189
+ if (value === null || value === void 0 || typeof value !== "object") return null;
3190
+ return value;
3191
+ }
3192
+ /** Narrow one list-row value; null when it is not a row at all. */
3193
+ function agentRowOf(value) {
3194
+ const rec = asRecord(value);
3195
+ if (rec === null) return null;
3196
+ const projections = asRecord(rec.projectionValues);
3197
+ return {
3198
+ ...typeof rec.displayTitle === "string" ? { displayTitle: rec.displayTitle } : {},
3199
+ ...typeof rec.title === "string" ? { title: rec.title } : {},
3200
+ ...typeof rec.parentId === "string" ? { parentId: rec.parentId } : {},
3201
+ ...typeof rec.origin === "string" ? { origin: rec.origin } : {},
3202
+ running: rec.running === true,
3203
+ completed: rec.completed === true,
3204
+ blank: rec.blank === true,
3205
+ updatedAt: numOf(rec.updatedAt),
3206
+ ...projections !== null ? { projections } : {}
3207
+ };
3208
+ }
3209
+ /** Narrow the subagent identity projection value (null = not a descriptor-backed subagent). */
3210
+ function agentIdentityOf(value) {
3211
+ const rec = asRecord(value);
3212
+ if (rec === null) return null;
3213
+ if (rec.mode !== "one-shot" && rec.mode !== "continuable") return null;
3214
+ return {
3215
+ mode: rec.mode,
3216
+ ...typeof rec.label === "string" && rec.label !== "" ? { label: rec.label } : {}
3217
+ };
3218
+ }
3219
+ /** Narrow the subagent timing projection value into a single duration (null = absent/malformed). */
3220
+ function agentDurationOf(value) {
3221
+ const rec = asRecord(value);
3222
+ if (rec === null) return null;
3223
+ const settled = numOf(rec.settledMs);
3224
+ const active = asRecord(rec.active);
3225
+ const total = settled + (active !== null ? Math.max(0, numOf(active.through) - numOf(active.since)) : 0);
3226
+ return total > 0 ? total : null;
3227
+ }
3228
+ /**
3229
+ * Fold one row's projection values into render-ready stats. The composition
3230
+ * prefers the plugin's own `contextTimeline` (six buckets, provider-anchored
3231
+ * by token-meter's pressure/breakdown — the exact headlineOf derivation the
3232
+ * overview card shows); a pressure-only row (plugin host half absent for that
3233
+ * session) still yields an occupancy ring without slices.
3234
+ */
3235
+ function agentStatsOf(values) {
3236
+ const timeline = timelineOf(values?.contextTimeline);
3237
+ const pressure = contextPressureOf(values?.contextPressure);
3238
+ const breakdown = contextBreakdownOf(values?.contextBreakdown);
3239
+ const usage = tokenUsageOf(values?.tokenUsage);
3240
+ let head = null;
3241
+ if (timeline !== null) head = headlineOf(timeline, pressure, breakdown);
3242
+ else if (pressure !== null) {
3243
+ const tokens = typeof pressure.projectedTokens === "number" ? pressure.projectedTokens : typeof pressure.pressureTokens === "number" ? pressure.pressureTokens : null;
3244
+ if (tokens !== null) {
3245
+ const window = typeof pressure.contextWindow === "number" && pressure.contextWindow > 0 ? pressure.contextWindow : void 0;
3246
+ head = {
3247
+ tokens,
3248
+ window,
3249
+ pct: window !== void 0 ? Math.min(100, Math.round(tokens / window * 100)) : null,
3250
+ parts: []
3251
+ };
3252
+ }
3253
+ }
3254
+ const billed = usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null;
3255
+ return {
3256
+ head,
3257
+ requests: timeline !== null ? timeline.requests.length : 0,
3258
+ billed,
3259
+ durationMs: agentDurationOf(values?.subagentTiming),
3260
+ identity: agentIdentityOf(values?.subagent)
3261
+ };
3262
+ }
3263
+ /**
3264
+ * Build the current session's agent family from a session-list snapshot:
3265
+ * walk up `parentId` to the topmost known ancestor, then DFS its whole
3266
+ * subtree (blank placeholder rows excluded). Null when there is no anchor —
3267
+ * no current session, or a snapshot without a `byId` table (older harness).
3268
+ * The current session synthesizes a row when the list has not delivered it
3269
+ * yet, so the card can still show its live self stats.
3270
+ */
3271
+ function agentForestOf(snapshot, currentId, self) {
3272
+ const byId = asRecord(asRecord(snapshot)?.byId);
3273
+ if (byId === null || currentId === void 0 || currentId === "") return null;
3274
+ const rows = /* @__PURE__ */ new Map();
3275
+ for (const key of Object.keys(byId)) {
3276
+ const row = agentRowOf(byId[key]);
3277
+ if (row !== null && (!row.blank || key === currentId)) rows.set(key, row);
3278
+ }
3279
+ if (!rows.has(currentId)) rows.set(currentId, {
3280
+ running: false,
3281
+ completed: false,
3282
+ blank: false,
3283
+ updatedAt: 0
3284
+ });
3285
+ let root = currentId;
3286
+ const chain = /* @__PURE__ */ new Set([currentId]);
3287
+ for (;;) {
3288
+ const parent = rows.get(root)?.parentId;
3289
+ if (parent === void 0 || !rows.has(parent) || chain.has(parent)) break;
3290
+ chain.add(parent);
3291
+ root = parent;
3292
+ }
3293
+ const rootRow = rows.get(root);
3294
+ /* v8 ignore next 2 -- root is currentId (inserted above) or a parent
3295
+ verified with rows.has, so its row always exists. */
3296
+ if (rootRow === void 0) return null;
3297
+ const childrenOf = /* @__PURE__ */ new Map();
3298
+ for (const [id, row] of rows) {
3299
+ if (row.parentId === void 0 || !rows.has(row.parentId)) continue;
3300
+ const list = childrenOf.get(row.parentId) ?? [];
3301
+ list.push({
3302
+ id,
3303
+ row
3304
+ });
3305
+ childrenOf.set(row.parentId, list);
3306
+ }
3307
+ for (const kids of childrenOf.values()) kids.sort((a, b) => {
3308
+ const runDelta = Number(b.row.running) - Number(a.row.running);
3309
+ if (runDelta !== 0) return runDelta;
3310
+ const timeDelta = b.row.updatedAt - a.row.updatedAt;
3311
+ return timeDelta !== 0 ? timeDelta : a.id < b.id ? -1 : 1;
3312
+ });
3313
+ const measure = (id, seen) => {
3314
+ if (seen.has(id)) return 0;
3315
+ seen.add(id);
3316
+ let total = 1;
3317
+ for (const kid of childrenOf.get(id) ?? []) total += measure(kid.id, seen);
3318
+ return total;
3319
+ };
3320
+ const total = measure(root, /* @__PURE__ */ new Set());
3321
+ const nodes = [];
3322
+ const edges = [];
3323
+ const visit = (id, row, parentId, depth, seen, family) => {
3324
+ if (seen.has(id) || nodes.length >= 25) return;
3325
+ seen.add(id);
3326
+ const stats = agentStatsOf(row.projections);
3327
+ const identity = stats.identity;
3328
+ const node = {
3329
+ ...stats,
3330
+ id,
3331
+ label: identity?.label ?? row.title ?? row.displayTitle ?? id,
3332
+ ...parentId !== void 0 ? { parentId } : {},
3333
+ depth,
3334
+ family,
3335
+ isCurrent: id === currentId,
3336
+ running: row.running,
3337
+ completed: row.completed,
3338
+ subagent: row.origin === "subagent" || identity !== null
3339
+ };
3340
+ if (node.isCurrent && self !== void 0) {
3341
+ node.head = self.head ?? node.head;
3342
+ node.billed = self.billed ?? node.billed;
3343
+ node.requests = self.requests > 0 ? self.requests : node.requests;
3344
+ }
3345
+ nodes.push(node);
3346
+ if (parentId !== void 0) edges.push({
3347
+ from: parentId,
3348
+ to: id
3349
+ });
3350
+ (childrenOf.get(id) ?? []).forEach((kid, ki) => {
3351
+ visit(kid.id, kid.row, id, depth + 1, seen, depth === 0 ? ki : family);
3352
+ });
3353
+ };
3354
+ visit(root, rootRow, void 0, 0, /* @__PURE__ */ new Set(), -1);
3355
+ return {
3356
+ nodes,
3357
+ edges,
3358
+ overflow: Math.max(0, total - nodes.length),
3359
+ solo: nodes.length === 1
3360
+ };
3361
+ }
3362
+ /**
3363
+ * Tidy top-down tree layout: one row per depth level, siblings claim leaf
3364
+ * slots, parents center over their children. Fully responsive to the stage's
3365
+ * visible width: the slot pitch stretches up to SLOT_MAX and compresses down
3366
+ * to SLOT_MIN (captions wrap tighter); a level that still overflows wraps
3367
+ * into bands of at most a per-level node count derived from the stage width —
3368
+ * vertical room is cheaper than horizontal scrolling. Links exit a parent at
3369
+ * its cell bottom (below the caption zone) and enter the child at its top,
3370
+ * so a connector never crosses a label.
3371
+ */
3372
+ function layoutForest(forest, stageWidth = 0) {
3373
+ const childrenOf = /* @__PURE__ */ new Map();
3374
+ for (const n of forest.nodes) {
3375
+ if (n.parentId === void 0) continue;
3376
+ const kids = childrenOf.get(n.parentId) ?? [];
3377
+ kids.push(n);
3378
+ childrenOf.set(n.parentId, kids);
3379
+ }
3380
+ const slotOf = /* @__PURE__ */ new Map();
3381
+ let leafSlots = 0;
3382
+ const place = (node) => {
3383
+ const kids = childrenOf.get(node.id) ?? [];
3384
+ if (kids.length === 0) {
3385
+ const slot = leafSlots;
3386
+ leafSlots++;
3387
+ slotOf.set(node.id, slot);
3388
+ return slot;
3389
+ }
3390
+ let first = 0;
3391
+ let last = 0;
3392
+ kids.forEach((kid, index) => {
3393
+ const slot = place(kid);
3394
+ if (index === 0) first = slot;
3395
+ last = slot;
3396
+ });
3397
+ const slot = (first + last) / 2;
3398
+ slotOf.set(node.id, slot);
3399
+ return slot;
3400
+ };
3401
+ /* v8 ignore next 1 -- a forest always holds at least the (possibly
3402
+ synthesized) current node. */
3403
+ if (forest.nodes.length > 0) place(forest.nodes[0]);
3404
+ const perLevel = stageWidth > 0 ? Math.max(2, Math.floor(stageWidth / SLOT_MIN)) : 0;
3405
+ if (perLevel > 0 && leafSlots > perLevel) {
3406
+ const bandSlot = Math.min(SLOT_MAX, stageWidth / perLevel);
3407
+ const width = perLevel * bandSlot;
3408
+ const points = [];
3409
+ let row = 0;
3410
+ const emitBand = (nodes, depth) => {
3411
+ const inset = (width - nodes.length * bandSlot) / 2;
3412
+ nodes.forEach((node, i) => {
3413
+ points.push({
3414
+ id: node.id,
3415
+ x: inset + (i + .5) * bandSlot,
3416
+ y: PAD_Y + row * LEVEL_H,
3417
+ depth
3418
+ });
3419
+ });
3420
+ row++;
3421
+ let band = [];
3422
+ const flush = () => {
3423
+ if (band.length === 0) return;
3424
+ const packed = band;
3425
+ band = [];
3426
+ emitBand(packed, depth + 1);
3427
+ };
3428
+ for (const node of nodes) {
3429
+ const kids = childrenOf.get(node.id) ?? [];
3430
+ for (let start = 0; start < kids.length; start += perLevel) {
3431
+ const group = kids.slice(start, start + perLevel);
3432
+ if (band.length + group.length > perLevel) flush();
3433
+ band.push(...group);
3434
+ if (band.length === perLevel) flush();
3435
+ }
3436
+ }
3437
+ flush();
3438
+ };
3439
+ /* v8 ignore next 1 -- a forest always holds at least the current node. */
3440
+ if (forest.nodes.length > 0) emitBand([forest.nodes[0]], 0);
3441
+ return {
3442
+ width,
3443
+ height: PAD_Y + (row - 1) * LEVEL_H + CELL_H + 28,
3444
+ captionW: bandSlot - CAPTION_GUTTER,
3445
+ points,
3446
+ links: linksOf(forest, points)
3447
+ };
3448
+ }
3449
+ const slot = stageWidth > 0 && leafSlots > 1 ? Math.min(SLOT_MAX, stageWidth / leafSlots) : SLOT_MAX;
3450
+ const points = forest.nodes.map((node) => ({
3451
+ id: node.id,
3452
+ /* v8 ignore next 1 -- place() visits every node: the forest is exactly
3453
+ the root's subtree by construction. */
3454
+ x: (slotOf.get(node.id) ?? 0) * slot + slot / 2,
3455
+ y: PAD_Y + node.depth * LEVEL_H,
3456
+ depth: node.depth
3457
+ }));
3458
+ const maxDepth = points.reduce((max, p) => Math.max(max, p.depth), 0);
3459
+ return {
3460
+ width: leafSlots * slot,
3461
+ height: PAD_Y + maxDepth * LEVEL_H + CELL_H + 28,
3462
+ captionW: slot - CAPTION_GUTTER,
3463
+ points,
3464
+ links: linksOf(forest, points)
3465
+ };
3466
+ }
3467
+ /**
3468
+ * Family hue by level-1 subtree index: the golden angle keeps consecutive
3469
+ * families maximally separated on the color wheel without a hand-tuned palette.
3470
+ */
3471
+ function familyHue(index) {
3472
+ return `hsl(${Math.round(index * 137.508) % 360} 58% 52%)`;
3473
+ }
3474
+ /** Parent→child links: exit the parent's cell bottom, enter the child's top. */
3475
+ function linksOf(forest, points) {
3476
+ const pointOf = new Map(points.map((p) => [p.id, p]));
3477
+ const nodeOf = new Map(forest.nodes.map((n) => [n.id, n]));
3478
+ const runningIds = new Set(forest.nodes.filter((n) => n.running).map((n) => n.id));
3479
+ const links = [];
3480
+ for (const edge of forest.edges) {
3481
+ const from = pointOf.get(edge.from);
3482
+ const to = pointOf.get(edge.to);
3483
+ /* v8 ignore next 2 -- edges are emitted only for visited parent/child
3484
+ pairs, so both points always exist. */
3485
+ if (from === void 0 || to === void 0) continue;
3486
+ links.push({
3487
+ to: edge.to,
3488
+ running: runningIds.has(edge.to),
3489
+ /* v8 ignore next 1 -- edges only connect visited nodes. */
3490
+ color: familyHue(nodeOf.get(edge.to)?.family ?? 0),
3491
+ x1: from.x,
3492
+ y1: from.y + CELL_H,
3493
+ x2: to.x,
3494
+ y2: to.y - 26 - 10
3495
+ });
3496
+ }
3497
+ return links;
3498
+ }
3499
+ /**
3500
+ * One fused ring per agent — the exact semantics of the chat composer's own
3501
+ * context ring: the composition parts, scaled to the occupancy share of the
3502
+ * window, fill the circle, and a neutral remainder marks the free window.
3503
+ * With no known window the composition fills the whole circle; with no
3504
+ * composition (pressure-only rows) a single threshold-colored arc carries
3505
+ * the occupancy; a known window with zero occupancy draws the free outline.
3506
+ */
3507
+ function ringSegments(parts, pct, radius, fallbackColor) {
3508
+ const circumference = 2 * Math.PI * radius;
3509
+ const occ = pct === null ? 1 : Math.min(100, Math.max(0, pct)) / 100;
3510
+ let total = 0;
3511
+ for (const p of parts) total += p.value > 0 ? p.value : 0;
3512
+ const segs = [];
3513
+ let offset = 0;
3514
+ if (total > 0) for (const p of parts) {
3515
+ if (p.value <= 0) continue;
3516
+ const len = circumference * (p.value / total) * occ;
3517
+ if (len <= 0) continue;
3518
+ segs.push({
3519
+ key: p.key,
3520
+ color: p.color,
3521
+ len,
3522
+ offset,
3523
+ free: false
3524
+ });
3525
+ offset += len;
3526
+ }
3527
+ else if (pct !== null && occ > 0) {
3528
+ segs.push({
3529
+ key: "fill",
3530
+ color: fallbackColor,
3531
+ len: circumference * occ,
3532
+ offset: 0,
3533
+ free: false
3534
+ });
3535
+ offset = circumference * occ;
3536
+ }
3537
+ if (pct !== null && offset < circumference) segs.push({
3538
+ key: "free",
3539
+ color: "",
3540
+ len: circumference - offset,
3541
+ offset,
3542
+ free: true
3543
+ });
3544
+ return segs;
3545
+ }
3546
+ /** Session-switch navigation, fail-soft: a stale row (list rebuilt between snapshot and click) loses its open() race and is ignored. */
3547
+ function openAgentSession(face, id) {
3548
+ if (face === null || typeof face.open !== "function") return;
3549
+ try {
3550
+ face.open(id);
3551
+ } catch {}
3552
+ }
3553
+ /** Narrow `ctx.get('sessions')` to the card's face (null = harness without the outward sessions service). */
3554
+ function sessionsFaceOf(ctx) {
3555
+ const rec = asRecord(ctx.get("sessions"));
3556
+ if (rec === null) return null;
3557
+ const list = asRecord(rec.list);
3558
+ if (list === null || typeof list.getSnapshot !== "function" || typeof list.subscribe !== "function") return null;
3559
+ return rec;
3560
+ }
3561
+ /** Compact duration: `42s`, `3m05s`, `1h07m` (shared by both locales). */
3562
+ function fmtDuration(ms) {
3563
+ if (!Number.isFinite(ms) || ms < 0) return "—";
3564
+ const s = Math.round(ms / 1e3);
3565
+ if (s < 60) return `${s}s`;
3566
+ const m = Math.floor(s / 60);
3567
+ if (m < 60) return `${m}m${String(s % 60).padStart(2, "0")}s`;
3568
+ return `${Math.floor(m / 60)}h${String(m % 60).padStart(2, "0")}m`;
3569
+ }
3570
+ //#endregion
3571
+ //#region src/client/components/agentGraph.tsx
3572
+ /** Caption box height under a node (3 wrapped label lines + the tokens line). */
3573
+ const CAPTION_H = 60;
3574
+ /** Fallback arc color for pressure-only nodes (no composition data), by fill ratio. */
3575
+ function ringColorOf(pct) {
3576
+ if (pct === null) return "var(--dsw-alias-border-l1)";
3577
+ if (pct >= 90) return "#ef4444";
3578
+ if (pct >= 70) return "#f59e0b";
3579
+ return "#22c55e";
3580
+ }
3581
+ function makeAgentGraph(ctx, kit) {
3582
+ const { t, fmt, catLabel } = kit;
3583
+ function AgentGraph(props) {
3584
+ const face = React.useMemo(() => sessionsFaceOf(ctx), []);
3585
+ const subscribe = React.useCallback((fn) => {
3586
+ if (face === null) return () => {};
3587
+ /* v8 ignore next 2 -- sessionsFaceOf returns a face only after proving list.subscribe. */
3588
+ if (face.list === void 0) return () => {};
3589
+ return face.list.subscribe(fn);
3590
+ }, [face]);
3591
+ const getSnapshot = React.useCallback(() => {
3592
+ if (face === null) return null;
3593
+ /* v8 ignore next 2 -- sessionsFaceOf proves list before returning the face. */
3594
+ if (face.list === void 0) return null;
3595
+ return face.list.getSnapshot();
3596
+ }, [face]);
3597
+ const snapshot = React.useSyncExternalStore(subscribe, getSnapshot);
3598
+ const sessionId = props.sessionId;
3599
+ const [hoverId, setHoverId] = React.useState(null);
3600
+ const stageRef = React.useRef(null);
3601
+ const [stageWidth, setStageWidth] = React.useState(0);
3602
+ React.useEffect(() => {
3603
+ /* v8 ignore start -- jsdom has neither ResizeObserver nor layout; tests exercise the natural-pitch fallback (stageWidth 0). */
3604
+ const el = stageRef.current;
3605
+ if (el === null || typeof ResizeObserver !== "function") return;
3606
+ setStageWidth(el.clientWidth);
3607
+ const observer = new ResizeObserver(() => {
3608
+ setStageWidth(el.clientWidth);
3609
+ });
3610
+ observer.observe(el);
3611
+ return () => {
3612
+ observer.disconnect();
3613
+ };
3614
+ /* v8 ignore stop */
3615
+ }, []);
3616
+ React.useEffect(() => {
3617
+ if (face === null || typeof sessionId !== "string" || sessionId === "") return;
3618
+ if (typeof face.refreshSubagents !== "function") return;
3619
+ face.refreshSubagents(sessionId).catch(() => {});
3620
+ }, [face, sessionId]);
3621
+ const built = React.useMemo(() => {
3622
+ const forest = agentForestOf(snapshot, sessionId, props.self);
3623
+ return forest !== null ? {
3624
+ forest,
3625
+ layout: layoutForest(forest, stageWidth)
3626
+ } : null;
3627
+ }, [
3628
+ snapshot,
3629
+ sessionId,
3630
+ props.self,
3631
+ stageWidth
3632
+ ]);
3633
+ if (built === null) return null;
3634
+ const { forest, layout } = built;
3635
+ const byId = new Map(forest.nodes.map((n) => [n.id, n]));
3636
+ /* v8 ignore next 1 -- agentForestOf anchors the forest at the current
3637
+ session, so a current node always exists. */
3638
+ const current = forest.nodes.find((n) => n.isCurrent) ?? forest.nodes[0];
3639
+ const inspected = (hoverId !== null ? byId.get(hoverId) : void 0) ?? current;
3640
+ const runningCount = forest.nodes.filter((n) => n.running).length;
3641
+ let totalTokens = 0;
3642
+ for (const n of forest.nodes) totalTokens += n.head !== null ? n.head.tokens : 0;
3643
+ const open = (id) => {
3644
+ if (id === current.id) return;
3645
+ openAgentSession(face, id);
3646
+ };
3647
+ const keyOpen = (id) => (ev) => {
3648
+ if (ev.key !== "Enter" && ev.key !== " ") return;
3649
+ ev.preventDefault();
3650
+ open(id);
3651
+ };
3652
+ 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", {
3653
+ className: "lc-agents-stage",
3654
+ ref: stageRef
3655
+ }, /* @__PURE__ */ React.createElement("svg", {
3656
+ className: "lc-agents-svg",
3657
+ width: layout.width,
3658
+ height: layout.height,
3659
+ viewBox: `0 0 ${layout.width} ${layout.height}`
3660
+ }, layout.links.map((link) => {
3661
+ const d = `M ${link.x1} ${link.y1} L ${link.x2} ${link.y2}`;
3662
+ return /* @__PURE__ */ React.createElement("g", { key: link.to }, /* @__PURE__ */ React.createElement("path", {
3663
+ className: "lc-agents-link" + (link.running ? " lc-agents-link-live" : ""),
3664
+ d,
3665
+ stroke: link.color,
3666
+ fill: "none"
3667
+ }), link.running ? /* @__PURE__ */ React.createElement("path", {
3668
+ className: "lc-agents-flow",
3669
+ d,
3670
+ stroke: link.color,
3671
+ fill: "none"
3672
+ }) : null);
3673
+ }), forest.nodes.map((node) => {
3674
+ const point = layout.points.find((p) => p.id === node.id);
3675
+ /* v8 ignore next 2 -- layoutForest positions every forest node,
3676
+ so the lookup never misses. */
3677
+ if (point === void 0) return null;
3678
+ return /* @__PURE__ */ React.createElement(AgentNodeView, {
3679
+ key: node.id,
3680
+ node,
3681
+ x: point.x,
3682
+ y: point.y,
3683
+ captionW: layout.captionW,
3684
+ hovered: hoverId === node.id,
3685
+ onHover: setHoverId,
3686
+ onOpen: open,
3687
+ onKeyOpen: keyOpen(node.id),
3688
+ t,
3689
+ fmt
3690
+ });
3691
+ }))), forest.solo ? /* @__PURE__ */ React.createElement("div", { className: "lc-empty lc-agents-solo" }, t("agents.solo")) : null, /* @__PURE__ */ React.createElement(Inspector, {
3692
+ node: inspected,
3693
+ t,
3694
+ fmt
3695
+ }), /* @__PURE__ */ React.createElement("div", { className: "lc-agents-legend" }, CATS.map((c) => /* @__PURE__ */ React.createElement("span", {
3696
+ key: c.key,
3697
+ className: "lc-agents-legend-item"
3698
+ }, /* @__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"))));
3699
+ }
3700
+ return AgentGraph;
3701
+ }
3702
+ function AgentNodeView(props) {
3703
+ const { node, x, y, captionW } = props;
3704
+ const pct = node.head !== null ? node.head.pct : null;
3705
+ const ring = 2 * Math.PI * 20;
3706
+ const segs = node.head !== null ? ringSegments(node.head.parts, pct, 20, ringColorOf(pct)) : [];
3707
+ 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");
3708
+ return /* @__PURE__ */ React.createElement("g", {
3709
+ className: cls,
3710
+ transform: `translate(${x}, ${y})`,
3711
+ "data-agent": node.id,
3712
+ role: node.isCurrent ? "img" : "button",
3713
+ tabIndex: node.isCurrent ? void 0 : 0,
3714
+ onClick: () => {
3715
+ props.onOpen(node.id);
3716
+ },
3717
+ onKeyDown: props.onKeyOpen,
3718
+ onMouseEnter: () => {
3719
+ props.onHover(node.id);
3720
+ },
3721
+ onMouseLeave: () => {
3722
+ props.onHover(null);
3723
+ }
3724
+ }, /* @__PURE__ */ React.createElement("circle", {
3725
+ className: "lc-agent-halo",
3726
+ r: 35
3727
+ }), /* @__PURE__ */ React.createElement("circle", {
3728
+ className: "lc-agent-track",
3729
+ r: 26
3730
+ }), segs.map((seg) => /* @__PURE__ */ React.createElement("circle", {
3731
+ key: seg.key,
3732
+ className: "lc-agent-seg" + (seg.free ? " lc-agent-free" : ""),
3733
+ r: 20,
3734
+ stroke: seg.free ? void 0 : seg.color,
3735
+ strokeDasharray: `${seg.len} ${ring - seg.len}`,
3736
+ strokeDashoffset: -seg.offset,
3737
+ transform: "rotate(-90)"
3738
+ })), /* @__PURE__ */ React.createElement("text", {
3739
+ className: "lc-agent-pct",
3740
+ textAnchor: "middle",
3741
+ dy: "0.32em"
3742
+ }, pct !== null ? `${pct}%` : node.head !== null ? props.fmt(node.head.tokens) : "—"), /* @__PURE__ */ React.createElement("foreignObject", {
3743
+ x: -captionW / 2,
3744
+ y: 34,
3745
+ width: captionW,
3746
+ height: CAPTION_H
3747
+ }, /* @__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) : "—"))));
3748
+ }
3749
+ /** The detail strip mirroring the hovered (or current) node: identity, occupancy, activity, and the open hint. */
3750
+ function Inspector(props) {
3751
+ const { node, t, fmt } = props;
3752
+ const bits = [];
3753
+ if (node.head !== null) {
3754
+ const head = node.head;
3755
+ const window = head.window !== void 0 ? ` / ${fmt(head.window)}` : "";
3756
+ const pct = head.pct !== null ? ` · ${head.pct}%` : "";
3757
+ bits.push(`${fmt(head.tokens)}${window}${pct}`);
3758
+ }
3759
+ if (node.requests > 0) bits.push(t("agents.requests", { n: node.requests }));
3760
+ if (node.billed !== null && node.billed > 0) bits.push(t("agents.billed", { n: fmt(node.billed) }));
3761
+ if (node.durationMs !== null) bits.push(fmtDuration(node.durationMs));
3762
+ 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);
3763
+ }
2597
3764
  //#endregion
2598
3765
  //#region src/client/components/events.tsx
2599
3766
  const EVENT_ICONS = {
@@ -2656,6 +3823,14 @@ window.__ModuleLoader__.load({
2656
3823
  eventAt
2657
3824
  };
2658
3825
  }
3826
+ /**
3827
+ * Set the native `title` on every label whose text overflows its box (the styled tips cover cards, but a plain
3828
+ * ellipsis row still needs the native fallback); reads (scrollWidth/clientWidth) and writes (title) stay separate
3829
+ * from layout-affecting work, and callers gate WHEN this runs so it never becomes a per-render forced layout.
3830
+ */
3831
+ function syncTitles(root) {
3832
+ for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
3833
+ }
2659
3834
  function makeEventList(kit) {
2660
3835
  const { t, fmt, fmtTime, eventLabel, eventAt } = kit;
2661
3836
  return function EventList(props) {
@@ -2663,26 +3838,29 @@ window.__ModuleLoader__.load({
2663
3838
  React.useLayoutEffect(() => {
2664
3839
  const root = rootRef.current;
2665
3840
  if (!root) return;
2666
- const sync = () => {
2667
- for (const el of root.querySelectorAll(".lc-event-label")) el.title = el.scrollWidth > el.clientWidth ? el.textContent || "" : "";
3841
+ syncTitles(root);
3842
+ }, [props.events]);
3843
+ React.useEffect(() => {
3844
+ const onResize = () => {
3845
+ const root = rootRef.current;
3846
+ if (root !== null) syncTitles(root);
2668
3847
  };
2669
- sync();
2670
- window.addEventListener("resize", sync);
3848
+ window.addEventListener("resize", onResize);
2671
3849
  return () => {
2672
- window.removeEventListener("resize", sync);
3850
+ window.removeEventListener("resize", onResize);
2673
3851
  };
2674
- });
3852
+ }, []);
2675
3853
  if (props.events.length === 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("events.empty"));
2676
3854
  const sorted = props.events.slice().reverse();
2677
3855
  return /* @__PURE__ */ React.createElement("div", {
2678
3856
  className: "lc-events",
2679
3857
  ref: rootRef
2680
- }, sorted.map((ev, i) => {
3858
+ }, sorted.map((ev) => {
2681
3859
  const label = eventLabel(ev);
2682
3860
  const at = eventAt(ev);
2683
3861
  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
3862
  return /* @__PURE__ */ React.createElement("div", {
2685
- key: `${ev.seq}-${i}`,
3863
+ key: ev.seq,
2686
3864
  className: "lc-event"
2687
3865
  }, /* @__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
3866
  }));
@@ -2690,13 +3868,8 @@ window.__ModuleLoader__.load({
2690
3868
  }
2691
3869
  //#endregion
2692
3870
  //#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
3871
  function makeFileCard(kit, settings) {
2696
3872
  const { t, fmt, fmtTime } = kit;
2697
- function formGlyph(form) {
2698
- return form === "image" ? "🖼" : form === "dir" ? "📁" : "📄";
2699
- }
2700
3873
  function matches(e, f) {
2701
3874
  if (f === "all") return true;
2702
3875
  if (f === "image") return e.form === "image";
@@ -2708,7 +3881,7 @@ window.__ModuleLoader__.load({
2708
3881
  function DeltaPair(props) {
2709
3882
  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
3883
  }
2711
- return function FileCard(props) {
3884
+ return React.memo(function FileCard(props) {
2712
3885
  const { activity } = props;
2713
3886
  const [filter, setFilter] = React.useState("all");
2714
3887
  const [sort, setSort] = React.useState(() => settings.defaultFileSort());
@@ -2744,7 +3917,7 @@ window.__ModuleLoader__.load({
2744
3917
  ops: activity.totals.image.ops
2745
3918
  }
2746
3919
  ];
2747
- const opLine = (op) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-tool" }, op.tool), op.detail !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.detail) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
3920
+ const opLine = (op) => /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-time" }, op.time !== void 0 ? fmtTime(op.time) : "—"), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-tool" }, op.tool), op.detail !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.detail) : null, op.hits !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, t("files.hits", { n: fmt(op.hits) })) : null, op.detail === void 0 && op.hits === void 0 && op.program !== void 0 ? /* @__PURE__ */ React.createElement("span", { className: "lc-fa-op-detail" }, op.program) : null, op.added + op.removed > 0 ? /* @__PURE__ */ React.createElement(DeltaPair, {
2748
3921
  added: op.added,
2749
3922
  removed: op.removed
2750
3923
  }) : null, op.err ? /* @__PURE__ */ React.createElement("span", {
@@ -2796,6 +3969,7 @@ window.__ModuleLoader__.load({
2796
3969
  const slash = trimmed.lastIndexOf("/");
2797
3970
  const dir = slash >= 0 ? trimmed.slice(0, slash + 1) : "";
2798
3971
  const base = slash >= 0 ? trimmed.slice(slash + 1) : trimmed;
3972
+ const glyph = glyphOf(e.path, e.form);
2799
3973
  return /* @__PURE__ */ React.createElement("div", {
2800
3974
  key: e.path,
2801
3975
  className: "lc-fa-item" + (open ? " lc-fa-item-on" : "")
@@ -2808,8 +3982,8 @@ window.__ModuleLoader__.load({
2808
3982
  }
2809
3983
  }, /* @__PURE__ */ React.createElement("span", { className: "lc-br-chev" + (open ? " lc-br-chev-on" : "") }, "▸"), /* @__PURE__ */ React.createElement("span", {
2810
3984
  className: "lc-fa-form",
2811
- title: t("files.form." + e.form)
2812
- }, formGlyph(e.form)), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-path" }, dir !== "" ? /* @__PURE__ */ React.createElement("em", null, dir) : null, /* @__PURE__ */ React.createElement("b", null, base)), e.reads > 0 ? /* @__PURE__ */ React.createElement("span", {
3985
+ title: t(glyph.tip)
3986
+ }, glyph.glyph), /* @__PURE__ */ React.createElement("span", { className: "lc-fa-path" }, dir !== "" ? /* @__PURE__ */ React.createElement("em", null, dir) : null, /* @__PURE__ */ React.createElement("b", null, base)), e.reads > 0 ? /* @__PURE__ */ React.createElement("span", {
2813
3987
  className: "lc-fa-badge lc-fa-b-read",
2814
3988
  title: t("files.kind.read")
2815
3989
  }, /* @__PURE__ */ React.createElement("i", null), fmt(e.reads)) : null, e.writes > 0 ? /* @__PURE__ */ React.createElement("span", {
@@ -2824,10 +3998,11 @@ window.__ModuleLoader__.load({
2824
3998
  }) : null, e.errs > 0 ? /* @__PURE__ */ React.createElement("span", {
2825
3999
  className: "lc-br-err-dot",
2826
4000
  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) => {
4001
+ }) : 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, i) => {
2828
4002
  const onLocate = props.onLocate;
4003
+ const key = `${op.seq}:${i}`;
2829
4004
  return onLocate !== void 0 ? /* @__PURE__ */ React.createElement("button", {
2830
- key: op.seq,
4005
+ key,
2831
4006
  type: "button",
2832
4007
  className: "lc-fa-op lc-fa-op-link",
2833
4008
  title: t("files.locate"),
@@ -2835,28 +4010,41 @@ window.__ModuleLoader__.load({
2835
4010
  onLocate(op);
2836
4011
  }
2837
4012
  }, opLine(op)) : /* @__PURE__ */ React.createElement("div", {
2838
- key: op.seq,
4013
+ key,
2839
4014
  className: "lc-fa-op"
2840
4015
  }, 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
- };
4016
+ })) : null);
4017
+ }))));
4018
+ });
2844
4019
  }
2845
4020
  const PLUGIN_REPO = "https://github.com/bowenliang123/dsh-context";
2846
4021
  const PLUGIN_REPO_SHORT = PLUGIN_REPO.replace(/^https?:\/\/github\.com\//, "");
2847
4022
  //#endregion
2848
4023
  //#region src/client/latestVersion.ts
2849
4024
  /**
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.
4025
+ * One live check for the Plugin-info card: lazy with 1h TTL; registries
4026
+ * are tried strictly in order official npm first, npmmirror only as a
4027
+ * fallback — and one-at-a-time, never in parallel. Each source's failure
4028
+ * (non-ok response, missing/non-string version, network error) advances
4029
+ * to the next one; only an all-source failure resolves to null so the
4030
+ * card silently keeps its static version.
2852
4031
  */
2853
- const REGISTRY_URL = "https://registry.npmjs.org/dsh-context/latest";
4032
+ const REGISTRY_HOSTS = ["https://registry.npmjs.org", "https://registry.npmmirror.com"];
2854
4033
  const TTL_MS = 36e5;
2855
4034
  let cached = null;
4035
+ function readVersion(host) {
4036
+ 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);
4037
+ }
2856
4038
  function fetchLatestVersion() {
2857
4039
  if (!cached || Date.now() - cached.at >= TTL_MS) cached = {
2858
4040
  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)
4041
+ promise: (async () => {
4042
+ for (const host of REGISTRY_HOSTS) {
4043
+ const v = await readVersion(host);
4044
+ if (v !== null) return v;
4045
+ }
4046
+ return null;
4047
+ })()
2860
4048
  };
2861
4049
  return cached.promise;
2862
4050
  }
@@ -2885,7 +4073,7 @@ window.__ModuleLoader__.load({
2885
4073
  return function PluginInfo() {
2886
4074
  const [latest, setLatest] = React.useState(null);
2887
4075
  React.useEffect(() => {
2888
- if ("0.33.1".includes("-dev")) return;
4076
+ if ("0.34.1".includes("-dev")) return;
2889
4077
  let on = true;
2890
4078
  fetchLatestVersion().then((v) => {
2891
4079
  if (on && v) setLatest(v);
@@ -2894,13 +4082,18 @@ window.__ModuleLoader__.load({
2894
4082
  on = false;
2895
4083
  };
2896
4084
  }, []);
2897
- const update = latest !== null && isNewerVersion(latest, "0.33.1") ? latest : null;
2898
- const nameValue = ["dsh-context (v0.33.1)"];
4085
+ const update = latest !== null && isNewerVersion(latest, "0.34.1") ? latest : null;
4086
+ const nameValue = ["dsh-context (v0.34.1)"];
2899
4087
  if (update) nameValue.push(/* @__PURE__ */ React.createElement("span", {
2900
4088
  key: "update",
2901
4089
  className: "lc-pi-update"
2902
4090
  }, "↑ v" + update));
2903
- return /* @__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("plugin.title")), /* @__PURE__ */ React.createElement("span", { className: "lc-card-sub" }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
4091
+ return /* @__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("plugin.title")), /* @__PURE__ */ React.createElement("a", {
4092
+ className: "lc-card-sub lc-pi-hint",
4093
+ href: PLUGIN_REPO,
4094
+ target: "_blank",
4095
+ rel: "noreferrer"
4096
+ }, t("plugin.hint"))), /* @__PURE__ */ React.createElement("div", { className: "lc-pi-grid" }, row(t("plugin.name"), nameValue, PLUGIN_REPO + "/releases"), row(t("plugin.github"), PLUGIN_REPO_SHORT, PLUGIN_REPO)));
2904
4097
  };
2905
4098
  }
2906
4099
  //#endregion
@@ -2910,21 +4103,22 @@ window.__ModuleLoader__.load({
2910
4103
  /**
2911
4104
  * One brief row: a fixed-width kind tag plus one glanceable line. The tag carries a styled, instant explanation bubble (the
2912
4105
  * `.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.
4106
+ * Clickable when the browser linkage is wired AND the row carries a node (the always-present In row's empty state stays inert).
2914
4107
  */
2915
4108
  function BriefRow(props) {
2916
- const cls = "lc-brief-row" + (props.onLocate !== void 0 ? " lc-brief-row-link" : "");
2917
4109
  const inner = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "lc-brief-tag" }, props.tag, /* @__PURE__ */ React.createElement("span", {
2918
4110
  className: "lc-brief-tip",
2919
4111
  role: "tooltip"
2920
4112
  }, props.tagTip)), props.children);
2921
- if (props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: cls }, inner);
4113
+ if (props.node === void 0 || props.onLocate === void 0) return /* @__PURE__ */ React.createElement("div", { className: "lc-brief-row" }, inner);
4114
+ const node = props.node;
4115
+ const onLocate = props.onLocate;
2922
4116
  const locate = () => {
2923
- props.onLocate?.(props.node, props.isResponse);
4117
+ onLocate(node, props.isResponse);
2924
4118
  };
2925
4119
  return /* @__PURE__ */ React.createElement("button", {
2926
4120
  type: "button",
2927
- className: cls,
4121
+ className: "lc-brief-row lc-brief-row-link",
2928
4122
  onClick: locate
2929
4123
  }, inner);
2930
4124
  }
@@ -2998,9 +4192,12 @@ window.__ModuleLoader__.load({
2998
4192
  function factTitle(tag, text) {
2999
4193
  return tag !== null ? text !== "" ? tag + " · " + text : tag : text;
3000
4194
  }
4195
+ /**
4196
+ * The brief's container ALWAYS renders — a fixed three-row lane (`.lc-brief`'s min-height) so scrubbing the chart never
4197
+ * changes the panel's height; unknown or empty steps just leave parts of the lane blank instead of collapsing it.
4198
+ */
3001
4199
  function BriefSection(props) {
3002
- const { opener, inputs, response } = props.brief;
3003
- if (opener === void 0 && inputs.length === 0 && response === void 0) return null;
4200
+ const { opener, inputs, response } = props.brief ?? { inputs: [] };
3004
4201
  const convOf = props.convOf ?? (() => void 0);
3005
4202
  const hint = props.onLocate !== void 0 ? " — " + t("brief.locate") : "";
3006
4203
  const locateChip = props.onLocate === void 0 ? void 0 : (n) => (e) => {
@@ -3032,13 +4229,13 @@ window.__ModuleLoader__.load({
3032
4229
  }, openerParts.tag !== null ? /* @__PURE__ */ React.createElement("span", { className: "lc-brief-fact" }, openerParts.tag) : null, openerParts.text !== "" ? /* @__PURE__ */ React.createElement("span", {
3033
4230
  className: "lc-brief-text",
3034
4231
  title: factTitle(openerParts.tag, openerParts.text) + hint
3035
- }, openerParts.text) : null) : null, inputs.length > 0 ? /* @__PURE__ */ React.createElement(BriefRow, {
4232
+ }, openerParts.text) : null) : null, /* @__PURE__ */ React.createElement(BriefRow, {
3036
4233
  tag: t("brief.input"),
3037
4234
  tagTip: t("brief.inputTip"),
3038
4235
  node: inputs[0],
3039
4236
  isResponse: false,
3040
4237
  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, {
4238
+ }, 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
4239
  tag: t("brief.reply"),
3043
4240
  tagTip: t("brief.replyTip"),
3044
4241
  node: response,
@@ -3046,7 +4243,7 @@ window.__ModuleLoader__.load({
3046
4243
  onLocate: props.onLocate
3047
4244
  }, nodeChip(response, void 0, true)) : null);
3048
4245
  }
3049
- return function RequestDetail(props) {
4246
+ return React.memo(function RequestDetail(props) {
3050
4247
  const req = props.request;
3051
4248
  if (!req) return null;
3052
4249
  const isTurn = req.stepCount !== void 0 && req.stepCount > 1;
@@ -3078,11 +4275,11 @@ window.__ModuleLoader__.load({
3078
4275
  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
4276
  className: "lc-detail-marker",
3080
4277
  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, {
4278
+ }, "✂ " + 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
4279
  brief: props.brief,
3083
4280
  convOf: props.convOf,
3084
4281
  onLocate: props.onLocate
3085
- }) : null, /* @__PURE__ */ React.createElement(StackedBar, {
4282
+ }), /* @__PURE__ */ React.createElement(StackedBar, {
3086
4283
  parts,
3087
4284
  height: 10
3088
4285
  }), /* @__PURE__ */ React.createElement("div", { className: "lc-detail-rows" }, CATS.map((c, i) => {
@@ -3105,7 +4302,7 @@ window.__ModuleLoader__.load({
3105
4302
  }
3106
4303
  })), 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
4304
  })));
3108
- };
4305
+ });
3109
4306
  }
3110
4307
  //#endregion
3111
4308
  //#region src/client/cost.ts
@@ -3435,25 +4632,8 @@ window.__ModuleLoader__.load({
3435
4632
  const scrollRef = React.useRef(null);
3436
4633
  const scrolledOnce = React.useRef(false);
3437
4634
  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
- };
4635
+ const lastSeqRef = React.useRef(0);
4636
+ const prevScrollWidthRef = React.useRef(0);
3457
4637
  /**
3458
4638
  * 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
4639
  * (narrower-than-label blocks stay put); reads (offsetWidth) batch before writes (transform) to avoid layout thrash — out-of-view
@@ -3488,6 +4668,9 @@ window.__ModuleLoader__.load({
3488
4668
  /* v8 ignore next 1 -- the scroll div renders unconditionally and React
3489
4669
  attaches refs before layout effects run; el is never null here. */
3490
4670
  if (el === null) return;
4671
+ const newestSeq = requests.length === 0 ? 0 : requests[requests.length - 1].seq;
4672
+ const grew = newestSeq !== lastSeqRef.current;
4673
+ const widthBeforeAppend = prevScrollWidthRef.current;
3491
4674
  if (props.granularity !== lastGranRef.current) {
3492
4675
  lastGranRef.current = props.granularity;
3493
4676
  scrolledOnce.current = false;
@@ -3499,14 +4682,19 @@ window.__ModuleLoader__.load({
3499
4682
  el.scrollLeft = Math.max(0, gi * 16 + BAR_W / 2 - el.clientWidth / 2);
3500
4683
  }
3501
4684
  props.onFocusTurnHandled();
3502
- }
3503
- if (!scrolledOnce.current) {
4685
+ } else if (!scrolledOnce.current) {
3504
4686
  scrolledOnce.current = true;
3505
4687
  el.scrollLeft = el.scrollWidth;
3506
- } else if (el.scrollLeft + el.clientWidth >= el.scrollWidth - 24) el.scrollLeft = el.scrollWidth;
3507
- updateEdges(el);
4688
+ } else if (grew && el.scrollLeft + el.clientWidth >= widthBeforeAppend - 24) el.scrollLeft = el.scrollWidth;
4689
+ lastSeqRef.current = newestSeq;
4690
+ prevScrollWidthRef.current = el.scrollWidth;
3508
4691
  updateTurnLabels(el);
3509
- });
4692
+ syncTip(el);
4693
+ }, [
4694
+ props.granularity,
4695
+ props.focusTurn,
4696
+ requests
4697
+ ]);
3510
4698
  const tipOf = (req) => {
3511
4699
  const head = req.stepCount !== void 0 && req.stepCount > 1 ? t("tip.turn", {
3512
4700
  t: req.turn ?? 0,
@@ -3527,17 +4715,43 @@ window.__ModuleLoader__.load({
3527
4715
  };
3528
4716
  const hoveredIdx = props.hoveredSeq !== null ? requests.findIndex((r) => r.seq === props.hoveredSeq) : -1;
3529
4717
  const hoveredReq = hoveredIdx >= 0 ? requests[hoveredIdx] : null;
4718
+ const tipColRef = React.useRef(0);
4719
+ /**
4720
+ * Glue the hover tip to its bar's VISIBLE slice. The tip deliberately does NOT live inside the scrolling
4721
+ * content: an absolutely-positioned child of a scroller contributes to its scrollable overflow, so a wide
4722
+ * reply preview on a right-edge bar used to inflate scrollWidth on every hover and flap the horizontal
4723
+ * scrollbar open/closed — jumping the whole card. Reads (offsetWidth/clientWidth) batch before the single
4724
+ * style write; unchanged transforms write nothing.
4725
+ */
4726
+ const syncTip = (el) => {
4727
+ /* v8 ignore next 1 -- the scroll div renders unconditionally while mounted, so its parent exists. */
4728
+ const tip = (el.parentElement ?? document.body).querySelector(".lc-chart-tip");
4729
+ if (tip === null) return;
4730
+ const lw = tip.offsetWidth;
4731
+ const cw = el.clientWidth;
4732
+ const half = Math.min(lw / 2, cw / 2);
4733
+ const cx = Math.min(Math.max(tipColRef.current - el.scrollLeft, half), cw - half);
4734
+ const next = `translate(${Math.round(cx - lw / 2)}px, 0)`;
4735
+ if (tip.style.transform !== next) tip.style.transform = next;
4736
+ };
4737
+ React.useLayoutEffect(() => {
4738
+ /* v8 ignore next 1 -- the scroll div renders unconditionally and React attaches refs before
4739
+ layout effects run; el is never null here. */
4740
+ if (scrollRef.current === null) return;
4741
+ tipColRef.current = hoveredIdx >= 0 ? hoveredIdx * 16 + BAR_W / 2 : 0;
4742
+ syncTip(scrollRef.current);
4743
+ });
3530
4744
  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
4745
  className: "lc-axis-mid",
3532
4746
  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", {
4747
+ }, "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
4748
  className: "lc-chart-scroll" + (props.activeTurn !== null ? " lc-chart-dim" : ""),
3535
4749
  ref: scrollRef,
3536
4750
  onScroll: (e) => {
3537
- updateEdges(e.currentTarget);
3538
4751
  updateTurnLabels(e.currentTarget);
4752
+ syncTip(e.currentTarget);
3539
4753
  }
3540
- }, edges.left ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-fade lc-chart-fade-l" }) : null, /* @__PURE__ */ React.createElement("div", {
4754
+ }, /* @__PURE__ */ React.createElement("div", {
3541
4755
  className: "lc-chart",
3542
4756
  onMouseLeave: () => {
3543
4757
  props.onHover(null);
@@ -3558,10 +4772,7 @@ window.__ModuleLoader__.load({
3558
4772
  deltaScale: delta ? deltaScale : void 0,
3559
4773
  onSelect: props.onSelect,
3560
4774
  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", {
4775
+ }))), /* @__PURE__ */ React.createElement("div", {
3565
4776
  className: "lc-turns",
3566
4777
  onMouseLeave: () => {
3567
4778
  props.onHoverTurn(null);
@@ -3583,7 +4794,7 @@ window.__ModuleLoader__.load({
3583
4794
  props.onPickTurn(grp.turn);
3584
4795
  }
3585
4796
  }, /* @__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);
4797
+ }))), hoveredReq !== null ? /* @__PURE__ */ React.createElement("div", { className: "lc-chart-tip" }, tipOf(hoveredReq)) : null));
3587
4798
  };
3588
4799
  }
3589
4800
  //#endregion
@@ -3607,6 +4818,7 @@ window.__ModuleLoader__.load({
3607
4818
  const StatsBoard = makeStatsBoard(kit);
3608
4819
  const PluginInfo = makePluginInfo(kit);
3609
4820
  const ContextBrowser = makeContextBrowser(kit, StackedBar);
4821
+ const AgentGraph = makeAgentGraph(ctx, kit);
3610
4822
  const ErrorBoundary = makeErrorBoundary(t);
3611
4823
  function ContextViewBody(props) {
3612
4824
  const sessionId = props.sessionId;
@@ -3676,16 +4888,6 @@ window.__ModuleLoader__.load({
3676
4888
  for (const n of convNodes ?? []) m.set(n.seq, n);
3677
4889
  return m;
3678
4890
  }, [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
4891
  let pinnedIdx = -1;
3690
4892
  for (let i = 0; i < displayRequests.length; i++) if (displayRequests[i].seq === selectedSeq) pinnedIdx = i;
3691
4893
  const pinnedReq = pinnedIdx >= 0 ? displayRequests[pinnedIdx] : null;
@@ -3699,32 +4901,34 @@ window.__ModuleLoader__.load({
3699
4901
  if (activeIdx < 0) activeIdx = pinnedIdx;
3700
4902
  if (activeIdx < 0 && displayRequests.length > 0) activeIdx = displayRequests.length - 1;
3701
4903
  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
4904
  let filesBefore = null;
3705
4905
  if (activeReq !== null) {
3706
4906
  const ri = requests.findIndex((r) => r.seq === activeReq.seq);
3707
4907
  filesBefore = ri + 1 < requests.length ? requests[ri + 1].seq : null;
3708
4908
  }
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) => {
3719
- const step = locateStepOf(requests, op.seq, op.gone);
4909
+ const brief = React.useMemo(() => activeReq !== null ? briefOf(briefList, displayRequests, activeIdx) : null, [
4910
+ activeReq,
4911
+ briefList,
4912
+ displayRequests,
4913
+ activeIdx
4914
+ ]);
4915
+ const convOf = React.useCallback((seq) => bySeq.get(seq), [bySeq]);
4916
+ const fileActivity = React.useMemo(() => activityOf(briefList, convOf, filesBefore), [
4917
+ briefList,
4918
+ convOf,
4919
+ filesBefore
4920
+ ]);
4921
+ const locateFileOp = React.useCallback((op) => {
4922
+ const seq = op.parent ?? op.seq;
4923
+ const step = locateStepOf(requests, seq, op.gone);
3720
4924
  if (step === null) return;
3721
4925
  setNodeFocus({
3722
4926
  step,
3723
- seq: op.seq,
4927
+ seq,
3724
4928
  cat: "tool"
3725
4929
  });
3726
- };
3727
- const locateNode = (node, isResponse) => {
4930
+ }, [requests]);
4931
+ const locateNode = React.useCallback((node, isResponse) => {
3728
4932
  /* v8 ignore next 1 -- locateNode is only wired to brief rows, and
3729
4933
  brief !== null guarantees activeReq !== null in the same closure. */
3730
4934
  if (activeReq === null) return;
@@ -3735,7 +4939,30 @@ window.__ModuleLoader__.load({
3735
4939
  seq: node.seq,
3736
4940
  cat: node.cat
3737
4941
  });
4942
+ }, [
4943
+ activeReq,
4944
+ activeIdx,
4945
+ displayRequests
4946
+ ]);
4947
+ if (!data) return /* @__PURE__ */ React.createElement("div", {
4948
+ className: "lc-root",
4949
+ ref: rootRef
4950
+ }, /* @__PURE__ */ React.createElement("div", { className: "lc-empty" }, t("loading")));
4951
+ const markerOf = (req) => {
4952
+ const i = displayRequests.indexOf(req);
4953
+ /* v8 ignore next 1 -- the only caller passes displayRequests[activeIdx],
4954
+ an element of the very array indexOf scans. */
4955
+ return i >= 0 ? markers[i] : void 0;
3738
4956
  };
4957
+ const head = headlineOf(data, pressure, breakdown);
4958
+ let fileScope = t("files.scopeLatest");
4959
+ if (activeReq !== null && filesBefore !== null) fileScope = activeReq.stepCount !== void 0 && activeReq.stepCount > 1 ? t("detail.turn", {
4960
+ t: activeReq.turn ?? 0,
4961
+ n: activeReq.stepCount
4962
+ }) : t("detail.step", {
4963
+ t: activeReq.turn ?? 0,
4964
+ s: activeReq.step ?? 0
4965
+ });
3739
4966
  let activeTurn = hoverTurn;
3740
4967
  if (activeTurn === null && hoveredSeq !== null) {
3741
4968
  for (const req of displayRequests) if (req.seq === hoveredSeq) {
@@ -3743,9 +4970,9 @@ window.__ModuleLoader__.load({
3743
4970
  break;
3744
4971
  }
3745
4972
  }
3746
- const head = headlineOf(data, pressure, breakdown);
3747
4973
  const localeSvc = ctx.get("locale");
3748
4974
  const activeLocale = localeSvc !== void 0 && typeof localeSvc.getLocale === "function" ? localeSvc.getLocale().active : "en";
4975
+ const subtitle = (data.model ?? "") + (data.provider ? " · " + data.provider : "");
3749
4976
  return /* @__PURE__ */ React.createElement("div", {
3750
4977
  className: "lc-root",
3751
4978
  ref: rootRef
@@ -3759,7 +4986,7 @@ window.__ModuleLoader__.load({
3759
4986
  locale: activeLocale
3760
4987
  }), /* @__PURE__ */ React.createElement(PluginInfo, null)), /* @__PURE__ */ React.createElement("div", { className: "lc-cols" }, /* @__PURE__ */ React.createElement("div", { className: "lc-col" }, /* @__PURE__ */ React.createElement(CurrentComposition, {
3761
4988
  head,
3762
- subtitle: (data.model ? data.model : "") + (data.provider ? " · " + data.provider : ""),
4989
+ subtitle,
3763
4990
  hoverKey: hoverCat,
3764
4991
  onHoverKey: setHoverCat
3765
4992
  }), /* @__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 +5065,14 @@ window.__ModuleLoader__.load({
3838
5065
  activity: fileActivity,
3839
5066
  scope: fileScope,
3840
5067
  onLocate: locateFileOp
3841
- })), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
5068
+ })), /* @__PURE__ */ React.createElement(AgentGraph, {
5069
+ sessionId: typeof sessionId === "string" ? sessionId : void 0,
5070
+ self: {
5071
+ head,
5072
+ billed: usage !== null ? numOf(usage.uncachedInputTokens) + numOf(usage.outputTokens) + numOf(usage.cacheReadTokens) + numOf(usage.cacheWriteTokens) : null,
5073
+ requests: requests.length
5074
+ }
5075
+ }), /* @__PURE__ */ React.createElement("div", { className: "lc-foot" }, t("footer")));
3842
5076
  }
3843
5077
  return function ContextView(props) {
3844
5078
  return h(ErrorBoundary, null, h(ContextViewBody, props));
@@ -3859,7 +5093,7 @@ window.__ModuleLoader__.load({
3859
5093
  }
3860
5094
  //#endregion
3861
5095
  //#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}";
5096
+ 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-hint{text-decoration:none}.lc-pi-hint:hover{text-decoration:underline}.lc-pi-update{color:var(--dsw-alias-state-warn-primary);white-space:nowrap;margin-left:6px;font-size:11px}.lc-pi-label{color:var(--dsw-alias-label-secondary);white-space:nowrap;text-overflow:ellipsis;flex:none;font-size:11px;font-weight:600;overflow:hidden}.lc-pi-value{min-width:0;color:var(--dsw-alias-label-primary);white-space:nowrap;text-overflow:ellipsis;text-align:right;font-size:13px;font-weight:600;overflow:hidden}.lc-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
5097
  const tagId = "dsh-context/styles.css";
3864
5098
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
3865
5099
  const tag = document.createElement("style");