cc-viewer 1.6.334 → 1.6.336

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.
@@ -0,0 +1,164 @@
1
+ // Waiter-liveness reaper for short-poll AskUserQuestion entries.
2
+ //
3
+ // Problem: when the user declines an AskUserQuestion at the Claude Code CLI, the
4
+ // ask-bridge hook subprocess is SIGTERMed. Its short-poll GET loop simply stops;
5
+ // the GET close handler in ask-perm.js only unregisters the poll listener (a socket
6
+ // close is indistinguishable from the normal ~25s re-poll cycle), so nothing ever
7
+ // resolves the entry. It then stays `pending` in memory (24h timer) and on disk,
8
+ // and the WS reconnect replay keeps popping a dead "Question" modal.
9
+ //
10
+ // Fix: track when the waiter last proved it was alive (POST create / GET poll /
11
+ // hanging listener / open POST socket) and cancel entries whose waiter has gone
12
+ // silent for ASK_WAITER_LIVENESS_MS. This is NOT an ask timeout — the
13
+ // "GUI effectively no-timeout" contract holds; a connected bridge keeps its ask
14
+ // alive indefinitely. A falsely-reaped bridge degrades gracefully: its next GET
15
+ // hits consumeIfFinal and receives a definitive `{cancelled}` (never a 404 loop).
16
+ //
17
+ // Cross-instance safety: the ask-store file is SHARED by every cc-viewer instance
18
+ // (global ~/.claude/cc-viewer/). reapDeadAskWaiters therefore touches ONLY entries
19
+ // this instance owns in its pendingAskHooks Map (memory = ownership). Disk-only
20
+ // orphans are handled by sweepOrphanedDiskAsks, a one-shot boot sweep that runs
21
+ // only when no other cc-viewer instance is listening on the CCV port range.
22
+ import { ASK_WAITER_LIVENESS_MS, ASK_WAITER_REAP_INTERVAL_MS } from './ask-constants.js';
23
+
24
+ /**
25
+ * Broadcast a cancel to browsers + short-poll listeners + parent process.
26
+ * Shared by the periodic reaper and the boot sweep.
27
+ */
28
+ async function cancelAndBroadcast(deps, id, reason) {
29
+ const wrote = await deps.markCancelled(id, reason);
30
+ if (!wrote) return false; // an answer/cancel landed first — never clobber, never broadcast
31
+ try { deps.notifyShortPollCancel?.(id, reason); } catch {}
32
+ try { deps.broadcastCancelled?.(id, reason); } catch {}
33
+ try { deps.notifyParentPending?.({ type: 'ask-hook-cancelled', id }); } catch {}
34
+ return true;
35
+ }
36
+
37
+ /**
38
+ * Periodic sweep over MEMORY-OWNED short-poll entries whose waiter went silent.
39
+ *
40
+ * deps: {
41
+ * pendingAskHooks: Map<id, {questions,res,timer,createdAt,shortPoll}>,
42
+ * shortPollListeners: Map<id, Set<{finished}>>,
43
+ * askWaiterLastPoll: Map<id, ms>, // written by POST create + GET poll
44
+ * markCancelled(id, reason) -> Promise<boolean>,
45
+ * loadAskStore() -> {id: entry},
46
+ * notifyShortPollCancel?(id, reason), broadcastCancelled?(id, reason),
47
+ * notifyParentPending?(msg),
48
+ * livenessMs?, reapIntervalMs?,
49
+ * }
50
+ * state: caller-held mutable { lastSweepAt } so the wake guard survives across calls.
51
+ */
52
+ export async function reapDeadAskWaiters(deps, state, { now = Date.now() } = {}) {
53
+ const livenessMs = deps.livenessMs ?? ASK_WAITER_LIVENESS_MS;
54
+ const intervalMs = deps.reapIntervalMs ?? ASK_WAITER_REAP_INTERVAL_MS;
55
+ const lastPoll = deps.askWaiterLastPoll;
56
+
57
+ const candidates = [];
58
+ for (const [id, entry] of deps.pendingAskHooks) {
59
+ if (entry && entry.shortPoll === true) candidates.push([id, entry]);
60
+ }
61
+
62
+ // Wake guard: after a laptop sleep / long event-loop stall the (same-machine)
63
+ // bridge slept too and hasn't had a chance to re-poll. Re-baseline instead of
64
+ // reaping on the first sweep after resume.
65
+ const prevSweepAt = state.lastSweepAt ?? now;
66
+ state.lastSweepAt = now;
67
+ if (now - prevSweepAt > 3 * intervalMs) {
68
+ for (const [id] of candidates) lastPoll.set(id, now);
69
+ return { reaped: [], rebaselined: true };
70
+ }
71
+
72
+ const reaped = [];
73
+ for (const [id, entry] of candidates) {
74
+ // Open POST socket = waiter provably alive (covers the plugin onAskRequest
75
+ // window, where the short-poll ack has not been written yet and no GETs flow).
76
+ if (entry.res && !entry.res.writableEnded && !entry.res.destroyed) {
77
+ lastPoll.set(id, now);
78
+ continue;
79
+ }
80
+ // A hanging unfinished GET listener = waiter connected right now
81
+ // (covers legitimate wait=60000 pollers beyond the liveness window).
82
+ const listeners = deps.shortPollListeners.get(id);
83
+ let hasLive = false;
84
+ if (listeners) {
85
+ for (const l of listeners) { if (l && !l.finished) { hasLive = true; break; } }
86
+ }
87
+ if (hasLive) {
88
+ lastPoll.set(id, now);
89
+ continue;
90
+ }
91
+ const last = lastPoll.get(id) ?? entry.createdAt ?? now;
92
+ if (now - last < livenessMs) continue;
93
+
94
+ // Waiter is gone. Clean memory unconditionally; write/broadcast the cancel
95
+ // only when the shared disk row still exists as ours to resolve (an absent
96
+ // row means another instance's answer consumed it — do not fabricate a
97
+ // cancelled placeholder or grey out a real answer).
98
+ if (entry.timer) clearTimeout(entry.timer);
99
+ deps.pendingAskHooks.delete(id);
100
+ lastPoll.delete(id);
101
+ const diskRow = deps.loadAskStore()[id];
102
+ if (diskRow) {
103
+ await cancelAndBroadcast(deps, id, 'hook waiter lost');
104
+ }
105
+ reaped.push(id);
106
+ }
107
+
108
+ // GC liveness records for ids no longer pending in this instance.
109
+ for (const id of lastPoll.keys()) {
110
+ if (!deps.pendingAskHooks.has(id)) lastPoll.delete(id);
111
+ }
112
+ return { reaped, rebaselined: false };
113
+ }
114
+
115
+ /**
116
+ * Parse `lsof -Fp` output into pids, excluding our own. Strict `^p\d+$` lines only
117
+ * (mirrors isAnyCcvBusy in updater.js).
118
+ */
119
+ function otherCcvPids(lsofOut, ownPid) {
120
+ return String(lsofOut).replace(/\r/g, '').split('\n')
121
+ .filter((l) => /^p\d+$/.test(l))
122
+ .map((l) => Number(l.slice(1)))
123
+ .filter((n) => Number.isFinite(n) && n > 0 && n !== ownPid);
124
+ }
125
+
126
+ /**
127
+ * One-shot boot sweep: cancel disk-only pending entries orphaned by a previous
128
+ * server process. Runs ONLY when this is provably the sole cc-viewer instance
129
+ * (lsof scan of the CCV port range); on any doubt (another pid listening, lsof
130
+ * missing/failing, win32) it skips entirely — another instance may own those
131
+ * entries, and its own reaper / the fallback UI handles them.
132
+ *
133
+ * deps: reapDeadAskWaiters deps plus {
134
+ * bootTime: ms, ownPid: number, portRange: [start, end],
135
+ * lsofImpl?(cmd) -> string, // injectable for tests; default caller-provided
136
+ * platform?: string, // default process.platform
137
+ * }
138
+ */
139
+ export async function sweepOrphanedDiskAsks(deps, { now = Date.now() } = {}) {
140
+ const plat = deps.platform ?? process.platform;
141
+ if (plat === 'win32' || typeof deps.lsofImpl !== 'function') return { swept: [], skipped: true };
142
+ const [start, end] = deps.portRange;
143
+ let out;
144
+ try {
145
+ // lsofImpl may be sync or async (production uses async exec to keep the
146
+ // event loop free); await handles both.
147
+ out = await deps.lsofImpl(`lsof -iTCP:${start}-${end} -sTCP:LISTEN -P -n -Fp`);
148
+ } catch {
149
+ return { swept: [], skipped: true }; // conservative: cannot prove we are alone
150
+ }
151
+ if (otherCcvPids(out, deps.ownPid).length > 0) return { swept: [], skipped: true };
152
+
153
+ const swept = [];
154
+ const all = deps.loadAskStore();
155
+ for (const e of Object.values(all)) {
156
+ if (!e || e.status !== 'pending') continue;
157
+ if (!Array.isArray(e.questions) || e.questions.length === 0) continue;
158
+ if (typeof e.createdAt === 'number' && e.createdAt >= deps.bootTime) continue; // ours / fresh
159
+ if (deps.pendingAskHooks.has(e.id)) continue; // owned by this instance
160
+ const wrote = await cancelAndBroadcast(deps, e.id, 'orphaned pending ask (owner instance gone)');
161
+ if (wrote) swept.push(e.id);
162
+ }
163
+ return { swept, skipped: false };
164
+ }
@@ -287,7 +287,8 @@ export function renderLoginPage({ lang = 'en', error = false } = {}) {
287
287
  body {
288
288
  margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
289
289
  background: #1a1a1a; color: #e8e8e8;
290
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
290
+ /* Standalone document (no SPA CSS bundle): keep in sync with --font-ui in src/global.css */
291
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
291
292
  }
292
293
  .card {
293
294
  width: 100%; max-width: 340px; padding: 32px 28px; margin: 16px;
@@ -1,5 +1,5 @@
1
1
  // Ask / permission hook bridge routes (moved verbatim from server.js handleRequest).
2
- import { loadAskStore, consumeIfFinal as askStoreConsumeIfFinal } from '../lib/ask-store.js';
2
+ import { loadAskStore, consumeIfFinal as askStoreConsumeIfFinal, markCancelled as askStoreMarkCancelled } from '../lib/ask-store.js';
3
3
  import { runWaterfallHook, runParallelHook } from '../lib/plugin-loader.js';
4
4
  import { sendEventToClients } from '../lib/log-watcher.js';
5
5
 
@@ -128,6 +128,9 @@ function askHook(req, res, parsedUrl, isLocal, deps) {
128
128
  const _placeholderEntry = { questions, res, timer: null, createdAt: Date.now(), shortPoll: shortPollMode };
129
129
  deps.pendingAskHooks.set(id, _placeholderEntry);
130
130
  deps.persistAskEntry(id, _placeholderEntry);
131
+ // Seed waiter liveness for the reaper (ask-reaper.js). Optional chaining:
132
+ // older deps stubs and third-party callers may not provide the map.
133
+ if (shortPollMode) deps.askWaiterLastPoll?.set(id, Date.now());
131
134
 
132
135
  // res.on('close') 提前注册:handler 用 entry.timer 守卫(占位期 timer:null → 仅 delete Map)
133
136
  // Phase 3: short-poll 模式下 res 主动 end,close 不视为 client cancel;entry 由 24h timer 兜底清理。
@@ -237,6 +240,10 @@ async function askHookResult(req, res, parsedUrl, isLocal, deps) {
237
240
  }
238
241
  const qs = new URLSearchParams(m[2] || '');
239
242
  const wait = Math.max(1000, Math.min(60000, parseInt(qs.get('wait') || '30000', 10)));
243
+ // Refresh waiter liveness on every poll arrival (see ask-reaper.js). Unconditional:
244
+ // even a poll that ends in 404/final proves the waiter process is alive; the reaper
245
+ // GCs records for ids that are no longer pending.
246
+ deps.askWaiterLastPoll?.set(id, Date.now());
240
247
 
241
248
  // 1) disk 命中 answered/cancelled:浏览器答得早过 GET 到达 → 立即返并消费(一次性)
242
249
  // 用 consumeIfFinal 单次 withLock 内判 status 决定是否 delete —— 旧设计的
@@ -289,6 +296,81 @@ async function askHookResult(req, res, parsedUrl, isLocal, deps) {
289
296
  }
290
297
  }
291
298
 
299
+ // POST /api/ask-hook/:id/cancel — best-effort cancel from a dying hook process
300
+ // (ask-bridge SIGTERM/SIGINT handler) or any client that wants to durably resolve
301
+ // a pending ask. Mirrors the WS ask-cancel handler in server.js for the hook path.
302
+ // Auth: standard request prelude (loopback-exempt), same as the sibling ask-hook routes.
303
+ function askHookCancel(req, res, parsedUrl, isLocal, deps) {
304
+ const m = parsedUrl.pathname.match(/^\/api\/ask-hook\/([^/?]+)\/cancel$/);
305
+ const id = m ? decodeURIComponent(m[1]) : '';
306
+ if (!id || id.length > 256 || !/^[a-zA-Z0-9_-]+$/.test(id)) {
307
+ res.writeHead(400, { 'Content-Type': 'application/json' });
308
+ res.end(JSON.stringify({ error: 'invalid id' }));
309
+ return;
310
+ }
311
+ let body = '';
312
+ let bodyTooLarge = false;
313
+ req.on('data', (chunk) => {
314
+ if (bodyTooLarge) return;
315
+ body += chunk;
316
+ if (body.length > 4096) {
317
+ // Respond here, not in 'end': req.destroy() never emits 'end' in real Node,
318
+ // so a deferred 413 would be dead code (mirrors permHook's in-data rejection).
319
+ bodyTooLarge = true;
320
+ try { if (!res.headersSent) { res.writeHead(413, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Request body too large' })); } } catch {}
321
+ req.destroy();
322
+ }
323
+ });
324
+ req.on('end', async () => {
325
+ if (bodyTooLarge) return;
326
+ let reason = 'hook process exited';
327
+ try {
328
+ const parsed = body ? JSON.parse(body) : {};
329
+ if (typeof parsed.reason === 'string' && parsed.reason) reason = parsed.reason.slice(0, 500);
330
+ } catch {}
331
+ // Respond fast: the caller may be a process that is about to die.
332
+ try { if (!res.headersSent) { res.writeHead(204); res.end(); } } catch {}
333
+ try {
334
+ let handled = false;
335
+ const entry = deps.pendingAskHooks.get(id);
336
+ if (entry) {
337
+ if (entry.timer) clearTimeout(entry.timer);
338
+ deps.pendingAskHooks.delete(id);
339
+ deps.askWaiterLastPoll?.delete(id);
340
+ if (entry.shortPoll) {
341
+ const wrote = await askStoreMarkCancelled(id, reason);
342
+ if (wrote) deps.notifyShortPollCancel?.(id, reason);
343
+ handled = wrote;
344
+ } else {
345
+ // Long-poll: end the hanging hook response so the bridge unblocks.
346
+ try {
347
+ if (entry.res && !entry.res.headersSent) {
348
+ entry.res.writeHead(200, { 'Content-Type': 'application/json' });
349
+ entry.res.end(JSON.stringify({ cancelled: true, reason }));
350
+ }
351
+ } catch {}
352
+ deps.persistAskDelete(id);
353
+ handled = true;
354
+ }
355
+ } else {
356
+ // Disk-only (e.g. entry orphaned by a previous server process). Wake any
357
+ // hanging poll listeners too — parity with the WS ask-cancel handler.
358
+ handled = await askStoreMarkCancelled(id, reason);
359
+ if (handled) deps.notifyShortPollCancel?.(id, reason);
360
+ }
361
+ if (handled) {
362
+ if (deps.terminalWss) {
363
+ const cmsg = JSON.stringify({ type: 'ask-hook-cancelled', id, reason });
364
+ deps.terminalWss.clients.forEach((c) => {
365
+ if (c.readyState === 1) try { c.send(cmsg); } catch {}
366
+ });
367
+ }
368
+ deps.notifyParentPending({ type: 'ask-hook-cancelled', id });
369
+ }
370
+ } catch {}
371
+ });
372
+ }
373
+
292
374
  // Permission hook bridge: receive tool permission request from perm-bridge.js, long-poll for user decision
293
375
  function permHook(req, res, parsedUrl, isLocal, deps) {
294
376
  let body = '';
@@ -447,6 +529,7 @@ export const askPermRoutes = [
447
529
  { method: 'GET', match: 'exact', path: '/api/pending-asks', handler: pendingAsks },
448
530
  { method: 'POST', match: 'exact', path: '/api/ask-hook', handler: askHook },
449
531
  { predicate: (url, method) => url.startsWith('/api/ask-hook/') && url.includes('/result') && method === 'GET', handler: askHookResult },
532
+ { predicate: (url, method) => url.startsWith('/api/ask-hook/') && url.includes('/cancel') && method === 'POST', handler: askHookCancel },
450
533
  { method: 'POST', match: 'exact', path: '/api/perm-hook', handler: permHook },
451
534
  { method: 'POST', match: 'exact', path: '/api/stream-chunk', handler: streamChunk },
452
535
  ];
package/server/server.js CHANGED
@@ -9,8 +9,9 @@ import { execFile, exec, spawn } from 'node:child_process';
9
9
  import { promisify } from 'node:util';
10
10
  import { Worker } from 'node:worker_threads';
11
11
  import { isPathContained } from './lib/file-api.js';
12
- import { setEntry as askStoreSetEntry, deleteEntry as askStoreDeleteEntry, pruneStale as askStorePruneStale, markAnswered as askStoreMarkAnswered, markCancelled as askStoreMarkCancelled } from './lib/ask-store.js';
13
- import { ASK_TIMEOUT_MS } from './lib/ask-constants.js';
12
+ import { setEntry as askStoreSetEntry, deleteEntry as askStoreDeleteEntry, pruneStale as askStorePruneStale, markAnswered as askStoreMarkAnswered, markCancelled as askStoreMarkCancelled, loadAskStore as askStoreLoad } from './lib/ask-store.js';
13
+ import { ASK_TIMEOUT_MS, ASK_WAITER_REAP_INTERVAL_MS, ASK_WAITER_LIVENESS_MS } from './lib/ask-constants.js';
14
+ import { reapDeadAskWaiters, sweepOrphanedDiskAsks } from './lib/ask-reaper.js';
14
15
  import { sdkApprovalCloseType } from './lib/sdk-adapter.js';
15
16
  import { DIST_DIR, NODE_MODULES } from './_paths.js';
16
17
  import { createDispatcher } from './routes/_dispatch.js';
@@ -195,6 +196,11 @@ function _persistAskDelete(id) {
195
196
  // until either an answer/cancel arrives or wait ms elapses (then 204).
196
197
  const shortPollListeners = new Map(); // id -> Set<{ res, tid, finished }>
197
198
 
199
+ // Waiter-liveness tracking for short-poll asks (see server/lib/ask-reaper.js).
200
+ // Memory-only BY DESIGN: persisting it would bump the ask-store SCHEMA_VERSION;
201
+ // the reaper's boot-time orphan sweep covers restarts instead.
202
+ const askWaiterLastPoll = new Map(); // id -> ms of last POST create / GET result poll
203
+
198
204
  function _notifyShortPollAnswer(id, answers) {
199
205
  const set = shortPollListeners.get(id);
200
206
  if (!set) return;
@@ -493,6 +499,8 @@ const deps = {
493
499
  pendingAskHooks,
494
500
  pendingPermHooks,
495
501
  shortPollListeners,
502
+ askWaiterLastPoll,
503
+ notifyShortPollCancel: _notifyShortPollCancel,
496
504
  editorSessions,
497
505
  gitRestoreLocks,
498
506
  liveStreamLastSeq: _liveStreamLastSeq,
@@ -864,6 +872,56 @@ export async function startViewer() {
864
872
  }, 60 * 60 * 1000);
865
873
  _pruneAskStoreInterval.unref();
866
874
 
875
+ // Waiter-liveness reaper: resolves short-poll asks whose hook process (ask-bridge)
876
+ // died without notifying us (e.g. AskUserQuestion declined at the CLI → SIGTERM).
877
+ // Keys on waiter liveness, never on wall-clock ask age — the "GUI effectively
878
+ // no-timeout" contract is untouched. See server/lib/ask-reaper.js.
879
+ const _reaperDeps = {
880
+ pendingAskHooks,
881
+ shortPollListeners,
882
+ askWaiterLastPoll,
883
+ markCancelled: askStoreMarkCancelled,
884
+ loadAskStore: askStoreLoad,
885
+ notifyShortPollCancel: _notifyShortPollCancel,
886
+ // terminalWss is assigned later in startServer — resolve at call time via closure.
887
+ broadcastCancelled: (id, reason) => {
888
+ if (!terminalWss) return;
889
+ const cmsg = JSON.stringify({ type: 'ask-hook-cancelled', id, reason });
890
+ terminalWss.clients.forEach((c) => {
891
+ if (c.readyState === 1) try { c.send(cmsg); } catch {}
892
+ });
893
+ },
894
+ notifyParentPending: _notifyParentPending,
895
+ };
896
+ const _reaperState = { lastSweepAt: Date.now() };
897
+ _askReaperTimer = setInterval(() => {
898
+ reapDeadAskWaiters(_reaperDeps, _reaperState).catch(() => {});
899
+ }, ASK_WAITER_REAP_INTERVAL_MS);
900
+ _askReaperTimer.unref();
901
+ // One-shot boot sweep for disk-only orphans left by a previous server process.
902
+ // Delayed one liveness window so a bridge that survived our restart can re-poll
903
+ // and prove ownership first; skipped when another cc-viewer instance is running.
904
+ // Also skipped entirely under custom CCV_START_PORT/CCV_MAX_PORT: the lsof scan
905
+ // covers only OUR range, so an instance on a different custom range would be
906
+ // invisible and its live asks could be falsely swept. The memory-owned reaper
907
+ // and the ApprovalModal fallback UI still cover orphans in that setup.
908
+ const _customPortRange = !!(process.env.CCV_START_PORT || process.env.CCV_MAX_PORT);
909
+ const _bootTime = Date.now();
910
+ if (!_customPortRange) {
911
+ _askOrphanSweepTimer = setTimeout(() => {
912
+ sweepOrphanedDiskAsks({
913
+ ..._reaperDeps,
914
+ bootTime: _bootTime,
915
+ ownPid: process.pid,
916
+ portRange: [START_PORT, MAX_PORT],
917
+ // Async on purpose: a wedged lsof would otherwise block the event loop
918
+ // (and every in-flight request) for up to the full timeout.
919
+ lsofImpl: async (cmd) => (await execAsync(cmd, { timeout: 2000, encoding: 'utf-8' })).stdout,
920
+ }).catch(() => {});
921
+ }, ASK_WAITER_LIVENESS_MS);
922
+ _askOrphanSweepTimer.unref();
923
+ }
924
+
867
925
  // 通过插件 hook 获取 HTTPS 证书选项
868
926
  let httpsOptions = null;
869
927
  try {
@@ -2109,6 +2167,9 @@ export function broadcastTurnEnd(sessionId = null, ts = Date.now()) {
2109
2167
  // 流式状态 SSE 推送定时器:检测 streamingState 变化并广播给所有客户端。
2110
2168
  // rising-edge → turn_end cancel(丢弃 pending,不 flush)由 _observeStreamingTick 统一处理。
2111
2169
  let _streamingStatusTimer = null;
2170
+ // Waiter-liveness reaper timers (armed in startServer, cleared in the stop path).
2171
+ let _askReaperTimer = null;
2172
+ let _askOrphanSweepTimer = null;
2112
2173
  // 启动后 30s 的更新检查 timer 句柄。必须可清理:
2113
2174
  // - .unref() 防止它把事件循环 keep-alive 30s(测试进程靠 --test-force-exit 兜底是时序侥幸);
2114
2175
  // - _doStop 里 clearTimeout 防止 stop/start 循环(Electron tab / 测试)泄漏多个 pending 检查。
@@ -2200,6 +2261,14 @@ async function _doStop() {
2200
2261
  clearInterval(_streamingStatusTimer);
2201
2262
  _streamingStatusTimer = null;
2202
2263
  }
2264
+ if (_askReaperTimer) {
2265
+ clearInterval(_askReaperTimer);
2266
+ _askReaperTimer = null;
2267
+ }
2268
+ if (_askOrphanSweepTimer) {
2269
+ clearTimeout(_askOrphanSweepTimer);
2270
+ _askOrphanSweepTimer = null;
2271
+ }
2203
2272
  if (_updateCheckTimer) {
2204
2273
  clearTimeout(_updateCheckTimer);
2205
2274
  _updateCheckTimer = null;
@@ -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_8rc0c_1{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}._modeLeft_8rc0c_9{display:flex;align-items:center;gap:10px}._modeRight_8rc0c_15{display:flex;align-items:center;gap:8px}._overrideWarn_8rc0c_22{font-size:12px;color:var(--color-warning, #d4810a)}._previewLabel_8rc0c_27{font-size:13px;color:var(--text-secondary, #888)}._previewBox_8rc0c_33{border:1px solid var(--border-secondary, #d9d9d9);border-radius:8px;padding:8px 14px;min-height:320px;max-height:60vh;overflow:auto}._previewEmpty_8rc0c_42{font-size:13px;color:var(--text-secondary, #888)}._hint_8rc0c_47{margin-top:10px;font-size:12px;color:var(--text-secondary, #888);line-height:1.6}._dirLine_8rc0c_54{word-break:break-all}._warn_8rc0c_58{margin-top:10px;font-size:12px;color:var(--color-warning, #d4810a)}._tabRow_8rc0c_66{display:flex;align-items:flex-end;gap:6px;margin-bottom:10px;border-bottom:1px solid var(--border-secondary, #d9d9d9);flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}._tabRow_8rc0c_66::-webkit-scrollbar{display:none}._tabBtn_8rc0c_82{display:inline-flex;align-items:center;gap:5px;padding:6px 10px;font-size:12px;line-height:1.6;border:1px solid var(--border-color, #d9d9d9);border-radius:14px;background:var(--bg-main, var(--bg-container));color:var(--text-secondary);cursor:pointer;transition:all .2s;-webkit-user-select:none;user-select:none;white-space:nowrap;flex-shrink:0}._tabBtn_8rc0c_82:hover{border-color:var(--color-primary);color:var(--color-primary)}._tabBtn_8rc0c_82:focus-visible{outline:2px solid var(--color-primary);outline-offset:1px}._tabBtn_8rc0c_82._tabActive_8rc0c_110{background:var(--bg-container);color:var(--color-primary);border-color:var(--border-primary);border-bottom-color:var(--bg-container);border-radius:10px 10px 0 0;font-weight:500;margin-bottom:-1px;position:relative;z-index:1}._tabWrap_8rc0c_122{position:relative;display:inline-flex;align-items:center;flex-shrink:0}._tabTitle_8rc0c_129{max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}._tabDelete_8rc0c_137{position:absolute;top:-2px;inset-inline-end:-4px;width:14px;height:14px;border-radius:50%;background:var(--bg-elevated);border:1px solid var(--border-primary);color:var(--text-secondary);font-size:10px;line-height:1;display:flex;align-items:center;justify-content:center;cursor:pointer;opacity:0;transition:opacity .15s;z-index:2}._tabWrap_8rc0c_122:hover ._tabDelete_8rc0c_137{opacity:1}._tabDelete_8rc0c_137:hover{color:var(--color-error, #d4380d);border-color:var(--color-error, #d4380d)}@media(max-width:768px){._tabDelete_8rc0c_137{opacity:1}}._addBtn_8rc0c_169{display:inline-flex;align-items:center;align-self:center;gap:4px;padding:3px 10px;font-size:12px;line-height:1.6;margin-left:2px;border:none;border-radius:14px;background:transparent;color:var(--text-muted);cursor:pointer;transition:background .15s,color .15s;-webkit-user-select:none;user-select:none;white-space:nowrap;flex-shrink:0}._addBtn_8rc0c_169:hover{color:var(--color-primary);background:var(--bg-surface)}._addBtn_8rc0c_169:disabled{cursor:not-allowed;opacity:.5}[data-theme=light] ._addBtn_8rc0c_169{color:var(--text-secondary)}._scopeBadge_8rc0c_201{font-size:10px;line-height:1.4;padding:0 4px;border-radius:6px;background:var(--bg-surface);color:var(--text-muted)}._dirtyDot_8rc0c_211{width:6px;height:6px;border-radius:50%;background:var(--color-primary);flex-shrink:0}._helpBtn_8rc0c_220{align-self:center;margin-left:auto;width:16px;height:16px;border-radius:50%;border:1px solid var(--border-primary);background:var(--bg-elevated);color:var(--text-secondary);font-size:11px;line-height:1;display:inline-flex;align-items:center;justify-content:center;cursor:help;flex-shrink:0;-webkit-user-select:none;user-select:none}._helpBtn_8rc0c_220:hover{color:var(--color-primary);border-color:var(--color-primary)}._addPopover_8rc0c_244{display:flex;flex-direction:column;gap:8px;width:240px}._addError_8rc0c_250{font-size:12px;color:var(--color-error, #d4380d)}._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)}