cc-viewer 1.6.331 → 1.6.334

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 (85) hide show
  1. package/README.md +11 -2
  2. package/cli.js +6 -1
  3. package/dist/assets/App-CM76jJTq.css +1 -0
  4. package/dist/assets/App-DKxE-x5Q.js +2 -0
  5. package/dist/assets/{MdxEditorPanel-BjJTwpmV.js → MdxEditorPanel-B6n0VHxw.js} +1 -1
  6. package/dist/assets/{Mobile-BsJPIcqy.js → Mobile-DaFxkSMs.js} +1 -1
  7. package/dist/assets/{_baseUniq-jDC_lFvH.js → _baseUniq-DRoq9q8B.js} +1 -1
  8. package/dist/assets/{arc-D_MHcfPz.js → arc-Cvi1SHNs.js} +1 -1
  9. package/dist/assets/{architectureDiagram-Q4EWVU46-D3LhuQ4p.js → architectureDiagram-Q4EWVU46-BlqmPh3g.js} +1 -1
  10. package/dist/assets/{blockDiagram-DXYQGD6D-Bi9G2rJp.js → blockDiagram-DXYQGD6D-Dyk7JSWO.js} +1 -1
  11. package/dist/assets/{c4Diagram-AHTNJAMY-DlY0w3X4.js → c4Diagram-AHTNJAMY-C00cDbc4.js} +1 -1
  12. package/dist/assets/{channel-C87hLHco.js → channel-CdXT5cZK.js} +1 -1
  13. package/dist/assets/{chunk-4BX2VUAB-DodXg0hV.js → chunk-4BX2VUAB-CSv6_viy.js} +1 -1
  14. package/dist/assets/{chunk-4TB4RGXK-Ba2E_IX7.js → chunk-4TB4RGXK-B7dfdEMX.js} +1 -1
  15. package/dist/assets/{chunk-55IACEB6-CWG3ZdRs.js → chunk-55IACEB6-CzIsElZc.js} +1 -1
  16. package/dist/assets/{chunk-EDXVE4YY-lT5M-dRo.js → chunk-EDXVE4YY-C_16c0SJ.js} +1 -1
  17. package/dist/assets/{chunk-FMBD7UC4-BY_2pfoq.js → chunk-FMBD7UC4-BEsH6vuG.js} +1 -1
  18. package/dist/assets/{chunk-OYMX7WX6-DzLhEX7n.js → chunk-OYMX7WX6-ByJtK0pa.js} +1 -1
  19. package/dist/assets/{chunk-QZHKN3VN-BvdoGurF.js → chunk-QZHKN3VN-B2C0Ey4D.js} +1 -1
  20. package/dist/assets/{chunk-YZCP3GAM-C7rlhtyD.js → chunk-YZCP3GAM-DYeerN90.js} +1 -1
  21. package/dist/assets/classDiagram-6PBFFD2Q-0orWWSeB.js +1 -0
  22. package/dist/assets/classDiagram-v2-HSJHXN6E-0orWWSeB.js +1 -0
  23. package/dist/assets/clone-CqkWyivT.js +1 -0
  24. package/dist/assets/{cose-bilkent-S5V4N54A-DhsYplMy.js → cose-bilkent-S5V4N54A-nf2MgVIM.js} +1 -1
  25. package/dist/assets/{dagre-KV5264BT-PLnp9yct.js → dagre-KV5264BT-QuXoeQ8Q.js} +1 -1
  26. package/dist/assets/{diagram-5BDNPKRD-DqvSM_bo.js → diagram-5BDNPKRD-D7dXnuBC.js} +1 -1
  27. package/dist/assets/{diagram-G4DWMVQ6-DYN80unA.js → diagram-G4DWMVQ6-CJkDxGbe.js} +1 -1
  28. package/dist/assets/{diagram-MMDJMWI5-D-jv3M8U.js → diagram-MMDJMWI5-B0y-_dq3.js} +1 -1
  29. package/dist/assets/{diagram-TYMM5635-BbVwY3fb.js → diagram-TYMM5635-D-sAYexO.js} +1 -1
  30. package/dist/assets/{erDiagram-SMLLAGMA-9-rp8xsO.js → erDiagram-SMLLAGMA-45kKhs_p.js} +1 -1
  31. package/dist/assets/{flowDiagram-DWJPFMVM-Bu_JuIe2.js → flowDiagram-DWJPFMVM-cHZi71BI.js} +1 -1
  32. package/dist/assets/{ganttDiagram-T4ZO3ILL-B-PvSqDf.js → ganttDiagram-T4ZO3ILL-AOMWmRX8.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-UUTBAWPF-DGloG4Vg.js → gitGraphDiagram-UUTBAWPF-BbA6UT1O.js} +1 -1
  34. package/dist/assets/{graph-CbR0Gq9P.js → graph-C2vUhsyb.js} +1 -1
  35. package/dist/assets/{index-CtigeZCJ.js → index-BZPd8BxU.js} +1 -1
  36. package/dist/assets/{index-jdQotxUf.js → index-Bg-5xzbn.js} +1 -1
  37. package/dist/assets/{index-D8UlD-Rs.js → index-Ca1pfEu9.js} +1 -1
  38. package/dist/assets/{index-70lDhcDm.js → index-DVyCY4lq.js} +1 -1
  39. package/dist/assets/index-DrsJoCe8.js +2 -0
  40. package/dist/assets/{index-7ibWcMau.js → index-JyReQHe7.js} +1 -1
  41. package/dist/assets/{index-Dyqfqy0-.js → index-NJXIDr7-.js} +1 -1
  42. package/dist/assets/{index-CtLTny8H.js → index-NnF6whsx.js} +1 -1
  43. package/dist/assets/{infoDiagram-42DDH7IO-FGosRpTG.js → infoDiagram-42DDH7IO-JZCrFgrr.js} +1 -1
  44. package/dist/assets/{ishikawaDiagram-UXIWVN3A-BlclfpV5.js → ishikawaDiagram-UXIWVN3A-CnQoRWsu.js} +1 -1
  45. package/dist/assets/{journeyDiagram-VCZTEJTY--UkR986E.js → journeyDiagram-VCZTEJTY-CHPiVGlE.js} +1 -1
  46. package/dist/assets/{jszip.min-PwTGeWmJ.js → jszip.min-d8dJpMtW.js} +1 -1
  47. package/dist/assets/{kanban-definition-6JOO6SKY-1oO9JAku.js → kanban-definition-6JOO6SKY--vaCznms.js} +1 -1
  48. package/dist/assets/{layout-DKdPIoav.js → layout-C6UBgcNI.js} +1 -1
  49. package/dist/assets/{linear-D2uKszhd.js → linear-g0XsIl_6.js} +1 -1
  50. package/dist/assets/{mermaid.core-Adp4RVnV.js → mermaid.core-BAPdKagI.js} +2 -2
  51. package/dist/assets/{min-Czdk6h6Y.js → min-GUAWxzVx.js} +1 -1
  52. package/dist/assets/{mindmap-definition-QFDTVHPH-Bs7NLrcq.js → mindmap-definition-QFDTVHPH-D3NJObYN.js} +1 -1
  53. package/dist/assets/{pieDiagram-DEJITSTG-D7z6f5QH.js → pieDiagram-DEJITSTG-B2qdCrvu.js} +1 -1
  54. package/dist/assets/{quadrantDiagram-34T5L4WZ-2rae23Td.js → quadrantDiagram-34T5L4WZ-y5VgO9qJ.js} +1 -1
  55. package/dist/assets/{requirementDiagram-MS252O5E-B4uC_pLr.js → requirementDiagram-MS252O5E-M73JyLZ4.js} +1 -1
  56. package/dist/assets/{sankeyDiagram-XADWPNL6-xo0rKzRA.js → sankeyDiagram-XADWPNL6-r4eNItTO.js} +1 -1
  57. package/dist/assets/seqResourceLoaders-CnYQz2LG.js +2 -0
  58. package/dist/assets/{sequenceDiagram-FGHM5R23-3zQ_CXGR.js → sequenceDiagram-FGHM5R23-CyaSnCUK.js} +1 -1
  59. package/dist/assets/{stateDiagram-FHFEXIEX-BnAvwAN8.js → stateDiagram-FHFEXIEX-I57EKIQ6.js} +1 -1
  60. package/dist/assets/{stateDiagram-v2-QKLJ7IA2-Q4raD3B2.js → stateDiagram-v2-QKLJ7IA2-CzouMKFP.js} +1 -1
  61. package/dist/assets/{timeline-definition-GMOUNBTQ-BMSMoqEA.js → timeline-definition-GMOUNBTQ-BsZ7rRxl.js} +1 -1
  62. package/dist/assets/{vendor-antd-TJ9xmLQO.js → vendor-antd-Be06DOUh.js} +2 -2
  63. package/dist/assets/{vendor-codemirror-CIGft5iF.js → vendor-codemirror-C9Tqo5sR.js} +1 -1
  64. package/dist/assets/{vendor-mdxeditor-BuIUwnOf.js → vendor-mdxeditor-C927Ebjk.js} +2 -2
  65. package/dist/assets/{vendor-qrcode-BOhJZrOT.js → vendor-qrcode-d7HbbJ8T.js} +1 -1
  66. package/dist/assets/{vendor-virtuoso-CyrVCdOW.js → vendor-virtuoso-TtNQfqSa.js} +1 -1
  67. package/dist/assets/{vennDiagram-DHZGUBPP-Dd_tCka1.js → vennDiagram-DHZGUBPP-exSnurzw.js} +1 -1
  68. package/dist/assets/{wardley-RL74JXVD-DGkrRuSv.js → wardley-RL74JXVD-C3w7f85J.js} +1 -1
  69. package/dist/assets/{wardleyDiagram-NUSXRM2D-DlbDpNJR.js → wardleyDiagram-NUSXRM2D-BcHQWT7G.js} +1 -1
  70. package/dist/assets/{xychartDiagram-5P7HB3ND-CtBG1yl-.js → xychartDiagram-5P7HB3ND-CjUkYNTb.js} +1 -1
  71. package/dist/index.html +4 -4
  72. package/findcc.js +52 -5
  73. package/package.json +5 -3
  74. package/server/lib/model-system-prompts.js +253 -0
  75. package/server/lib/system-prompt-files.js +32 -5
  76. package/server/pty-manager.js +30 -4
  77. package/server/routes/expert.js +84 -0
  78. package/server/server.js +3 -2
  79. package/dist/assets/App-Bla1Fr_X.css +0 -1
  80. package/dist/assets/App-CCQARuiH.js +0 -2
  81. package/dist/assets/classDiagram-6PBFFD2Q-DiOul2Ap.js +0 -1
  82. package/dist/assets/classDiagram-v2-HSJHXN6E-DiOul2Ap.js +0 -1
  83. package/dist/assets/clone-r-iPz1mj.js +0 -1
  84. package/dist/assets/index-ReSZCrNR.js +0 -2
  85. package/dist/assets/seqResourceLoaders-K-REW6io.js +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-viewer",
3
- "version": "1.6.331",
3
+ "version": "1.6.334",
4
4
  "description": "Claude Code logging, visualization, and management toolkit — launch a web viewer alongside Claude Code with full request/response tracing, proxy, and mobile support",
5
5
  "license": "MIT",
6
6
  "main": "server.js",
@@ -21,11 +21,13 @@
21
21
  "start": "node server.js",
22
22
  "lint:control-bytes": "node scripts/check-no-control-bytes.js",
23
23
  "pretest": "npm run lint:control-bytes",
24
+ "pretest:cli": "npm run lint:control-bytes",
24
25
  "pretest:coverage": "npm run lint:control-bytes",
25
26
  "pretest:coverage:html": "npm run lint:control-bytes",
26
27
  "test": "CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 node --test --test-force-exit --test-timeout=120000",
27
- "test:coverage": "CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 node --test --test-force-exit --test-timeout=120000 --experimental-test-coverage --test-coverage-include='server/**/*.js' --test-coverage-include='src/utils/**/*.js' --test-coverage-include='*.js'",
28
- "test:coverage:html": "CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 c8 --reporter=text-summary --reporter=html node --test --test-force-exit --test-timeout=120000",
28
+ "test:cli": "CCV_TEST_CLI=1 CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 node --test --test-force-exit --test-timeout=120000",
29
+ "test:coverage": "CCV_TEST_CLI=1 CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 node --test --test-force-exit --test-timeout=120000 --experimental-test-coverage --test-coverage-include='server/**/*.js' --test-coverage-include='src/utils/**/*.js' --test-coverage-include='*.js'",
30
+ "test:coverage:html": "CCV_TEST_CLI=1 CCV_INSTANCE_ID= CCV_LOG_DIR=tmp CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 c8 --reporter=text-summary --reporter=html node --test --test-force-exit --test-timeout=120000",
29
31
  "prepublishOnly": "npm run build",
30
32
  "electron:dev": "electron electron/main.js",
31
33
  "electron:build": "npm run build && electron-builder",
@@ -0,0 +1,253 @@
1
+ import { readdirSync, readFileSync, writeFileSync, rmSync, mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { isNonEmptyFile } from './system-prompt-files.js';
4
+
5
+ // 「按模型定制 system prompt」的文件夹与文件名语法。
6
+ // 全局目录 <LOG_DIR>/system_prompt/ 与工作区目录 <workspace>/system_prompt/ 各放一套;
7
+ // 启动时用「上次启动所用模型 id」做不区分大小写的子串匹配(条目名 "OPUS" 命中
8
+ // "claude-opus-4-8[1m]"),命中的文件整体取代工作区默认的 CC_SYSTEM.md / CC_APPEND_SYSTEM.md。
9
+ //
10
+ // Model-specific system prompt files. Two scopes share the same folder name
11
+ // (MODEL_PROMPT_DIR): global <LOG_DIR>/system_prompt/ and per-workspace
12
+ // <workspace>/system_prompt/. At spawn the model id from the last launch is
13
+ // matched case-insensitively as a substring against entry names; a match fully
14
+ // replaces the workspace Default sentinels for that launch.
15
+ //
16
+ // Known limitations (v1, by design):
17
+ // - readClaudeProjectModel filters /haiku/i, so an entry named "haiku" never
18
+ // matches at spawn (haiku is the auxiliary model; see context-watcher.js).
19
+ // - Opening ~/.claude/cc-viewer itself as the workspace makes both scopes the
20
+ // same directory; harmless — the workspace scope short-circuits first.
21
+ export const MODEL_PROMPT_DIR = 'system_prompt';
22
+
23
+ // 条目名语法:首字符字母数字,其余允许 . _ -,总长 ≤64。禁止 `/` `\` 与前导点 → 无路径穿越。
24
+ // Entry-name grammar: first char alphanumeric, then [A-Za-z0-9._-], max 64 chars.
25
+ export const MODEL_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
26
+
27
+ // 后缀编码该条目的模式(对齐 CC_SYSTEM.md / CC_APPEND_SYSTEM.md 命名惯例):
28
+ // <NAME>_SYSTEM.md = 覆盖(override),<NAME>_APPEND_SYSTEM.md = 追加(append)。
29
+ // The suffix encodes the entry's mode, mirroring the CC_* sentinel convention.
30
+ const OVERRIDE_SUFFIX = '_SYSTEM.md';
31
+ const APPEND_SUFFIX = '_APPEND_SYSTEM.md';
32
+
33
+ /**
34
+ * 校验并规范化模型条目名:合法则返回全大写规范名,否则 null。
35
+ * 统一大写落盘可根除大小写敏感/不敏感文件系统上的 "Opus" vs "OPUS" 碰撞,无需索引文件。
36
+ * 拒绝以 `_APPEND` 结尾的名字(否则 `X_APPEND` 的 override 文件 `X_APPEND_SYSTEM.md`
37
+ * 会被最长后缀优先的解析读成 `{X, append}`);拒绝保留名 `DEFAULT`(UI 首个页签)。
38
+ * Validate + canonicalize an entry name to UPPERCASE, or return null.
39
+ *
40
+ * @param {string} name
41
+ * @returns {string|null}
42
+ */
43
+ export function normalizeModelName(name) {
44
+ if (typeof name !== 'string' || !MODEL_NAME_RE.test(name)) return null;
45
+ const upper = name.toUpperCase();
46
+ if (/_APPEND$/.test(upper)) return null;
47
+ if (upper === 'DEFAULT') return null;
48
+ return upper;
49
+ }
50
+
51
+ /**
52
+ * 由条目名 + 模式得到文件名;名字非法时 throw(调用方应先各自校验)。
53
+ * Build the on-disk filename for an entry; throws on invalid name.
54
+ *
55
+ * @param {string} name
56
+ * @param {'override'|'append'} mode 非 'override' 一律按 'append' 处理(对齐 writeWorkspaceSystemText)
57
+ * @returns {string} e.g. 'GEMINI3_SYSTEM.md'
58
+ */
59
+ export function modelPromptFileName(name, mode) {
60
+ const canonical = normalizeModelName(name);
61
+ if (!canonical) throw new Error('invalid model prompt name');
62
+ return canonical + (mode === 'override' ? OVERRIDE_SUFFIX : APPEND_SUFFIX);
63
+ }
64
+
65
+ /**
66
+ * 解析文件名 → { name, mode } 或 null(非本语法文件)。
67
+ * 最长后缀优先:先试 `_APPEND_SYSTEM.md` 再试 `_SYSTEM.md`;剥后缀得到的 stem 必须
68
+ * 自身通过 normalizeModelName(空 stem、含非法字符、`_APPEND` 结尾一律拒绝)——
69
+ * 在解析器内部再校验一次,read/match 路径就不可能被列表跳过之外的异类文件喂进来。
70
+ * Parse a filename into { name, mode } (longest suffix first) or null.
71
+ *
72
+ * @param {string} fileName
73
+ * @returns {{ name: string, mode: 'override'|'append' } | null}
74
+ */
75
+ export function parseModelPromptFileName(fileName) {
76
+ if (typeof fileName !== 'string') return null;
77
+ let stem = null;
78
+ let mode = null;
79
+ if (fileName.endsWith(APPEND_SUFFIX)) {
80
+ stem = fileName.slice(0, -APPEND_SUFFIX.length);
81
+ mode = 'append';
82
+ } else if (fileName.endsWith(OVERRIDE_SUFFIX)) {
83
+ stem = fileName.slice(0, -OVERRIDE_SUFFIX.length);
84
+ mode = 'override';
85
+ } else {
86
+ return null;
87
+ }
88
+ const name = normalizeModelName(stem);
89
+ if (!name) return null;
90
+ return { name, mode };
91
+ }
92
+
93
+ /**
94
+ * 列出目录里的模型条目。仅统计「存在、普通文件、非空」且文件名符合语法的文件;
95
+ * 目录缺失/不可读 → []。同名去重:override 压过 append(对齐 readWorkspaceSystemText
96
+ * 的优先级);同名同模式(大小写敏感文件系统上手工造出的重复)取字典序最小的文件名,保证确定性。
97
+ * List model prompt entries in a directory.
98
+ *
99
+ * @param {string} dir
100
+ * @returns {Array<{ name: string, mode: 'override'|'append', fileName: string }>} 按 name 排序
101
+ */
102
+ export function listModelPrompts(dir) {
103
+ if (!dir) return [];
104
+ let files;
105
+ try {
106
+ files = readdirSync(dir);
107
+ } catch {
108
+ return [];
109
+ }
110
+ const byName = new Map();
111
+ for (const fileName of files.slice().sort()) {
112
+ const parsed = parseModelPromptFileName(fileName);
113
+ if (!parsed) continue;
114
+ if (!isNonEmptyFile(join(dir, fileName))) continue;
115
+ const prev = byName.get(parsed.name);
116
+ if (!prev) {
117
+ byName.set(parsed.name, { name: parsed.name, mode: parsed.mode, fileName });
118
+ continue;
119
+ }
120
+ // override 压过 append;同模式保留字典序最小(files 已排序,先到先得)。
121
+ if (prev.mode === 'append' && parsed.mode === 'override') {
122
+ byName.set(parsed.name, { name: parsed.name, mode: parsed.mode, fileName });
123
+ }
124
+ }
125
+ return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
126
+ }
127
+
128
+ /**
129
+ * 读取某条目的文本(经 listModelPrompts 的去重/优先级规则挑出生效文件)。
130
+ * Read one entry's effective text, or null when absent/unreadable.
131
+ *
132
+ * @param {string} dir
133
+ * @param {string} name
134
+ * @returns {{ name: string, mode: 'override'|'append', text: string } | null}
135
+ */
136
+ export function readModelPrompt(dir, name) {
137
+ const canonical = normalizeModelName(name);
138
+ if (!canonical) return null;
139
+ const entry = listModelPrompts(dir).find((e) => e.name === canonical);
140
+ if (!entry) return null;
141
+ try {
142
+ return { name: entry.name, mode: entry.mode, text: readFileSync(join(dir, entry.fileName), 'utf-8') };
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ // 删除目录里「解析后同名」的全部文件——覆盖两种模式与大小写变体(大小写敏感文件系统上
149
+ // 手工创建的 Opus_SYSTEM.md 之类)。返回是否真的删掉了东西。
150
+ function removeAllVariants(dir, canonical) {
151
+ let files;
152
+ try {
153
+ files = readdirSync(dir);
154
+ } catch {
155
+ return false;
156
+ }
157
+ let deleted = false;
158
+ for (const fileName of files) {
159
+ const parsed = parseModelPromptFileName(fileName);
160
+ if (parsed && parsed.name === canonical) {
161
+ try {
162
+ rmSync(join(dir, fileName), { force: true });
163
+ deleted = true;
164
+ } catch { /* ignore */ }
165
+ }
166
+ }
167
+ return deleted;
168
+ }
169
+
170
+ /**
171
+ * 写入某条目:
172
+ * - text 去空白后为空 → 删掉该名字全部变体(= 删除条目),{ cleared:true }
173
+ * - 否则 mkdir -p 目录;先删反向模式/大小写变体、再写目标文件(缩小「两份并存」中间态窗口,
174
+ * 与 writeWorkspaceSystemText 的先删后写同理)。
175
+ * Upsert an entry; empty text deletes it.
176
+ *
177
+ * @param {string} dir
178
+ * @param {string} name
179
+ * @param {'override'|'append'} mode
180
+ * @param {string} text
181
+ * @returns {{ name: string, mode: 'override'|'append', written: boolean, cleared: boolean }}
182
+ */
183
+ export function writeModelPrompt(dir, name, mode, text) {
184
+ if (!dir) throw new Error('no target directory');
185
+ const canonical = normalizeModelName(name);
186
+ if (!canonical) throw new Error('invalid model prompt name');
187
+ const normMode = mode === 'override' ? 'override' : 'append';
188
+ const raw = typeof text === 'string' ? text : '';
189
+
190
+ if (raw.trim().length === 0) {
191
+ removeAllVariants(dir, canonical);
192
+ return { name: canonical, mode: normMode, written: false, cleared: true };
193
+ }
194
+ mkdirSync(dir, { recursive: true });
195
+ const target = modelPromptFileName(canonical, normMode);
196
+ // 先删同名其它变体(含反向模式与大小写变体),再写目标文件。
197
+ let files;
198
+ try {
199
+ files = readdirSync(dir);
200
+ } catch {
201
+ files = [];
202
+ }
203
+ for (const fileName of files) {
204
+ if (fileName === target) continue;
205
+ const parsed = parseModelPromptFileName(fileName);
206
+ if (parsed && parsed.name === canonical) {
207
+ try { rmSync(join(dir, fileName), { force: true }); } catch { /* ignore */ }
208
+ }
209
+ }
210
+ writeFileSync(join(dir, target), raw, 'utf-8');
211
+ return { name: canonical, mode: normMode, written: true, cleared: false };
212
+ }
213
+
214
+ /**
215
+ * 删除某条目(全部模式/大小写变体)。
216
+ * Delete an entry entirely.
217
+ *
218
+ * @param {string} dir
219
+ * @param {string} name
220
+ * @returns {{ deleted: boolean }}
221
+ */
222
+ export function deleteModelPrompt(dir, name) {
223
+ const canonical = normalizeModelName(name);
224
+ if (!canonical) return { deleted: false };
225
+ return { deleted: removeAllVariants(dir, canonical) };
226
+ }
227
+
228
+ /**
229
+ * 在候选目录序列中为 modelId 匹配模型条目。
230
+ * 匹配 = 双方都转小写后做子串包含("OPUS" 命中 "claude-opus-4-8[1m]")。
231
+ * candidates 按「工作区在前」传入:工作区一旦有命中即短路,全局哪怕名字更长也不再看
232
+ * (用户决策:工作区覆盖全局;名字长度只在同一 scope 内当消歧规则)。
233
+ * 同 scope 多条命中:名字最长者胜("OPUS-4" 压过 "OPUS"),等长按字典序,确定性兜底。
234
+ * Match modelId against candidate dirs (workspace-first short-circuit).
235
+ *
236
+ * @param {string|null} modelId e.g. 'claude-opus-4-8[1m]'
237
+ * @param {Array<{ dir: string, scope: 'workspace'|'global' }>} candidates
238
+ * @returns {{ path: string, fileName: string, name: string, mode: 'override'|'append', scope: 'workspace'|'global' } | null}
239
+ */
240
+ export function matchModelPrompt(modelId, candidates) {
241
+ if (!modelId || typeof modelId !== 'string') return null;
242
+ if (!Array.isArray(candidates)) return null;
243
+ const id = modelId.toLowerCase();
244
+ for (const cand of candidates) {
245
+ if (!cand || !cand.dir) continue;
246
+ const hits = listModelPrompts(cand.dir).filter((e) => id.includes(e.name.toLowerCase()));
247
+ if (!hits.length) continue;
248
+ hits.sort((a, b) => b.name.length - a.name.length || a.name.localeCompare(b.name));
249
+ const e = hits[0];
250
+ return { path: join(cand.dir, e.fileName), fileName: e.fileName, name: e.name, mode: e.mode, scope: cand.scope };
251
+ }
252
+ return null;
253
+ }
@@ -1,5 +1,6 @@
1
1
  import { statSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { MODEL_PROMPT_DIR, matchModelPrompt } from './model-system-prompts.js';
3
4
 
4
5
  // 启动目录里检查的「全大写 sentinel」文件名:多词约定用下划线连接
5
6
  // (对齐 CLAUDE.md / GitHub CODE_OF_CONDUCT.md 等惯例)。
@@ -13,7 +14,7 @@ export const DISABLE_AUTO_SYSTEM_PROMPT_ENV = 'CCV_DISABLE_AUTO_SYSTEM_PROMPT';
13
14
 
14
15
  // 文件需存在、是普通文件且非空(size>0)才算数:空文件会把 system prompt 抹空,跳过更安全。
15
16
  // 坏符号链接 / 不存在 / 目录 → statSync 抛错或 isFile()=false → 返回 false。
16
- function isNonEmptyFile(p) {
17
+ export function isNonEmptyFile(p) {
17
18
  try {
18
19
  const st = statSync(p);
19
20
  return st.isFile() && st.size > 0;
@@ -38,19 +39,45 @@ function hasArg(args, ...names) {
38
39
  * 两者独立生效;用户已手动传同义 flag 时跳过对应自动项(手动优先);
39
40
  * 空文件跳过;CCV_DISABLE_AUTO_SYSTEM_PROMPT=1 整体关闭。
40
41
  *
42
+ * 模型定制(opts.modelId 提供时):先在 <projectDir>/system_prompt/(工作区)与
43
+ * opts.globalModelDir(全局)里做模糊匹配;命中的条目「整体取代」上面两份默认 sentinel
44
+ * ——即便手动 flag 抑制了注入也不再回看默认文件(条目已取而代之)。未命中/无 modelId
45
+ * 则完全走旧逻辑。
46
+ *
41
47
  * Decide whether to inject system-prompt file flags based on sentinel files in
42
- * the launch directory. Pure function: side effects limited to reading fs/env.
48
+ * the launch directory. When opts.modelId is given, model-specific entries in
49
+ * the workspace/global system_prompt folders are matched first; a match fully
50
+ * supersedes the Default sentinels. Pure function: reads fs/env only.
43
51
  *
44
52
  * @param {string} projectDir 启动目录(绝对路径)
45
53
  * @param {string[]} [existingArgs] 已有的 claude 参数(用于「手动优先」判断)
46
54
  * @param {Object} [env] 环境变量(默认 process.env)
47
- * @returns {{ args: string[], loaded: string[] }} args: 待追加参数;loaded: 实际加载的文件名(用于终端提示)
55
+ * @param {{ modelId?: string|null, globalModelDir?: string|null }} [opts]
56
+ * @returns {{ args: string[], loaded: string[], model: string|null }}
57
+ * args: 待追加参数;loaded: 实际加载的文件(终端提示);model: 命中的条目名(未命中为 null)
48
58
  */
49
- export function buildSystemPromptFileArgs(projectDir, existingArgs = [], env = process.env) {
50
- const out = { args: [], loaded: [] };
59
+ export function buildSystemPromptFileArgs(projectDir, existingArgs = [], env = process.env, opts = {}) {
60
+ const out = { args: [], loaded: [], model: null };
51
61
  if (!projectDir) return out;
52
62
  if (env?.[DISABLE_AUTO_SYSTEM_PROMPT_ENV] === '1') return out;
53
63
 
64
+ if (opts?.modelId) {
65
+ const candidates = [{ dir: join(projectDir, MODEL_PROMPT_DIR), scope: 'workspace' }];
66
+ if (opts.globalModelDir) candidates.push({ dir: opts.globalModelDir, scope: 'global' });
67
+ const match = matchModelPrompt(opts.modelId, candidates);
68
+ if (match) {
69
+ const flagPair = match.mode === 'override'
70
+ ? ['--system-prompt', '--system-prompt-file']
71
+ : ['--append-system-prompt', '--append-system-prompt-file'];
72
+ if (!hasArg(existingArgs, ...flagPair)) {
73
+ out.args.push(flagPair[1], match.path);
74
+ out.loaded.push(`${match.scope === 'global' ? 'global ' : ''}${MODEL_PROMPT_DIR}/${match.fileName}`);
75
+ out.model = match.name;
76
+ }
77
+ return out; // 命中即返回:默认 sentinel 不再参与(含手动 flag 抑制注入的情况)。
78
+ }
79
+ }
80
+
54
81
  // 整段替换:CC_SYSTEM.md → --system-prompt-file (用户已传 --system-prompt[-file] 则跳过)
55
82
  if (!hasArg(existingArgs, '--system-prompt', '--system-prompt-file')) {
56
83
  const p = join(projectDir, SYSTEM_PROMPT_FILE);
@@ -1,6 +1,6 @@
1
1
  import { resolveNativePath, LOG_DIR } from '../findcc.js';
2
2
  import { fileURLToPath } from 'node:url';
3
- import { join, dirname } from 'node:path';
3
+ import { join, dirname, sep } from 'node:path';
4
4
  import { chmodSync, statSync } from 'node:fs';
5
5
  import { platform, arch, homedir } from 'node:os';
6
6
  import { createRequire } from 'node:module';
@@ -8,6 +8,8 @@ import { prepareEmbeddedShellSpawn, stripClaudeNoFlickerUnlessOptedIn, applyClau
8
8
  import { killPtyTree } from './lib/term-signals.js';
9
9
  import { findSafeSliceStart, splitTrailingIncomplete } from './lib/ansi-safe-slice.js';
10
10
  import { buildSystemPromptFileArgs } from './lib/system-prompt-files.js';
11
+ import { MODEL_PROMPT_DIR } from './lib/model-system-prompts.js';
12
+ import { readClaudeProjectModel } from './lib/context-watcher.js';
11
13
 
12
14
  const __filename = fileURLToPath(import.meta.url);
13
15
  const __dirname = dirname(__filename);
@@ -48,6 +50,20 @@ export function _setPtyImportForTests(fn) {
48
50
  _ptyImportForTests = fn;
49
51
  }
50
52
 
53
+ // spawn 时读取「上次启动所用模型 id」供模型定制 system prompt 匹配。
54
+ // readClaudeProjectModel 读的是真实 ~/.claude.json(无 NODE_TEST_CONTEXT 屏障),
55
+ // 而多数 pty 单测以仓库根为 cwd —— 默认读取器在测试上下文里必须失活,
56
+ // 否则开发机的真实模型记录会漏进单测(机器状态依赖)。测试用 _setSpawnModelReaderForTests 显式注入。
57
+ // env/reader 参数化只为可测性:单测能直接断言「测试上下文 → null / 生产 → 透传」,
58
+ // 而无需真的读 ~/.claude.json(见 test/pty-manager.test.js 的 guard 单测)。
59
+ export function _defaultSpawnModelReader(c, env = process.env, reader = readClaudeProjectModel) {
60
+ return env.NODE_TEST_CONTEXT ? null : reader(c);
61
+ }
62
+ let _spawnModelReader = _defaultSpawnModelReader;
63
+ export function _setSpawnModelReaderForTests(fn) {
64
+ _spawnModelReader = fn || _defaultSpawnModelReader;
65
+ }
66
+
51
67
  async function getPty() {
52
68
  if (typeof _ptyImportForTests === 'function') {
53
69
  return _ptyImportForTests();
@@ -277,9 +293,18 @@ async function _spawnClaudeImpl(proxyPort, cwd, extraArgs = [], claudePath = nul
277
293
 
278
294
  // 启动目录存在 CC_SYSTEM.md / CC_APPEND_SYSTEM.md(非空)时,自动追加
279
295
  // --system-prompt-file / --append-system-prompt-file(两者独立、用户已传同义 flag 时跳过对应项)。
296
+ // 模型定制:以「上次启动所用模型」在 <cwd>/system_prompt/ 与 <LOG_DIR>/system_prompt/
297
+ // 里模糊匹配,命中的条目整体取代上面两份默认 sentinel。
280
298
  // 注:currentWorkspacePath 在下方才赋值,这里用 cwd 参数判定启动目录。
281
- let sysPrompt = buildSystemPromptFileArgs(cwd || process.cwd(), finalExtraArgs);
282
- if (_systemPromptFileRejectedPaths.has(claudePath)) sysPrompt = { args: [], loaded: [] };
299
+ // LOG_DIR 内的 spawn(IM worker 工作目录 = <LOG_DIR>/IM_<id>/)跳过模型匹配:
300
+ // IM 人格依赖默认 sentinel CC_APPEND_SYSTEM.md 注入,全局模型条目不得静默取代它。
301
+ const spawnDir = cwd || process.cwd();
302
+ const insideLogDir = spawnDir === LOG_DIR || spawnDir.startsWith(LOG_DIR + sep);
303
+ let sysPrompt = buildSystemPromptFileArgs(spawnDir, finalExtraArgs, process.env, {
304
+ modelId: insideLogDir ? null : _spawnModelReader(spawnDir),
305
+ globalModelDir: join(LOG_DIR, MODEL_PROMPT_DIR),
306
+ });
307
+ if (_systemPromptFileRejectedPaths.has(claudePath)) sysPrompt = { args: [], loaded: [], model: null };
283
308
  const launchArgs = sysPrompt.args.length ? [...finalExtraArgs, ...sysPrompt.args] : finalExtraArgs;
284
309
 
285
310
  let command = claudePath;
@@ -309,7 +334,8 @@ async function _spawnClaudeImpl(proxyPort, cwd, extraArgs = [], claudePath = nul
309
334
 
310
335
  // 注入了 system prompt 文件时向终端打印一行提示(可见性/安全);内部重启已抑制以免重复。
311
336
  if (sysPrompt.loaded.length && !_suppressNextSpawnNotice) {
312
- emitSpawnNotice(`[CC Viewer] loaded ${sysPrompt.loaded.join(', ')} as system prompt`);
337
+ const modelSuffix = sysPrompt.model ? ` (model match: ${sysPrompt.model})` : '';
338
+ emitSpawnNotice(`[CC Viewer] loaded ${sysPrompt.loaded.join(', ')} as system prompt${modelSuffix}`);
313
339
  }
314
340
  _suppressNextSpawnNotice = false;
315
341
 
@@ -2,8 +2,17 @@
2
2
  // (CC_SYSTEM.md / CC_APPEND_SYSTEM.md),对应偏好设置 → 专家设置 → 系统文本修改。
3
3
  // 这两个文件由 pty-manager._spawnClaudeImpl 在启动 claude 时自动注入为
4
4
  // --system-prompt-file / --append-system-prompt-file(见 server/lib/system-prompt-files.js)。
5
+ // 另有「按模型定制」条目(/api/expert/model-prompts):工作区 <ws>/system_prompt/ 与
6
+ // 全局 <LOG_DIR>/system_prompt/ 两套目录,见 server/lib/model-system-prompts.js。
5
7
  // 鉴权沿用 dispatch 之前的全局鉴权(与 files-fs 写操作一致,不额外 gate isLocal)。
8
+ import { join } from 'node:path';
9
+ import { readFileSync } from 'node:fs';
6
10
  import { readWorkspaceSystemText, writeWorkspaceSystemText } from '../lib/system-prompt-files.js';
11
+ import {
12
+ MODEL_PROMPT_DIR, normalizeModelName, listModelPrompts,
13
+ writeModelPrompt, deleteModelPrompt,
14
+ } from '../lib/model-system-prompts.js';
15
+ import { LOG_DIR } from '../../findcc.js';
7
16
 
8
17
  // 当前工作区目录全部由服务端解析(绝不接收客户端路径 → 无遍历面):
9
18
  // 运行中/最近一次 claude 的 cwd > CCV_PROJECT_DIR > (仅非工作区模式才回退 process.cwd())
@@ -66,7 +75,82 @@ function postSystemText(req, res, parsedUrl, isLocal, deps) {
66
75
  });
67
76
  }
68
77
 
78
+ // 列出某目录全部模型条目并内联文本(条目数很小,一次返回免去 N 次跟进请求)。
79
+ // 直接读 listModelPrompts 给出的生效文件,避免 readModelPrompt 每条目重扫目录(N+1)。
80
+ function collectModelEntries(dir) {
81
+ if (!dir) return [];
82
+ return listModelPrompts(dir)
83
+ .map((e) => {
84
+ try {
85
+ return { name: e.name, mode: e.mode, text: readFileSync(join(dir, e.fileName), 'utf-8') };
86
+ } catch {
87
+ return null; // 列表后被并发删除等:跳过该条目
88
+ }
89
+ })
90
+ .filter(Boolean);
91
+ }
92
+
93
+ async function getModelPrompts(req, res, parsedUrl, isLocal, deps) {
94
+ try {
95
+ const dir = await resolveDir(deps);
96
+ const globalDir = join(LOG_DIR, MODEL_PROMPT_DIR);
97
+ sendJson(res, 200, {
98
+ workspaceDir: dir || null,
99
+ workspaceActive: !!dir,
100
+ globalDir,
101
+ workspace: dir ? collectModelEntries(join(dir, MODEL_PROMPT_DIR)) : [],
102
+ global: collectModelEntries(globalDir),
103
+ });
104
+ } catch (e) {
105
+ console.error('[CC Viewer] expert model-prompts GET failed:', e.message);
106
+ sendJson(res, 500, { error: 'read_failed' });
107
+ }
108
+ }
109
+
110
+ function postModelPrompts(req, res, parsedUrl, isLocal, deps) {
111
+ let body = '';
112
+ let truncated = false;
113
+ req.on('data', (chunk) => {
114
+ body += chunk;
115
+ if (body.length > deps.MAX_POST_BODY) { truncated = true; req.destroy(); }
116
+ });
117
+ req.on('end', async () => {
118
+ if (truncated) return; // 超限已 destroy,socket 关闭,勿再解析/回包(对齐 postSystemText)
119
+ try {
120
+ const { scope, name, mode, text } = JSON.parse(body || '{}');
121
+ if (scope !== 'workspace' && scope !== 'global') {
122
+ sendJson(res, 400, { error: 'bad_scope' });
123
+ return;
124
+ }
125
+ const canonical = normalizeModelName(name);
126
+ if (!canonical) { sendJson(res, 400, { error: 'bad_model_name' }); return; }
127
+ let targetDir;
128
+ if (scope === 'global') {
129
+ targetDir = join(LOG_DIR, MODEL_PROMPT_DIR);
130
+ } else {
131
+ const dir = await resolveDir(deps);
132
+ if (!dir) { sendJson(res, 400, { error: 'no_active_workspace' }); return; }
133
+ targetDir = join(dir, MODEL_PROMPT_DIR);
134
+ }
135
+ const raw = typeof text === 'string' ? text : '';
136
+ if (raw.trim().length === 0) {
137
+ // 空文本 = 删除条目(对齐 system-text 的「存空即禁用」约定;此时 mode 可缺省)。
138
+ deleteModelPrompt(targetDir, canonical);
139
+ sendJson(res, 200, { ok: true, name: canonical, scope, cleared: true });
140
+ return;
141
+ }
142
+ const result = writeModelPrompt(targetDir, canonical, mode === 'override' ? 'override' : 'append', raw);
143
+ sendJson(res, 200, { ok: true, scope, ...result });
144
+ } catch (e) {
145
+ console.error('[CC Viewer] expert model-prompts POST failed:', e.message);
146
+ sendJson(res, 500, { error: 'write_failed' });
147
+ }
148
+ });
149
+ }
150
+
69
151
  export const expertRoutes = [
70
152
  { method: 'GET', match: 'exact', path: '/api/expert/system-text', handler: getSystemText },
71
153
  { method: 'POST', match: 'exact', path: '/api/expert/system-text', handler: postSystemText },
154
+ { method: 'GET', match: 'exact', path: '/api/expert/model-prompts', handler: getModelPrompts },
155
+ { method: 'POST', match: 'exact', path: '/api/expert/model-prompts', handler: postModelPrompts },
72
156
  ];
package/server/server.js CHANGED
@@ -74,7 +74,7 @@ function execWithStdin(cmd, args, input, options) {
74
74
  }
75
75
  import { LOG_FILE, _initPromise, _resumeState, _projectName, _logDir, streamingState, resetStreamingState, PROFILE_PATH, setLivePort, getImLiveText, resetImLiveText } from './interceptor.js';
76
76
  import { recordInstance, listInstances } from './lib/instance-registry.js';
77
- import { LOG_DIR, setLogDir, getClaudeConfigDir } from '../findcc.js';
77
+ import { LOG_DIR, setLogDir, getClaudeConfigDir, isBrowserOpenSuppressed } from '../findcc.js';
78
78
  import { t, getLang, setLang } from './i18n.js';
79
79
  import { loadAuthConfig, loadAuthState, saveAuthConfig, clearProjectOverride, generatePassword, decideAuth, parseCookies, renderLoginPage, localeFromAcceptLanguage } from './lib/auth.js';
80
80
  import { checkAndUpdate } from './lib/updater.js';
@@ -946,11 +946,12 @@ export async function startViewer() {
946
946
  }
947
947
  }
948
948
  // v2.0.69 之前的版本会清空控制台,自动打开浏览器确保用户能看到界面
949
+ // L7 sandbox: suppressed under tests / CCV_NO_OPEN=1 (see findcc.js).
949
950
  try {
950
951
  const ccPkgPath = join(NODE_MODULES, '@anthropic-ai', 'claude-code', 'package.json');
951
952
  const ccVer = JSON.parse(readFileSync(ccPkgPath, 'utf-8')).version;
952
953
  const [maj, min, pat] = ccVer.split('.').map(Number);
953
- if (maj < 2 || (maj === 2 && min === 0 && pat < 69)) {
954
+ if ((maj < 2 || (maj === 2 && min === 0 && pat < 69)) && !isBrowserOpenSuppressed()) {
954
955
  const cmd = platform() === 'darwin' ? 'open' : platform() === 'win32' ? 'start' : 'xdg-open';
955
956
  execAsync(`${cmd} ${url}`, { timeout: 5000 }).catch(() => {});
956
957
  }
@@ -1 +0,0 @@
1
- ._liveTag_8g4c3_3{position:relative;display:inline-flex;align-items:center;justify-content:flex-start;border-radius:999px;border:1px solid;border-color:var(--ctx-color);color:var(--ctx-color);padding:0 10px;height:100%;font-size:12px;line-height:1;overflow:hidden;transition:border-color .3s,color .3s;white-space:nowrap;background:var(--bg-base-pure)}._liveTagFill_8g4c3_23{position:absolute;left:0;top:0;bottom:0;width:var(--ctx-percent, 0);background-color:var(--ctx-color);background-image:repeating-linear-gradient(135deg,rgba(255,255,255,.25) 0,rgba(255,255,255,.25) 2px,transparent 2px,transparent 7px);opacity:.35;transition:width .5s ease,background-color .3s;pointer-events:none}._liveTagContent_8g4c3_44{position:relative;z-index:1;display:inline-flex;align-items:center}._liveTagHistory_8g4c3_52{background:var(--bg-surface);border-color:var(--border-light);color:var(--text-primary)}._liveTagText_8g4c3_59{margin-left:4px;font-variant-numeric:tabular-nums}._cachePopoverPlaceholder_8g4c3_65{min-width:300px}._modeRow_4zm2s_1{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}._modeLeft_4zm2s_9{display:flex;align-items:center;gap:10px}._modeRight_4zm2s_15{display:flex;align-items:center;gap:8px}._overrideWarn_4zm2s_22{font-size:12px;color:var(--color-warning, #d4810a)}._previewLabel_4zm2s_27{font-size:13px;color:var(--text-secondary, #888)}._previewBox_4zm2s_33{border:1px solid var(--border-secondary, #d9d9d9);border-radius:8px;padding:8px 14px;min-height:320px;max-height:60vh;overflow:auto}._previewEmpty_4zm2s_42{font-size:13px;color:var(--text-secondary, #888)}._hint_4zm2s_47{margin-top:10px;font-size:12px;color:var(--ccv-text-secondary, #888);line-height:1.6}._dirLine_4zm2s_54{word-break:break-all}._warn_4zm2s_58{margin-top:10px;font-size:12px;color:var(--ccv-warning, #d4810a)}._editButton_31hdb_10{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;background:transparent;border:none;padding:0 2px;margin-left:4px;cursor:pointer;font-size:inherit;line-height:1;color:var(--text-secondary, #888);opacity:0;border-radius:3px;transition:opacity .12s ease,color .12s ease,background-color .12s ease}._editButton_31hdb_10 .anticon{display:inline-flex;align-items:center;line-height:1;position:relative;top:-1px}._editButton_31hdb_10:hover,._editButton_31hdb_10:focus-visible{opacity:1;color:var(--text-primary, #333);background-color:var(--bg-hover, rgba(0, 0, 0, .04));outline:none}._editButton_31hdb_10:focus-visible{box-shadow:0 0 0 2px var(--focus-ring, rgba(64, 158, 255, .4))}._footer_31hdb_55{display:flex;justify-content:space-between;align-items:center;gap:8px}._footerLeft_31hdb_61,._footerRight_31hdb_65{display:flex;gap:8px}._projectNameRow_31hdb_70{display:flex;align-items:center;gap:8px;margin-bottom:12px;padding:6px 10px;background:var(--bg-secondary, rgba(0, 0, 0, .03));border-radius:4px;font-size:12px}._projectNameLabel_31hdb_80{color:var(--text-secondary, #888)}._projectNameValue_31hdb_83{color:var(--text-primary, #333);font-family:var(--font-mono, ui-monospace, SFMono-Regular, monospace);word-break:break-all}._panel_1nxl3_1{display:flex;flex-direction:column;gap:14px}._required_1nxl3_7{margin-left:2px;color:var(--color-error-light, #ff7b7b)}._optional_1nxl3_12{margin-left:6px;font-size:12px;font-weight:400;color:var(--text-secondary)}._row_1nxl3_19{display:flex;align-items:center;justify-content:space-between;gap:12px}._label_1nxl3_26{font-size:14px;font-weight:500}._control_1nxl3_31{display:inline-flex;align-items:center;gap:10px}._field_1nxl3_37{display:flex;flex-direction:column;gap:6px}._fieldLabel_1nxl3_43{font-size:13px;color:var(--text-secondary)}._help_1nxl3_48{font-size:12px;color:var(--text-secondary);line-height:1.4}._warn_1nxl3_54{font-size:12px;line-height:1.5;color:var(--color-error-light, #ff7b7b)}._hint_1nxl3_60{font-size:12px;line-height:1.5;color:var(--text-secondary)}._helpIcon_1nxl3_67{margin-left:6px;color:var(--text-tertiary, #999);cursor:help;font-size:13px}._detailsToggle_1nxl3_74{display:inline-flex;align-items:center;gap:5px;align-self:flex-start;padding:0;border:none;background:none;cursor:pointer;font-size:12px;color:var(--text-secondary)}._detailsToggle_1nxl3_74:hover{color:var(--text-primary, var(--text-secondary))}._details_1nxl3_74{display:flex;flex-direction:column;gap:10px}._actions_1nxl3_98{display:flex;justify-content:flex-end;gap:10px;margin-top:16px;padding-top:16px;border-top:1px solid var(--border-secondary)}._testBtn_1nxl3_108{margin-right:auto}._tabRow_1q32a_4{display:flex;align-items:flex-end;gap:6px;padding:0 8px;flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}._tabRow_1q32a_4::-webkit-scrollbar{display:none}._tabBtn_1q32a_24{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;font-size:14px;line-height:1.5;border:1px solid transparent;border-radius:10px;background:transparent;color:var(--text-secondary);cursor:pointer;transition:color .18s,border-color .18s,background .18s;white-space:nowrap;flex-shrink:0;-webkit-user-select:none;user-select:none}._tabBtn_1q32a_24:hover{color:var(--color-primary)}._tabBtn_1q32a_24._tabBtnActive_1q32a_52,._tabBtn_1q32a_24._tabBtnActive_1q32a_52:hover{background:var(--bg-container);color:var(--color-primary);border-color:var(--border-primary);border-bottom:2px solid var(--bg-container);border-radius:10px 10px 0 0;font-weight:500;margin-bottom:-1px;position:relative;z-index:2}._toolBody_1q32a_8{border:1px solid var(--border-primary);border-top:none;border-radius:8px;background:var(--bg-container);padding:18px 20px;min-width:0}[data-theme=light] ._toolBody_1q32a_8{box-shadow:0 3px 8px #00000014}[data-theme=light] ._tabBtnActive_1q32a_52{box-shadow:0 -3px 8px #0000000f}[data-theme=dark] ._toolBody_1q32a_8 .ant-input,[data-theme=dark] ._toolBody_1q32a_8 .ant-input-affix-wrapper,[data-theme=dark] ._toolBody_1q32a_8 .ant-select-selector{background-color:var(--bg-elevated)}._scrollBody_riojj_1{height:100%;box-sizing:border-box;overflow-y:auto;overflow-x:hidden;scrollbar-gutter:stable;padding:4px 2px;background:var(--bg-container)}._headerBar_riojj_15{display:flex;align-items:center;gap:8px}._refreshBtn_riojj_21{flex:0 0 auto}._statusTag_riojj_27{display:inline-flex;align-items:center;margin-left:auto}._statusTag_riojj_27 .ant-tag{margin-inline-end:0}._center_riojj_36{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:40px 0;color:var(--text-secondary)}._hint_riojj_46{color:var(--text-secondary);font-size:13px}._chip_bvaji_4{position:relative;line-height:0;cursor:pointer;-webkit-user-select:none;user-select:none;transition:opacity .15s}._chip_bvaji_4:hover{opacity:.75}._logo_bvaji_16{display:block;transition:color .15s}._connecting_bvaji_22{opacity:.5}._dotError_bvaji_27{position:absolute;right:-2px;top:-2px;width:6px;height:6px;border-radius:50%;background:var(--color-error, #ff4d4f);pointer-events:none}._modelCard_8xfmi_3{border:1px solid var(--border-secondary);border-radius:6px;padding:8px 10px;background:var(--bg-container)}._modelName_8xfmi_9{font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border-secondary)}._statsTable_8xfmi_17{width:100%;border-collapse:collapse}._th_8xfmi_21{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:var(--text-tertiary);font-weight:400;text-align:right}._td_8xfmi_30{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:var(--text-primary);text-align:right}._label_8xfmi_38{padding:2px 12px;font-size:12px;font-family:monospace;white-space:nowrap;color:var(--text-light);font-weight:400;text-align:left}._rowBorder_8xfmi_47{border-bottom:1px solid var(--border-primary)}._rebuildTotalRow_8xfmi_50{border-top:1px solid var(--border-light)}._rebuildTotalRow_8xfmi_50 td{font-weight:600}._cachePopoverEmpty_8xfmi_56{padding:8px 4px;color:var(--text-tertiary);font-size:13px}._toolChipGrid_8xfmi_61{display:flex;flex-wrap:wrap;gap:4px;padding:2px 0 6px 2px}._cacheToolChip_8xfmi_67{font-size:11px;padding:0 6px;border-radius:3px;background:var(--bg-surface);color:var(--text-secondary);line-height:18px;max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;border:1px solid var(--border-primary);cursor:help}._titleIcon_8xfmi_81{margin-right:8px}._detailMarkdownCard_8xfmi_86{border:1px solid var(--border-secondary);border-radius:6px;padding:8px 10px;background:var(--bg-container)}._memoryMarkdown_8xfmi_93{font-size:12.5px;line-height:1.55;color:var(--text-primary);word-break:break-word}._memoryMarkdown_8xfmi_93 p{margin:0 0 6px}._memoryMarkdown_8xfmi_93 ul,._memoryMarkdown_8xfmi_93 ol{margin:4px 0 6px;padding-left:20px}._memoryMarkdown_8xfmi_93 li{margin:2px 0}._memoryMarkdown_8xfmi_93 h1,._memoryMarkdown_8xfmi_93 h2,._memoryMarkdown_8xfmi_93 h3,._memoryMarkdown_8xfmi_93 h4{font-size:13px;font-weight:600;margin:8px 0 4px;color:var(--text-primary)}._memoryMarkdown_8xfmi_93 h1{font-size:14px}._memoryMarkdown_8xfmi_93 a{color:var(--primary-color, #1677ff);text-decoration:none;cursor:pointer}._memoryMarkdown_8xfmi_93 a:hover{text-decoration:underline}._memoryMarkdown_8xfmi_93 code{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;padding:1px 4px;border-radius:3px;background:var(--bg-surface);color:var(--text-primary)}._memoryMarkdown_8xfmi_93 pre{margin:6px 0;padding:8px 10px;border-radius:4px;background:var(--bg-surface);overflow-x:auto}._memoryMarkdown_8xfmi_93 pre code{padding:0;background:transparent;font-size:12px}._memoryMarkdown_8xfmi_93 blockquote{margin:4px 0;padding:2px 8px;border-left:3px solid var(--border-hover);color:var(--text-secondary)}._memoryMarkdown_8xfmi_93 hr{margin:8px 0;border:none;border-top:1px solid var(--border-secondary)}._quickMenuGroup_8xfmi_164{position:relative}._quickMenuRow_8xfmi_168{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:var(--text-primary);font-size:13px;padding:6px 12px;text-align:left;cursor:pointer;border-radius:4px;white-space:nowrap}._quickMenuGroup_8xfmi_164:hover ._quickMenuRow_8xfmi_168,._quickMenuGroupOpen_8xfmi_185 ._quickMenuRow_8xfmi_168{background:var(--border-secondary)}._quickMenuRowIcon_8xfmi_189{display:flex;align-items:center;color:var(--text-tertiary)}._quickMenuRowIcon_8xfmi_189 svg{width:14px;height:14px}._quickMenuLabel_8xfmi_200{flex:1}._quickMenuValue_8xfmi_205{color:var(--text-tertiary);font-size:12px}._quickMenuCaret_8xfmi_210{color:var(--text-tertiary);font-size:10px}._quickMenuSubWrap_8xfmi_218{display:none;position:absolute;left:100%;bottom:-5px;padding-left:6px;z-index:1}._quickMenuGroupOpen_8xfmi_185 ._quickMenuSubWrap_8xfmi_218{display:block}._quickMenuSub_8xfmi_218{display:flex;flex-direction:column;background:var(--bg-elevated);border:1px solid var(--border-hover);border-radius:8px;padding:4px;min-width:76px;max-height:60vh;overflow-y:auto;box-shadow:0 6px 16px #0000002e}._quickMenuOption_8xfmi_248{background:none;border:none;color:var(--text-primary);font-size:13px;padding:5px 12px;text-align:left;cursor:pointer;border-radius:4px;white-space:nowrap}._quickMenuOption_8xfmi_248:hover{background:var(--border-secondary)}._quickMenuOption_8xfmi_248._quickMenuOptionActive_8xfmi_264{color:var(--color-primary);font-weight:500}._quickMenuSubTipBox_8xfmi_270{padding:8px 12px;width:300px;white-space:normal}._headerBar_uzc7r_2{display:flex;align-items:center;justify-content:space-between;width:100%;height:100%}._logoWrap_uzc7r_10{display:inline-flex;align-items:center;position:relative;margin-top:14px}._logoWrapActive_uzc7r_17:after{content:"";position:absolute;top:-10px;bottom:-10px;left:0;right:-200px}._logoImage_uzc7r_26{height:24px;width:24px;border-radius:3px;vertical-align:middle;opacity:.75;transition:opacity .2s;cursor:pointer}._logoImageActive_uzc7r_36{opacity:1}._compactBtn_uzc7r_43{font-size:12px;height:30px;display:inline-flex;align-items:center;justify-content:center}._compactBtnNoBorder_uzc7r_52{width:30px;height:30px;min-width:30px;padding:0;border:none;font-size:18px;line-height:1;display:inline-flex;align-items:center;justify-content:center}._compactBtnNoBorder_uzc7r_52 .anticon{display:inline-flex;align-items:center;justify-content:center;line-height:0}._headerProjectName_uzc7r_79{font-size:12px;color:inherit;white-space:nowrap}._headerProjectName_uzc7r_79:hover [data-alias-edit-trigger],._headerProjectName_uzc7r_79:focus-within [data-alias-edit-trigger]{opacity:.55}._countdownStrong_uzc7r_96{font-variant-numeric:tabular-nums}._qrcodePopover_uzc7r_101{display:flex;flex-direction:column;align-items:center;padding:8px}._qrcodeSection_uzc7r_108{display:flex;flex-direction:column;align-items:center;padding:16px;margin-bottom:12px;border:1px solid var(--border-secondary);border-radius:8px;background:var(--bg-container)}._qrcodeTitle_uzc7r_119{font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:12px}._qrcodeUrlInput_uzc7r_126{margin-top:12px;font-size:12px;font-family:Menlo,Monaco,monospace}._qrcodeUrlCopy_uzc7r_132{cursor:pointer;color:var(--text-tertiary);transition:color .2s}._qrcodeUrlCopy_uzc7r_132:hover{color:var(--color-primary-light)}._authSection_uzc7r_143{display:flex;flex-direction:column;align-items:stretch;box-sizing:border-box;width:100%;margin-top:12px;padding:12px;border:1px solid var(--border-secondary);border-radius:8px;background:var(--bg-container);gap:8px}._authHeaderRow_uzc7r_157{display:flex;align-items:center;justify-content:space-between}._authTitle_uzc7r_163{font-size:13px;font-weight:600;color:var(--text-primary)}._authPasswordLabel_uzc7r_169{font-size:12px;color:var(--text-secondary)}._authPasswordInput_uzc7r_174{font-size:12px;font-family:Menlo,Monaco,monospace}._authSaveBtn_uzc7r_179{align-self:flex-end}._authEmptyWarn_uzc7r_183{font-size:12px;color:var(--color-error-light);line-height:1.4}._settingsGroupBox_uzc7r_191{border:1px solid var(--border-secondary);border-radius:8px;background:var(--bg-container);padding:4px 16px;margin-bottom:12px}._settingsGroupTitle_uzc7r_199{font-size:14px;font-weight:600;color:var(--text-primary);padding:12px 0 4px;border-bottom:1px solid var(--border-secondary)}._settingsItem_uzc7r_208{display:flex;justify-content:space-between;align-items:center;padding:12px 0}._settingsLabel_uzc7r_215{font-size:14px}._settingsHelpIcon_uzc7r_220{font-size:16px;color:var(--text-disabled);cursor:help;margin-left:4px}._settingsDivider_uzc7r_227{border-top:1px solid var(--border-primary);margin:12px 0}._logDirInput_uzc7r_232{margin-top:8px;background:var(--bg-base-alt);border-color:var(--border-light);color:var(--text-primary);font-family:monospace;font-size:13px}._tokenStatsEmpty_uzc7r_242{padding:8px 4px;color:var(--text-tertiary);font-size:13px}._tokenStatsContainer_uzc7r_249{display:flex;gap:12px;align-items:flex-start}._tokenStatsColumn_uzc7r_255{min-width:240px}._toolStatsColumn_uzc7r_259{min-width:180px}._modelCardSpaced_uzc7r_265{margin-bottom:10px}._rebuildCard_uzc7r_274{border:1px solid var(--border-secondary);border-radius:6px;padding:8px 10px;margin-top:10px;background:var(--bg-container)}._promptExportBar_uzc7r_283{margin-bottom:12px}._promptScrollArea_uzc7r_287{max-height:500px;overflow:auto}._promptEmpty_uzc7r_292{color:var(--text-tertiary);padding:12px}._promptTimestamp_uzc7r_298{color:var(--text-muted);font-size:12px;margin:12px 0 4px;padding-bottom:6px}._textPromptCard_uzc7r_306{margin:4px 0;background:var(--bg-container);border-radius:6px;border:1px solid var(--border-secondary);padding:10px 14px}._preText_uzc7r_315{white-space:pre-wrap;word-break:break-word;font-size:13px;line-height:1.6;color:var(--text-primary);margin:4px 0}._systemCollapse_uzc7r_325{margin:4px 0;background:var(--bg-elevated);border:1px solid var(--border-secondary);border-radius:6px}._systemLabel_uzc7r_332{color:var(--text-tertiary);font-size:12px}._preSys_uzc7r_337{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:var(--text-tertiary);margin:0}._promptTextarea_uzc7r_347{box-sizing:border-box;background:var(--bg-base-pure);width:100%;min-height:400px;color:var(--text-primary);font-family:monospace;font-size:13px;line-height:1.6;border:none;resize:vertical;padding:10px 14px;outline:none}._projectStatsCenter_uzc7r_363{display:flex;justify-content:center;padding:40px 0}._projectStatsEmpty_uzc7r_369{color:var(--text-tertiary);padding:40px 0;text-align:center;font-size:13px}._projectStatsContent_uzc7r_376{display:flex;flex-direction:column;gap:16px}._projectStatsUpdated_uzc7r_382{color:var(--text-muted);font-size:12px;text-align:right}._projectStatsSummary_uzc7r_388{display:grid;grid-template-columns:1fr 1fr;gap:10px}._projectStatCard_uzc7r_394{background:var(--bg-container);border:1px solid var(--border-secondary);border-radius:8px;padding:14px 12px;text-align:center}._projectStatValue_uzc7r_402{font-size:22px;font-weight:700;color:var(--text-primary);font-family:monospace;font-variant-numeric:tabular-nums}._projectStatLabel_uzc7r_410{font-size:12px;color:var(--text-tertiary);margin-top:4px}._projectStatsSection_uzc7r_416{display:flex;flex-direction:column;gap:10px}._projectStatsSectionTitle_uzc7r_422{font-size:14px;font-weight:600;color:var(--text-secondary);padding-bottom:4px;border-bottom:1px solid var(--border-secondary)}._projectStatsModelCard_uzc7r_430{background:var(--bg-container);border:1px solid var(--border-secondary);border-radius:6px;padding:10px 12px}._projectStatsModelHeader_uzc7r_437{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border-secondary)}._projectStatsModelName_uzc7r_446{font-size:13px;font-weight:600;color:var(--text-primary)}._projectStatsModelCount_uzc7r_452{font-size:12px;color:var(--text-tertiary);font-family:monospace}._cacheCopyBtn_uzc7r_460{font-size:14px;color:var(--text-tertiary);cursor:pointer;transition:color .2s;margin-left:8px}._cacheCopyBtn_uzc7r_460:hover{color:var(--text-primary)}._cacheTokenInfo_uzc7r_472{display:flex;align-items:center;font-size:12px;font-family:monospace;color:var(--text-light);margin-bottom:8px}._cacheCodeBlock_uzc7r_485{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:var(--text-secondary);background:var(--bg-container);border:1px solid var(--border-primary);border-radius:4px;padding:8px;margin:4px 0;font-family:Menlo,Monaco,monospace}._cacheCodeBlockSystem_uzc7r_499{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:var(--text-secondary);background:var(--bg-code-system);border:1px solid var(--border-code-system);border-radius:4px;padding:8px;margin:4px 0;font-family:Menlo,Monaco,monospace}._cacheNavBtn_uzc7r_513{margin-left:auto;font-size:11px;color:var(--color-primary);cursor:pointer;border:1px solid var(--color-primary);border-radius:3px;padding:1px 6px;white-space:nowrap}._cacheNavBtn_uzc7r_513:hover{background:var(--color-primary-bg-light)}._cacheNavList_uzc7r_528{width:600px;max-height:300px;overflow-y:auto}._cacheNavItem_uzc7r_534{padding:4px 8px;font-size:12px;color:var(--text-secondary);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-radius:3px}._cacheNavItem_uzc7r_534:hover{background:var(--color-primary-bg-lighter);color:var(--text-white)}._cacheBlockHighlight_uzc7r_550{box-shadow:0 0 10px var(--color-primary-shadow);transition:box-shadow .2s ease-in}._cacheBlockHighlightFading_uzc7r_555{box-shadow:0 0 10px transparent;transition:box-shadow 3s ease-out}._cacheBorderSvg_uzc7r_560{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible}._cacheBorderSvgFading_uzc7r_569{opacity:0;transition:opacity 3s ease-out}._cacheBorderRect_uzc7r_574{animation:_cacheDashRotate_uzc7r_1 4s linear infinite}@keyframes _cacheDashRotate_uzc7r_1{0%{stroke-dashoffset:0}to{stroke-dashoffset:-100}}._thLeft_uzc7r_584{text-align:left}._cacheWriteToken_uzc7r_599{color:var(--color-code-orange)}._cacheReadToken_uzc7r_603{color:var(--color-success)}._cacheCtxPercent_uzc7r_607{color:var(--text-tertiary);margin-left:6px}._qrcodeIcon_uzc7r_627{width:30px;height:30px;padding:6px;box-sizing:border-box;color:var(--text-secondary);cursor:pointer;border-radius:6px;transition:color .15s ease,background-color .15s ease;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;background:none;border:none}._qrcodeIcon_uzc7r_627:hover{color:var(--text-primary);background:var(--bg-hover, rgba(0, 0, 0, .04))}._qrcodeIcon_uzc7r_627:focus-visible{outline:2px solid var(--primary-color, #1677ff);outline-offset:1px}._approvalBell_uzc7r_653{position:relative;width:30px;height:30px;padding:6px;box-sizing:border-box;color:var(--color-warning, #faad14);cursor:pointer;border-radius:6px;transition:color .15s ease,background-color .15s ease;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;background:none;border:none}._approvalBell_uzc7r_653:hover{color:var(--text-primary);background:var(--bg-hover, rgba(0, 0, 0, .04))}._approvalBell_uzc7r_653:focus-visible{outline:2px solid var(--primary-color, #1677ff);outline-offset:1px}._approvalBellBadge_uzc7r_678{position:absolute;top:0;right:0;min-width:14px;height:14px;padding:0 3px;box-sizing:border-box;background:var(--color-error, #ff4d4f);color:#fff;font-size:9px;line-height:14px;border-radius:7px;text-align:center;font-weight:600}._proxySwapIcon_uzc7r_695{margin-right:4px;font-size:11px}._proxyProfileTag_uzc7r_701{border-radius:12px;background:var(--border-secondary);border:1px solid var(--border-light);color:var(--text-tertiary);font-size:12px;cursor:pointer;transition:color .2s,border-color .2s}._proxyProfileTag_uzc7r_701:hover{color:var(--text-secondary);border-color:var(--text-disabled)}._pinnedShortcut_uzc7r_717{height:24px;margin-top:11px;display:inline-flex;align-items:center;justify-content:center;font-size:16px;line-height:1;color:var(--text-tertiary);opacity:.75;cursor:pointer;transition:opacity .2s,color .2s}._pinnedShortcut_uzc7r_717:hover{opacity:1;color:var(--text-secondary)}._pinnedShortcut_uzc7r_717 .anticon{display:inline-flex;align-items:center;justify-content:center;line-height:0}._themeToggle_uzc7r_746{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;width:30px;height:30px;padding:0;border:none;border-radius:6px;background:transparent;color:var(--text-secondary);cursor:pointer;transition:background-color .2s ease,color .2s ease;flex-shrink:0;outline:none}._themeToggle_uzc7r_746:hover{background:var(--color-bg-hover, rgba(127, 127, 127, .12));color:var(--text-primary)}._themeToggle_uzc7r_746:focus-visible{box-shadow:0 0 0 2px var(--primary-color, #1677ff)}._themeToggleIcon_uzc7r_772{display:block}._headerRightRow_uzc7r_777 .ant-space-item{display:inline-flex;align-items:center}._headerCountdownTag_uzc7r_783{height:30px;margin:0;padding:0 10px;display:inline-flex;align-items:center;background:var(--bg-surface);border:1px solid var(--border-hover);border-radius:6px;line-height:1}._centerEmpty_1ivpu_1{display:flex;align-items:center;justify-content:center;height:100%}._scrollContainer_1ivpu_8{overflow:auto;height:100%;-webkit-overflow-scrolling:touch;will-change:scroll-position}._listItem_1ivpu_15{cursor:pointer;padding:8px 12px;border-left:6px solid transparent;border-right:1px solid var(--border-primary);border-top:1px solid transparent;border-bottom:1px solid var(--border-primary);transition:background .15s}._listItem_1ivpu_15:hover{border-left-color:var(--border-hover)}._listItemActive_1ivpu_29{background:var(--color-primary-bg-faint);border-left-color:var(--color-primary-light);border-right:1px solid var(--color-primary-light);border-top:1px solid var(--color-primary-light);border-bottom:1px solid var(--color-primary-light)}._listItemActive_1ivpu_29,._listItemActive_1ivpu_29:hover{background:var(--color-primary-bg-faint);border-left-color:var(--color-primary-light);border-right-color:var(--color-primary-light);border-top-color:var(--color-primary-light);border-bottom-color:var(--color-primary-light)}._itemContent_1ivpu_46{width:100%;min-width:0}._itemHeader_1ivpu_51{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:12px}._tagNoMargin_1ivpu_59{margin:0;font-size:12px}._modelName_1ivpu_64{font-size:12px;color:var(--text-tertiary)}._modelNameMain_1ivpu_69{color:var(--color-code-orange)}._time_1ivpu_73{font-size:12px;color:var(--text-gray);margin-left:auto}._detailRow_1ivpu_79{display:flex;gap:8px;font-size:12px;align-items:center}._urlText_1ivpu_86{color:var(--text-disabled);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}._duration_1ivpu_95{color:var(--text-gray);flex-shrink:0}._statusOk_1ivpu_100{color:var(--color-success);opacity:.5;flex-shrink:0}._statusErr_1ivpu_106{color:var(--color-error);flex-shrink:0}._statusDefault_1ivpu_111{color:var(--text-tertiary);flex-shrink:0}._usageBox_1ivpu_116{background:var(--bg-container);border-radius:4px;padding:3px 6px;margin-top:4px;font-size:12px;color:var(--text-gray);line-height:1.6}._cacheDot_1ivpu_126{display:inline-block;width:6px;height:6px;border-radius:50%;margin:0 3px;vertical-align:middle}._cacheDotLoss_1ivpu_135{background-color:var(--color-red-dark-bg);cursor:help}._cacheDotNormal_1ivpu_140{background-color:var(--border-hover)}._cacheDotTools_1ivpu_145{background-color:var(--color-code-purple);box-shadow:0 0 0 1.5px var(--color-purple-border)}._tagMainAgent_1ivpu_150{color:var(--color-code-orange);border-color:var(--color-code-orange-border);background:var(--color-code-orange-bg)}._tagPlan_1ivpu_156{color:var(--color-error-muted);border-color:var(--color-error-muted);background-color:var(--bg-base-pure)}._tagMuted_1ivpu_162{color:var(--text-muted);border-color:var(--border-light);background-color:var(--bg-base-pure)}._tooltipPreLine_1ivpu_168{white-space:pre-line}._GzYRV{line-height:1.2;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word}._3eOF8{margin-right:5px;font-weight:700}._3eOF8+._3eOF8{margin-left:-5px}._1MFti{cursor:pointer}._f10Tu{font-size:1.2em;margin-right:5px;-webkit-user-select:none;-moz-user-select:none;user-select:none}._1UmXx:after{content:"▸"}._1LId0:after{content:"▾"}._1pNG9{margin-right:5px}._1pNG9:after{content:"...";font-size:.8em}._2IvMF{background:#eee}._2bkNM{margin:0;padding:0 10px}._1BXBN{margin:0;padding:0}._1MGIk{font-weight:600;margin-right:5px;color:#000}._3uHL6{color:#000}._2T6PJ,._1Gho6{color:#df113a}._vGjyY{color:#2a3f3c}._1bQdo{color:#0b75f5}._3zQKs{color:#469038}._1xvuR{color:#43413d}._oLqym,._2AXVT,._2KJWg{color:#000}._11RoI{background:#002b36}._17H2C,._3QHg2,._3fDAz{color:#fdf6e3}._2bSDX{font-weight:bolder;margin-right:5px;color:#fdf6e3}._gsbQL{color:#fdf6e3}._LaAZe,._GTKgm{color:#81b5ac}._Chy1W{color:#cb4b16}._2bveF{color:#d33682}._2vRm-{color:#ae81ff}._1prJR{color:#268bd2}._container_qeuid_1{background:var(--bg-container);border-radius:6px;border:1px solid var(--border-primary);padding:12px;font-size:13px;font-family:monospace;overflow:auto}._root_16dib_1{display:flex;height:100%;min-height:0;gap:0}._sidebar_16dib_9{width:220px;flex-shrink:0;border-right:1px solid var(--border-primary);overflow-y:auto;padding:4px 0;-webkit-overflow-scrolling:touch}._section_16dib_18{-webkit-user-select:none;user-select:none}._sectionHeader_16dib_22{display:flex;align-items:center;gap:6px;width:100%;padding:6px 10px;cursor:pointer;color:var(--text-primary);font-size:12px;font-weight:600;transition:background .15s;background:none;border:0;text-align:left;font:inherit}._sectionHeader_16dib_22:hover{background:var(--overlay-light-faint)}._sectionHeader_16dib_22:focus-visible{outline:1px solid var(--color-primary-outline);outline-offset:-1px}._arrow_16dib_48{font-size:10px;color:var(--text-tertiary);flex-shrink:0}._sectionTitle_16dib_54{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._sectionCount_16dib_62{font-size:11px;color:var(--text-muted);background:var(--bg-elevated);border-radius:10px;padding:0 6px;line-height:18px}._sectionBody_16dib_71{padding:2px 0}._historyToggle_16dib_76{display:flex;align-items:center;gap:6px;width:100%;padding:4px 10px 4px 14px;cursor:pointer;color:var(--text-muted);font-size:11px;transition:color .15s,background .15s;background:none;border:0;text-align:left;font:inherit}._historyToggle_16dib_76:hover{color:var(--text-tertiary);background:var(--overlay-light-faint)}._historyToggle_16dib_76:focus-visible{outline:1px solid var(--color-primary-outline);outline-offset:-1px}._historyToggleLabel_16dib_102{flex:1}._item_16dib_107{display:flex;align-items:center;justify-content:space-between;width:100%;padding:4px 9px 4px 23px;font-size:12px;color:var(--text-tertiary);cursor:pointer;transition:background .15s,color .15s;background:none;border:1px solid transparent;border-radius:4px;box-sizing:border-box;text-align:left;font:inherit}._item_16dib_107:hover{background:var(--overlay-light-faint);color:var(--text-primary)}._item_16dib_107:focus-visible{outline:1px solid var(--color-primary-outline);outline-offset:-1px}._itemActive_16dib_135,._itemActive_16dib_135:hover{background:var(--color-primary-bg-faint);color:var(--color-primary);border-color:var(--color-primary-light)}._itemContent_16dib_142{flex:1;min-width:0;overflow:hidden}._itemLabel_16dib_148{font-family:monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}._itemSublabel_16dib_156{font-size:10px;color:var(--text-disabled);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:1px}._itemTime_16dib_165{font-size:9px;color:var(--text-disabled);flex-shrink:0;margin-left:4px;font-family:monospace}._contentWrap_16dib_176{flex:1;min-width:0;display:flex;flex-direction:column}._content_16dib_174{flex:1;min-width:0;min-height:0;overflow:auto;padding:12px 16px;-webkit-overflow-scrolling:touch}._contentEmpty_16dib_192{height:100%;display:flex;align-items:center;justify-content:center}._contentInner_16dib_199{padding-bottom:20px}._emptyWrap_16dib_203{display:flex;align-items:center;justify-content:center;height:200px}._roleHeader_16dib_211{display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:nowrap}._roleBadge_16dib_219{font-size:10px;font-weight:600;letter-spacing:.04em;padding:2px 7px;border-radius:4px;flex-shrink:0}._role_user_16dib_228{background:var(--color-primary-bg-lighter);color:var(--color-primary-lighter);border:1px solid var(--color-primary-bg-medium)}._role_assistant_16dib_234{background:var(--color-purple-bg);color:var(--color-code-purple);border:1px solid var(--color-purple-border)}._roleLabel_16dib_240{font-size:11px;color:var(--text-muted);flex:1;min-width:0}._contentTime_16dib_247{margin-left:auto;font-size:10px;color:var(--text-disabled);font-family:monospace;flex-shrink:0}._turnDivider_16dib_255{border:none;border-top:1px solid var(--border-primary);margin:14px 0}._textBlock_16dib_262{margin-bottom:8px;border:1px solid var(--border-primary);border-radius:6px;overflow:hidden}._textBlockBar_16dib_269{display:flex;align-items:center;gap:6px;padding:4px 10px;background:var(--bg-container);border-bottom:1px solid var(--border-primary)}._textBlockBody_16dib_278{padding:10px 12px;font-size:13px;line-height:1.7;color:var(--text-primary);word-break:break-word}._textBlockCompact_16dib_286{position:relative;padding:8px 10px;font-size:12px;color:var(--text-light)}._textBlockCompactFloat_16dib_293{float:right;margin-left:6px;margin-bottom:2px}._thinkingBlock_16dib_300{margin-bottom:8px;border:1px solid var(--color-thinking-border);border-radius:6px;overflow:hidden;background:var(--color-thinking-bg)}._thinkingHeader_16dib_308{display:flex;align-items:center;gap:6px;padding:5px 10px;cursor:pointer;font-size:12px;color:var(--text-tertiary);transition:background .15s}._thinkingHeader_16dib_308:hover{background:var(--overlay-light-faint)}._thinkingPreview_16dib_323{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--text-muted);font-size:11px}._thinkingBody_16dib_333{padding:8px 12px;border-top:1px solid var(--color-thinking-border)}._toolBlock_16dib_339{margin-bottom:8px;border:1px solid var(--border-primary);border-radius:6px;overflow:hidden}._toolBlockResult_16dib_346{border-color:var(--color-green-border)}._toolBlockError_16dib_350{border-color:var(--color-red-dark-border)}._toolBlockHeader_16dib_354{display:flex;align-items:center;gap:8px;padding:5px 10px;background:var(--bg-container);border-bottom:1px solid var(--border-primary);font-size:12px;flex-wrap:wrap}._toolBlockBody_16dib_365{padding:8px 10px;font-size:12px}._toolName_16dib_370{color:var(--text-primary);font-weight:500;font-family:monospace}._toolId_16dib_376{color:var(--text-disabled);font-size:10px;font-family:monospace;margin-left:auto}._errorLabel_16dib_383{font-size:10px;color:var(--color-error-light);background:var(--color-error-bg-light);border:1px solid var(--color-error-border);border-radius:3px;padding:1px 5px}._blockTag_16dib_393{font-size:9px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;padding:1px 5px;border-radius:3px;background:var(--bg-elevated);color:var(--text-muted);border:1px solid var(--border-primary);flex-shrink:0}._blockTagText_16dib_406{background:var(--color-primary-bg-faint);color:var(--color-primary-lighter);border-color:var(--color-primary-bg-lighter)}._blockTagThinking_16dib_412{background:var(--color-warning-bg-faint);color:var(--color-warning);border-color:var(--color-warning-border-light)}._blockTagResult_16dib_418{background:var(--color-green-dark-bg);color:var(--color-success);border-color:var(--color-green-dark-border)}._blockTagError_16dib_424{background:var(--color-error-bg-faint);color:var(--color-error-light);border-color:var(--color-error-border-light)}._jsonBlock_16dib_431{margin-bottom:8px;border:1px solid var(--border-primary);border-radius:6px;overflow:hidden}._jsonBlockLabel_16dib_438{font-size:10px;color:var(--text-muted);padding:3px 10px;background:var(--bg-container);border-bottom:1px solid var(--border-primary);font-family:monospace}._blockSeparator_16dib_448{border:none;border-top:1px solid var(--border-primary);margin:16px 0}._markdownBody_16dib_455{font-size:13px;line-height:1.7;color:var(--text-primary);word-break:break-word}._contentToolbar_16dib_463{display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:8px 16px 0;flex-shrink:0}._contentToolbarLabel_16dib_472{font-size:11px;color:var(--text-muted)}._rawPre_16dib_478{background:var(--bg-code-dark);border:1px solid var(--border-primary);border-radius:6px;padding:12px;font-size:12px;color:var(--text-primary);white-space:pre-wrap;word-break:break-all;margin:0}._toolAdded_16dib_491{color:var(--color-success)}._toolRemoved_16dib_495{color:var(--color-error);text-decoration:line-through}._toolDiffTag_16dib_500{margin-left:6px;font-size:9px;font-family:sans-serif;text-decoration:none;opacity:.85}._toolDiffSummary_16dib_508{margin-left:6px;font-size:11px;font-family:monospace;display:inline-flex;gap:4px;flex-shrink:0}._toolDiffSummaryAdd_16dib_517{color:var(--color-success)}._toolDiffSummaryRemove_16dib_521{color:var(--color-error)}._container_rg6mx_1{height:100%;overflow:hidden;padding:0 16px;display:flex;flex-direction:column;background:var(--bg-base)}._emptyState_rg6mx_10{display:flex;align-items:center;justify-content:center;height:100%}._urlSection_rg6mx_17{padding:12px 0;border-bottom:1px solid var(--border-primary);display:flex;align-items:flex-start;flex-shrink:0}._urlLeft_rg6mx_25{flex:1;min-width:0}._tokenStatsBox_rg6mx_30{flex-shrink:0;padding-left:12px;display:flex;align-items:center}._tokenGrid_rg6mx_37{display:flex;border:1px solid var(--border-secondary);border-radius:6px;overflow:hidden;min-width:360px;font-size:11px;line-height:1.6}._tokenRows_rg6mx_47{flex:1}._tokenRow_rg6mx_47{display:flex}._tokenRowBorder_rg6mx_55{border-top:1px solid var(--border-secondary)}._tokenLabel_rg6mx_59{color:var(--text-tertiary);padding:4px 8px;white-space:nowrap;font-weight:600}._tokenTd_rg6mx_66{flex:1;color:var(--text-primary);text-align:right;padding:4px 8px;font-family:monospace;white-space:nowrap}._tokenHitRate_rg6mx_75{display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-primary);padding:4px 8px;font-family:monospace;white-space:nowrap;border-left:1px solid var(--border-secondary);min-width:100px}._tokenHitRateLabel_rg6mx_88{color:var(--text-tertiary);font-size:10px;font-family:sans-serif}._tokenRowBorder_rg6mx_55 td{border-top:1px solid var(--border-secondary)}._urlText_rg6mx_98{color:var(--text-primary);font-size:13px;margin-bottom:8px;word-break:break-all}._metaText_rg6mx_105,._headersContainer_rg6mx_109{font-size:12px}._headerRow_rg6mx_113{display:flex;padding:4px 0;border-bottom:1px solid var(--border-primary)}._headerKey_rg6mx_119{min-width:200px;flex-shrink:0}._headerValue_rg6mx_124{word-break:break-all;margin-left:8px}._streamingBox_rg6mx_129{padding:20px;background:var(--bg-elevated);border-radius:6px;border:1px solid var(--border-primary)}._bodyToolbar_rg6mx_136{display:flex;gap:8px;margin-bottom:8px}._rawTextPre_rg6mx_142{background:var(--bg-code-dark);border:1px solid var(--border-primary);border-radius:6px;padding:12px;font-size:12px;color:var(--text-primary);overflow:auto;max-height:600px;white-space:pre-wrap;word-break:break-all}._tabContent_rg6mx_155{padding:16px 0 0;height:100%;overflow-y:auto;-webkit-overflow-scrolling:touch}._collapseSpacing_rg6mx_163{margin-bottom:16px}._bodyLabel_rg6mx_167{margin:0}._bodyHeader_rg6mx_171{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}._diffSection_rg6mx_178{margin-bottom:16px}._diffToggle_rg6mx_182{display:inline-block;margin-bottom:8px;cursor:pointer}._diffIcon_rg6mx_188{font-size:12px;margin-left:4px}._viewInChatBtn_rg6mx_193{display:inline-flex;align-items:center;height:26px;border-radius:13px;border:1px solid var(--border-light);background:#0000;color:var(--text-tertiary);cursor:pointer;font-size:12px;transition:all .2s;padding:0 10px;white-space:nowrap}._viewInChatBtn_rg6mx_193:hover{border-color:var(--text-muted);color:var(--text-primary);background:var(--overlay-light-faint)}._reminderSelect_rg6mx_214{min-width:140px;font-size:12px}._reminderSelect_rg6mx_214 .ant-select-selector.ant-select-selector{border-radius:2px;border-color:var(--border-light);background:#0000;min-height:26px;height:auto;padding:0 8px;font-family:monospace}._reminderSelect_rg6mx_214 .ant-select-selection-placeholder{font-size:11px}._diffHeaderRow_rg6mx_233{display:flex;align-items:center;gap:8px}._diffSpaceRight_rg6mx_239{margin-left:auto}._reminderFilterWrapper_rg6mx_243{display:inline-flex;align-items:center;gap:4px}._reminderLabel_rg6mx_249{color:var(--text-tertiary);font-size:12px;font-family:monospace}._cacheTabContent_rg6mx_255{padding-top:0;overflow:hidden}._userPromptList_rg6mx_260{width:600px;max-height:300px;overflow-y:auto}._userPromptItem_rg6mx_266{padding:4px 8px;font-size:12px;color:var(--text-secondary);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-radius:3px}._userPromptNavBtn_rg6mx_277{margin-left:auto;font-size:11px;color:var(--color-primary);cursor:pointer;border:1px solid var(--color-primary);border-radius:3px;padding:1px 6px;white-space:nowrap}._cacheContent_rg6mx_288{padding:8px 0;height:100%;display:flex;flex-direction:column}._cacheTokenBar_rg6mx_295{display:flex;align-items:center;font-size:12px;font-family:monospace;color:var(--text-light);margin-bottom:12px;flex-shrink:0}._cacheTokenWrite_rg6mx_305{color:var(--color-code-orange)}._cacheTokenRead_rg6mx_309{color:var(--color-success)}._cacheCopyIcon_rg6mx_313{margin-left:8px;cursor:pointer;color:var(--text-tertiary);transition:color .2s}._cacheScrollArea_rg6mx_320{flex:1;overflow-y:auto;min-height:0}._cacheSectionBlock_rg6mx_326{margin-bottom:12px}._cacheSectionHeader_rg6mx_330{font-size:13px;font-weight:600;color:var(--text-primary);margin-bottom:6px;cursor:pointer;-webkit-user-select:none;user-select:none;display:flex;align-items:center;gap:4px}._cacheCollapseArrow_rg6mx_342{display:inline-block;transition:transform .2s;font-size:10px}._cachePre_rg6mx_348{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:var(--text-secondary);background:var(--bg-container);border:1px solid var(--border-primary);border-radius:4px;padding:8px;margin:4px 0;font-family:Menlo,Monaco,monospace}._cacheHighlightSvg_rg6mx_362{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible}._cachePreSystem_rg6mx_371{white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;color:var(--text-secondary);background:var(--bg-code-system);border:1px solid var(--border-code-system);border-radius:4px;padding:8px;margin:4px 0;font-family:Menlo,Monaco,monospace}._container_rg6mx_1 .ant-tabs>.ant-tabs-nav{margin-bottom:0}._container_rg6mx_1 .ant-tabs{flex:1;min-height:0;display:flex;flex-direction:column}._container_rg6mx_1 .ant-tabs>.ant-tabs-content-holder{flex:1;min-height:0}._container_rg6mx_1 .ant-tabs-content,._container_rg6mx_1 .ant-tabs-tabpane-active{height:100%}._resizer_pzn4b_1{width:6px;cursor:col-resize;background:var(--bg-elevated);flex-shrink:0;transition:background .2s}._resizer_pzn4b_1:hover{background:var(--color-primary-light)}._flag_1q4ri_3{display:inline-flex;align-items:center;justify-content:center;font-size:13px;line-height:1;cursor:help;-webkit-user-select:none;user-select:none;height:14px;background:none;border:none;padding:0;color:inherit;font-family:inherit}._flag_1q4ri_3:focus-visible{outline:2px solid var(--primary-color, #1677ff);outline-offset:2px;border-radius:2px}._popover_1q4ri_27{color:var(--text-secondary);font-size:13px;line-height:22px}._meta_1q4ri_33{color:var(--text-tertiary);font-size:12px}._usagePill_18dz6_4{position:relative;display:inline-flex;align-items:center;justify-content:flex-start;border-radius:999px;border:1px solid;border-color:var(--text-disabled);color:var(--text-disabled);padding:0 7px;height:15px;font-size:11px;line-height:1;overflow:hidden;white-space:nowrap;cursor:default;background:var(--bg-base-pure)}._usageFill_18dz6_12{position:absolute;left:0;top:0;bottom:0;width:var(--usage-percent, 0);background-color:#999;opacity:.2;transition:width .5s ease;pointer-events:none}._usageContent_18dz6_38{position:relative;z-index:1;display:inline-flex;align-items:center}._usageText_18dz6_45{font-variant-numeric:tabular-nums}._muted_18dz6_50{border-color:var(--border-light);color:var(--text-disabled);background:var(--bg-surface)}._pop_18dz6_57{min-width:220px;font-size:12px;color:var(--text-primary)}._popTitle_18dz6_63{font-weight:600;margin-bottom:6px}._popTable_18dz6_69{border-collapse:collapse}._popTable_18dz6_69 td{padding-top:3px;padding-bottom:3px;vertical-align:middle}._tdName_18dz6_74{color:var(--text-secondary);padding-right:5px;white-space:nowrap}._tdBar_18dz6_74{padding-right:5px;white-space:nowrap}._tdReset_18dz6_92{color:var(--text-secondary);font-variant-numeric:tabular-nums;white-space:nowrap}._bar_18dz6_101{position:relative;display:inline-flex;align-items:center;justify-content:center;width:100px;height:14px;border-radius:999px;border:1px solid var(--text-disabled);overflow:hidden;background:var(--bg-base-pure);vertical-align:middle}._barFill_18dz6_115{position:absolute;left:0;top:0;bottom:0;background-color:var(--text-secondary);opacity:.15;pointer-events:none}._barText_18dz6_125{position:relative;z-index:1;font-size:11px;line-height:1;font-variant-numeric:tabular-nums;color:var(--text-primary)}