larkin 0.4.27 → 0.4.29

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 (34) hide show
  1. package/dist/app/agent-cli.mjs +4 -2
  2. package/dist/app/agent-config.mjs +45 -29
  3. package/dist/app/cli.mjs +16 -6
  4. package/dist/app/local-control.mjs +16 -3
  5. package/dist/app/pi-auth-cli.mjs +227 -17
  6. package/dist/app/runtime-model-directory.mjs +3 -2
  7. package/dist/app/runtime-process.mjs +42 -5
  8. package/dist/app/setup.mjs +15 -14
  9. package/dist/dashboard/dashboard-config-controller.mjs +145 -13
  10. package/dist/dashboard/dashboard-view-model.mjs +3 -2
  11. package/dist/dashboard/web/assets/dashboard.css +1 -1
  12. package/dist/dashboard/web/assets/dashboard.js +15 -15
  13. package/dist/feishu/host-shell.cjs +68 -16
  14. package/dist/feishu/host-shell.mjs +69 -17
  15. package/dist/platform/config.cjs +67 -16
  16. package/dist/platform/config.mjs +57 -16
  17. package/dist/runtime/pi-official-auth.cjs +594 -0
  18. package/dist/runtime/pi-official-auth.mjs +53 -0
  19. package/dist/runtime/pi-provider-config.cjs +26 -3
  20. package/dist/runtime/pi-provider-config.mjs +22 -2
  21. package/dist/runtime/pi-provider-login.mjs +240 -0
  22. package/dist/runtime/runtime-adapters.mjs +24 -4
  23. package/dist/runtime/runtime-host.cjs +215 -16
  24. package/dist/runtime/runtime-host.mjs +216 -17
  25. package/dist/runtime/runtime-model-catalog.cjs +4 -3
  26. package/dist/runtime/runtime-model-catalog.mjs +4 -3
  27. package/dist/runtime/runtime-readiness.cjs +123 -3
  28. package/dist/runtime/runtime-readiness.mjs +116 -4
  29. package/dist/runtime/user-runtime.cjs +51 -0
  30. package/dist/runtime/user-runtime.mjs +41 -0
  31. package/dist/setup/bot-register.mjs +3 -3
  32. package/dist/setup/setup-agent-choice.mjs +10 -9
  33. package/dist/setup/setup-bind.mjs +2 -2
  34. package/package.json +1 -1
@@ -6,22 +6,58 @@ import { ContextPromptBuilder } from "../agent/context-prompt.mjs";
6
6
  import { createNativeRuntimeAdapter } from "../runtime/runtime-adapters.mjs";
7
7
  import { createRuntimeHost } from "../runtime/runtime-host.mjs";
8
8
  import { createAgentStateStore } from "../agent/agent-state-store.mjs";
9
- import { loadConfig, markConfigApplied, runtimeConfigSignature } from "../platform/config.mjs";
9
+ import { loadConfig, markConfigApplied, runtimeConfigSignature, withConfigMutationLock, withConfigMutationLockAsync, } from "../platform/config.mjs";
10
10
  import { traceProcessBoundary } from "../platform/process-boundary-trace.mjs";
11
11
  import { createAgentControlServer, requestSupervisorAgentUpsert } from "./local-control.mjs";
12
- import { hydrateRuntimeAgent, syncAgentProfile } from "./runtime-agent-config.mjs";
12
+ import { hydrateRuntimeAgent, syncAgentProfile, } from "./runtime-agent-config.mjs";
13
13
  import { loadTelemetryConfig } from "../platform/telemetry-config.mjs";
14
14
  import { telemetrySingleton } from "../platform/telemetry-tracing.mjs";
15
15
  import { packageVersion } from "../platform/build-info.mjs";
16
- export function loadAndSyncRuntimeAgent(env, agentId, dependencies = {}) {
16
+ function readRuntimeAgent(env, agentId, expectedSignature) {
17
17
  const loaded = loadConfig(env);
18
18
  const stored = loaded.config.agents[agentId];
19
19
  if (!stored)
20
20
  throw new Error(`Agent ${agentId} 不存在于 canonical config`);
21
+ if (expectedSignature) {
22
+ const current = runtimeConfigSignature(loaded.config, agentId);
23
+ if (current !== expectedSignature) {
24
+ throw new Error("配置在 apply 期间发生变化;未热加载已切换的 Agent");
25
+ }
26
+ }
27
+ return { loaded, stored };
28
+ }
29
+ function hydrateAndSyncRuntimeAgent(env, loaded, stored, dependencies) {
21
30
  const agent = hydrateRuntimeAgent(loaded.configDir, stored);
22
31
  syncAgentProfile(agent, { ...env, LARKIN_CONFIG_DIR: loaded.configDir }, dependencies);
23
32
  return agent;
24
33
  }
34
+ export function loadAndSyncRuntimeAgent(env, agentId, dependencies = {}, options = {}) {
35
+ const snapshot = readRuntimeAgent(env, agentId, options.expectedSignature);
36
+ options.afterCanonicalValidate?.();
37
+ if (!options.expectedSignature) {
38
+ return hydrateAndSyncRuntimeAgent(env, snapshot.loaded, snapshot.stored, dependencies);
39
+ }
40
+ return withConfigMutationLock(env, () => {
41
+ const locked = readRuntimeAgent(env, agentId, options.expectedSignature);
42
+ return hydrateAndSyncRuntimeAgent(env, locked.loaded, locked.stored, dependencies);
43
+ });
44
+ }
45
+ /** 签名校验贯穿实际 Host upsert;并发 config mutation 不能热挂载已失效的 snapshot。 */
46
+ export async function applyRuntimeAgentUpsert(env, agentId, dependencies = {}, options = {}, upsert = () => undefined) {
47
+ const snapshot = readRuntimeAgent(env, agentId, options.expectedSignature);
48
+ options.afterCanonicalValidate?.();
49
+ if (!options.expectedSignature) {
50
+ const agent = hydrateAndSyncRuntimeAgent(env, snapshot.loaded, snapshot.stored, dependencies);
51
+ await upsert(agent);
52
+ return agent;
53
+ }
54
+ return withConfigMutationLockAsync(env, async () => {
55
+ const locked = readRuntimeAgent(env, agentId, options.expectedSignature);
56
+ const agent = hydrateAndSyncRuntimeAgent(env, locked.loaded, locked.stored, dependencies);
57
+ await upsert(agent);
58
+ return agent;
59
+ });
60
+ }
25
61
  export async function markConfigAppliedAfterRuntimeReady(env, runningAgents, runtimeReady) {
26
62
  await runtimeReady;
27
63
  try {
@@ -100,10 +136,11 @@ export async function main(env = process.env, overrides = {}) {
100
136
  larkinHome: env.LARKIN_HOME,
101
137
  authorityToken: env.LARKIN_CONTROL_AUTHORIZATION,
102
138
  async upsert(request) {
103
- const agent = loadAndSyncRuntimeAgent(env, request.agentId);
139
+ const agent = await applyRuntimeAgentUpsert(env, request.agentId, {}, {
140
+ ...(request.expectedSignature ? { expectedSignature: request.expectedSignature } : {}),
141
+ }, (candidate) => hostShell.upsertAgent(candidate));
104
142
  // Only the selected profile is synchronized; active profiles and their directory
105
143
  // are never quarantined or rebuilt during hot attach.
106
- await hostShell.upsertAgent(agent);
107
144
  const tracked = await requestSupervisorAgentUpsert({
108
145
  larkinHome: env.LARKIN_HOME, agentId: agent.agentId, operationId: request.operationId,
109
146
  });
@@ -8,6 +8,7 @@ import readline from "node:readline/promises";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import * as larkinConfig from "../platform/config.mjs";
10
10
  import { PI_PROVIDER_PRESETS } from "../runtime/pi-provider-config.mjs";
11
+ import { fromUserRuntime, isUserRuntime } from "../runtime/user-runtime.mjs";
11
12
  const PI_PROVIDER_IDS = PI_PROVIDER_PRESETS.map((p) => p.id).join(" | ") + " | custom";
12
13
  import { acquireProcessLock, readProcessState } from "../platform/process-state.mjs";
13
14
  import { requestAgentUpsert } from "./local-control.mjs";
@@ -35,18 +36,18 @@ if (has("--comment-subscription")) {
35
36
  // 默认应用级评论订阅:平台验证的应用订阅让该应用可见文档的全部评论进入 Agent Inbox。
36
37
  const commentSubscription = "application";
37
38
  const OPT = { runtime: flag("--runtime") || null, commentSubscription, start: true, help: has("--help") || has("-h"), nonInteractive: !Boolean(process.stdin.isTTY) };
38
- // runtime 枚举归一:builtin-pi(默认)/ external-pi / codex / claude → 内部 (runtime, distribution)
39
- const RUNTIME_ENUM = ["builtin-pi", "external-pi", "codex", "claude"];
39
+ // runtime 枚举归一:builtin-pi(默认)/ pi / codex / claude → 内部 (runtime, distribution)
40
+ const RUNTIME_ENUM = ["builtin-pi", "pi", "codex", "claude"];
40
41
  function normalizeRuntime(value) {
41
- const chosen = (value || "builtin-pi");
42
- if (!RUNTIME_ENUM.includes(chosen)) {
42
+ const raw = value || "builtin-pi";
43
+ if (raw === "external-pi") {
44
+ console.error("external-pi 已改名为 pi");
45
+ return fromUserRuntime("pi");
46
+ }
47
+ if (!isUserRuntime(raw) || !RUNTIME_ENUM.includes(raw)) {
43
48
  die(`未知 runtime \`${value}\`;可选:${RUNTIME_ENUM.join(" | ")}`);
44
49
  }
45
- if (chosen === "builtin-pi")
46
- return { runtime: "pi", distribution: "builtin" };
47
- if (chosen === "external-pi")
48
- return { runtime: "pi", distribution: "external" };
49
- return { runtime: chosen };
50
+ return fromUserRuntime(raw);
50
51
  }
51
52
  const normalizedRuntime = normalizeRuntime(OPT.runtime);
52
53
  // 参数化路径:显式 --runtime、无 TTY(Agent 驱动)、或任何 provider 参数。
@@ -59,12 +60,12 @@ if (!OPT.help && parameterized && normalizedRuntime.distribution === "builtin")
59
60
  const b = flag("--base-url");
60
61
  const k = flag("--api-key");
61
62
  if (!p && !b)
62
- die(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_IDS})或 --base-url;或 --runtime external-pi 用已有 pi`);
63
+ die(`builtin-pi 需要 --provider <id>(${PI_PROVIDER_IDS})或 --base-url;或 --runtime pi 用已有 pi`);
63
64
  if (!k)
64
- die("builtin-pi 需要 --api-key;或 --runtime external-pi 用已有登录");
65
+ die("builtin-pi 需要 --api-key;或 --runtime pi 用已有登录");
65
66
  }
66
67
  if (!OPT.help && normalizedRuntime.distribution === "external" && (flag("--provider") || flag("--api-key") || flag("--base-url"))) {
67
- die("external-pi 使用已有 pi 环境,不接受 --provider/--api-key/--base-url");
68
+ die("pi 使用已有 pi 环境,不接受 --provider/--api-key/--base-url");
68
69
  }
69
70
  if (OPT.help) {
70
71
  say(`larkin setup — Create or connect a Feishu (Lark) bot, then configure and attach its Agent
@@ -74,7 +75,7 @@ Usage:
74
75
  larkin setup --provider <id> --api-key <key> 指定内置 Pi provider(无需交互)
75
76
 
76
77
  Options:
77
- --runtime <runtime> Agent runtime 枚举:builtin-pi(默认,内置 Pi)| external-pi | codex | claude
78
+ --runtime <runtime> Agent runtime 枚举:builtin-pi(默认,内置 Pi)| pi | codex | claude
78
79
  --provider <id> 内置 Pi provider:deepseek | kimi | minimax | zhipu | openai |
79
80
  anthropic | gemini | groq | cerebras | xai | fireworks |
80
81
  together | mistral | openrouter | kimi-coding | qwen-cn | custom
@@ -96,7 +97,7 @@ daemon + dashboard supervisor used by larkin start.
96
97
 
97
98
  Interactive terminals without flags keep the guided flow (browser bot selection + runtime choice).
98
99
  Non-interactive (Agent-driven) runs default to builtin-pi and require --provider/--api-key (or
99
- --runtime external-pi to reuse an existing pi login).`);
100
+ --runtime pi to reuse an existing pi login).`);
100
101
  process.exit(0);
101
102
  }
102
103
  function runForeground(mode, args) {
@@ -8,6 +8,9 @@ import { discoverCodexModelCatalog } from "../runtime/codex-model-catalog.mjs";
8
8
  import { discoverPiModelCatalog } from "../runtime/pi-model-catalog.mjs";
9
9
  import { managedOfficialLarkCli } from "../app/agent-lark-cli-workspace.mjs";
10
10
  import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
11
+ import { RUNTIME_OPTIONS, piCatalogDistributionForUserRuntime, toUserRuntime } from "../runtime/user-runtime.mjs";
12
+ import { configureBuiltinPiProvider, listBuiltinPiProviderCatalog, logoutBuiltinPiProvider, sanitizeProviderLoginError, } from "../runtime/pi-provider-login.mjs";
13
+ import { createOfficialPiCredentialRuntime, officialPiAuthStatus, } from "../runtime/pi-official-auth.mjs";
11
14
  function pruneCache(cache, expired, maxEntries) {
12
15
  for (const [key, value] of cache)
13
16
  if (expired(value))
@@ -169,9 +172,38 @@ export function createPiModelDirectoryResolver(options = {}) {
169
172
  const cache = new Map();
170
173
  const failures = new Map();
171
174
  const inFlight = new Map();
175
+ const generations = new Map();
176
+ // 单调序号:targeted 后再 global 时,in-flight 的 started 不能与新 generation 相等。
177
+ let seq = 0;
178
+ let baseline = 0;
179
+ const generationOf = (agentId) => Math.max(baseline, generations.get(agentId) ?? 0);
180
+ const dropPrefixed = (store, agentId) => {
181
+ if (!agentId) {
182
+ store.clear();
183
+ return;
184
+ }
185
+ const prefix = `${agentId}\u0000`;
186
+ for (const key of store.keys())
187
+ if (key.startsWith(prefix))
188
+ store.delete(key);
189
+ };
172
190
  return {
191
+ invalidate(agentId) {
192
+ dropPrefixed(cache, agentId);
193
+ dropPrefixed(failures, agentId);
194
+ dropPrefixed(inFlight, agentId);
195
+ seq += 1;
196
+ if (!agentId) {
197
+ baseline = seq;
198
+ generations.clear();
199
+ }
200
+ else {
201
+ generations.set(agentId, seq);
202
+ }
203
+ },
173
204
  async resolve(input) {
174
205
  const key = [input.agentId, input.cwd, input.agentDir, input.command ?? "", ...(input.commandArgs ?? [])].join("\u0000");
206
+ const started = generationOf(input.agentId);
175
207
  pruneCache(cache, (entry) => now() >= entry.expiresAt, 64);
176
208
  pruneCache(failures, (entry) => now() >= entry.expiresAt, 64);
177
209
  const cached = cache.get(key);
@@ -195,17 +227,23 @@ export function createPiModelDirectoryResolver(options = {}) {
195
227
  id, label, ...(contextWindow ? { contextWindow } : {}), supportedReasoningEfforts, ...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
196
228
  })),
197
229
  ];
198
- failures.delete(key);
199
- cache.set(key, { expiresAt: now() + ttlMs, value: models });
230
+ if (generationOf(input.agentId) === started) {
231
+ failures.delete(key);
232
+ cache.set(key, { expiresAt: now() + ttlMs, value: models });
233
+ }
200
234
  return models;
201
235
  }
202
236
  catch (error) {
203
- failures.set(key, { expiresAt: now() + negativeTtlMs });
237
+ if (generationOf(input.agentId) === started)
238
+ failures.set(key, { expiresAt: now() + negativeTtlMs });
204
239
  throw error;
205
240
  }
206
241
  })();
207
242
  inFlight.set(key, pending);
208
- void pending.finally(() => inFlight.delete(key)).catch(() => undefined);
243
+ void pending.finally(() => {
244
+ if (inFlight.get(key) === pending)
245
+ inFlight.delete(key);
246
+ }).catch(() => undefined);
209
247
  }
210
248
  return await pending;
211
249
  },
@@ -382,13 +420,13 @@ function dashboardMutation(value) {
382
420
  return { kind: operation, agentId, model: String(value.model || "") };
383
421
  return { kind: "set-agent-effort", agentId, effort: value.effort === null || value.effort === "default" ? null : String(value.effort || "") };
384
422
  }
385
- export function createDashboardConfigController({ csrfCapability, env = process.env, requestUpsert = requestAgentUpsert, chatDirectoryResolver = createChatDirectoryResolver(), claudeModelDirectoryResolver = createClaudeModelDirectoryResolver(), codexModelDirectoryResolver = createCodexModelDirectoryResolver(), piModelDirectoryResolver = createPiModelDirectoryResolver(), }) {
386
- const resolvePiModelDirectory = async (agentId) => {
423
+ export function createDashboardConfigController({ csrfCapability, env = process.env, requestUpsert = requestAgentUpsert, chatDirectoryResolver = createChatDirectoryResolver(), claudeModelDirectoryResolver = createClaudeModelDirectoryResolver(), codexModelDirectoryResolver = createCodexModelDirectoryResolver(), piModelDirectoryResolver = createPiModelDirectoryResolver(), configureProvider = configureBuiltinPiProvider, logoutProvider = logoutBuiltinPiProvider, listProviderCatalog = listBuiltinPiProviderCatalog, loadProviderStatus = async (configDir, agentId) => officialPiAuthStatus(await createOfficialPiCredentialRuntime(configDir, agentId)), }) {
424
+ const resolvePiModelDirectory = async (agentId, userRuntime) => {
387
425
  const { config, configDir } = loadConfig(env);
388
426
  const agent = config.agents[agentId];
389
427
  if (!agent)
390
428
  throw new Error("unknown agent");
391
- const catalogCommand = piCatalogCommandSpec(agent.piDistribution, env);
429
+ const catalogCommand = piCatalogCommandSpec(piCatalogDistributionForUserRuntime(userRuntime), env);
392
430
  return await piModelDirectoryResolver.resolve({
393
431
  agentId,
394
432
  cwd: agent.workspaceDir,
@@ -402,8 +440,8 @@ export function createDashboardConfigController({ csrfCapability, env = process.
402
440
  const agent = config.agents[agentId];
403
441
  if (!agent)
404
442
  throw new Error("unknown agent");
405
- if (runtime === "pi")
406
- return await resolvePiModelDirectory(agentId);
443
+ if (runtime === "pi" || runtime === "builtin-pi")
444
+ return await resolvePiModelDirectory(agentId, runtime);
407
445
  if (runtime === "codex")
408
446
  return await codexModelDirectoryResolver.resolve({ agentId, cwd: agent.workspaceDir, env });
409
447
  if (runtime === "claude")
@@ -420,7 +458,9 @@ export function createDashboardConfigController({ csrfCapability, env = process.
420
458
  const agent = config.agents[mutation.agentId];
421
459
  if (!agent)
422
460
  throw new Error("unknown agent");
423
- const runtime = mutation.kind === "set-agent-runtime" ? mutation.runtime : agent.runtime;
461
+ const runtime = mutation.kind === "set-agent-runtime"
462
+ ? mutation.runtime
463
+ : toUserRuntime(agent.runtime, agent.piDistribution);
424
464
  const model = mutation.kind === "set-agent-runtime" ? mutation.model || "default"
425
465
  : mutation.kind === "set-agent-model" ? mutation.model : agent.model;
426
466
  if (mutation.kind === "set-agent-effort" && mutation.effort === null)
@@ -440,18 +480,22 @@ export function createDashboardConfigController({ csrfCapability, env = process.
440
480
  if (requestUrl.pathname === "/api/config" && req.method === "GET") {
441
481
  try {
442
482
  assertPrivateReadRequest(req, csrfCapability);
443
- json(res, 200, { ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined), runtimeModels: loadRuntimeModels() });
483
+ json(res, 200, {
484
+ ...await sanitizedView(env, chatDirectoryResolver, requestUrl.searchParams.get("agent") || undefined, requestUrl.searchParams.get("chat") || undefined),
485
+ runtimeModels: loadRuntimeModels(),
486
+ runtimeOptions: [...RUNTIME_OPTIONS],
487
+ });
444
488
  }
445
489
  catch (error) {
446
490
  json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "configuration unavailable" });
447
491
  }
448
492
  return true;
449
493
  }
450
- if (requestUrl.pathname === "/api/models/pi" && req.method === "GET") {
494
+ if ((requestUrl.pathname === "/api/models/pi" || requestUrl.pathname === "/api/models/builtin-pi") && req.method === "GET") {
451
495
  try {
452
496
  assertPrivateReadRequest(req, csrfCapability);
453
497
  const agentId = requestUrl.searchParams.get("agent") || "";
454
- const models = await resolvePiModelDirectory(agentId);
498
+ const models = await resolvePiModelDirectory(agentId, requestUrl.pathname === "/api/models/builtin-pi" ? "builtin-pi" : "pi");
455
499
  json(res, 200, { models });
456
500
  }
457
501
  catch (error) {
@@ -504,6 +548,94 @@ export function createDashboardConfigController({ csrfCapability, env = process.
504
548
  }
505
549
  return true;
506
550
  }
551
+ if (requestUrl.pathname === "/api/pi-auth/providers" && req.method === "GET") {
552
+ try {
553
+ assertPrivateReadRequest(req, csrfCapability);
554
+ json(res, 200, { providers: listProviderCatalog() });
555
+ }
556
+ catch (error) {
557
+ json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "provider catalog unavailable" });
558
+ }
559
+ return true;
560
+ }
561
+ if (requestUrl.pathname === "/api/pi-auth/status" && req.method === "GET") {
562
+ try {
563
+ assertPrivateReadRequest(req, csrfCapability);
564
+ const agentId = requestUrl.searchParams.get("agent") || "";
565
+ const { config, configDir } = loadConfig(env);
566
+ const agent = config.agents[agentId];
567
+ if (!agent || agent.runtime !== "pi" || agent.piDistribution !== "builtin")
568
+ throw new Error("unknown agent");
569
+ const credentials = await loadProviderStatus(configDir, agentId);
570
+ json(res, 200, { agentId, model: agent.model, credentials });
571
+ }
572
+ catch (error) {
573
+ json(res, error instanceof Error && /host|capability/.test(error.message) ? 403 : 500, { error: "provider status unavailable" });
574
+ }
575
+ return true;
576
+ }
577
+ if (requestUrl.pathname === "/api/pi-auth/login" && req.method === "POST") {
578
+ let secret = "";
579
+ try {
580
+ assertWriteRequest(req, csrfCapability);
581
+ const body = await boundedJson(req);
582
+ const allowed = ["agentId", "preset", "apiKey", "model", "baseUrl"];
583
+ if (Object.keys(body).some((key) => !allowed.includes(key)))
584
+ throw new Error("unsupported field");
585
+ const agentId = String(body.agentId || "");
586
+ const preset = String(body.preset || "");
587
+ const apiKey = typeof body.apiKey === "string" ? body.apiKey : "";
588
+ secret = apiKey;
589
+ if (!/^cli_[A-Za-z0-9]+$/.test(agentId) || !preset)
590
+ throw new Error("invalid login request");
591
+ const { config } = loadConfig(env);
592
+ const agent = config.agents[agentId];
593
+ if (!agent || agent.runtime !== "pi" || agent.piDistribution !== "builtin")
594
+ throw new Error("Agent is not builtin Pi");
595
+ const result = await configureProvider({
596
+ agentId,
597
+ preset,
598
+ apiKey,
599
+ ...(typeof body.model === "string" ? { model: body.model } : {}),
600
+ ...(typeof body.baseUrl === "string" ? { baseUrl: body.baseUrl } : {}),
601
+ env,
602
+ });
603
+ piModelDirectoryResolver.invalidate?.(agentId);
604
+ json(res, 200, {
605
+ ok: true,
606
+ agentId: result.agentId,
607
+ provider: result.provider,
608
+ preset: result.preset,
609
+ model: result.model,
610
+ credentialType: result.credentialType,
611
+ applyState: result.applyState,
612
+ ...(result.applyError ? { applyError: sanitizeProviderLoginError(result.applyError, secret || undefined) } : {}),
613
+ });
614
+ }
615
+ catch (error) {
616
+ json(res, 400, { error: sanitizeProviderLoginError(error, secret || undefined) });
617
+ }
618
+ return true;
619
+ }
620
+ if (requestUrl.pathname === "/api/pi-auth/logout" && req.method === "POST") {
621
+ try {
622
+ assertWriteRequest(req, csrfCapability);
623
+ const body = await boundedJson(req);
624
+ if (Object.keys(body).sort().join(",") !== "agentId,provider")
625
+ throw new Error("invalid logout request");
626
+ const agentId = String(body.agentId || "");
627
+ const provider = String(body.provider || "");
628
+ if (!/^cli_[A-Za-z0-9]+$/.test(agentId))
629
+ throw new Error("invalid logout request");
630
+ const result = await logoutProvider({ agentId, providerId: provider, env });
631
+ piModelDirectoryResolver.invalidate?.(agentId);
632
+ json(res, 200, { ok: true, agentId: result.agentId, provider: result.provider });
633
+ }
634
+ catch (error) {
635
+ json(res, 400, { error: sanitizeProviderLoginError(error) });
636
+ }
637
+ return true;
638
+ }
507
639
  if (requestUrl.pathname === "/api/config/apply" && req.method === "POST") {
508
640
  try {
509
641
  assertWriteRequest(req, csrfCapability);
@@ -13,6 +13,7 @@ import { collectWorkspaceEntry as collectTypedWorkspaceEntry } from "./dashboard
13
13
  import { buildFingerprint, packageVersion } from "../platform/build-info.mjs";
14
14
  import * as larkinConfig from "../platform/config.mjs";
15
15
  import { ownedPiCatalogAgentDir, piCatalogCommandSpec } from "../runtime/pi-provider-config.mjs";
16
+ import { toUserRuntime } from "../runtime/user-runtime.mjs";
16
17
  const HERE = path.dirname(fileURLToPath(import.meta.url));
17
18
  const ROOT = path.resolve(HERE, "../..");
18
19
  export const PACKAGE_VERSION = packageVersion(ROOT);
@@ -502,7 +503,7 @@ async function collectAgentStatus(a, configDir, daemonStartedAt, piModelResolver
502
503
  agentId: a.agentId,
503
504
  name: a.name,
504
505
  displayName: (botIdentity && botIdentity.name) || a.name,
505
- runtime: a.runtime,
506
+ runtime: toUserRuntime(a.runtime, a.piDistribution),
506
507
  model: status.session?.runtime === a.runtime && status.session?.model ? String(status.session.model) : a.model,
507
508
  effort: status.session?.runtime === a.runtime && status.session?.reasoningEffort ? String(status.session.reasoningEffort) : a.effort || null,
508
509
  runtimeReadiness,
@@ -526,7 +527,7 @@ async function collectAgentStatus(a, configDir, daemonStartedAt, piModelResolver
526
527
  sessions: agentState.sessions || {},
527
528
  session: sessionId ? {
528
529
  id: sessionId,
529
- runtime: a.runtime,
530
+ runtime: toUserRuntime(a.runtime, a.piDistribution),
530
531
  startedAt: usage.startedAt || (status.session?.id === sessionId ? status.session.startedAt : null) || null,
531
532
  ageSec: ageSec(usage.startedAt || (status.session?.id === sessionId ? status.session.startedAt : null)),
532
533
  lastTurnAt: status.session?.lastTurnAt || null,
@@ -1,3 +1,3 @@
1
1
  /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
- @layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.hidden{display:none}}:root{--paper:#f4f0e8;--panel:#fbf8f1;--ink:#201c18;--subtle:#6e675e;--faint:#989084;--line:#ddd5c7;--accent:#a3402c;--accent-soft:#f2e3dc;--success:#2d6a4f;--warning:#9a6700;--danger:#a3402c;--shadow:0 14px 44px #33281f14;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;font-synthesis:none;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--paper:#181513;--panel:#211d1a;--ink:#eee7dc;--subtle:#aaa195;--faint:#766d63;--line:#37302a;--accent:#d17862;--accent-soft:#3a2420;--success:#75b798;--warning:#e0ad55;--shadow:0 14px 44px #0000003d;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*{box-sizing:border-box}html,body,#root{min-height:100%}html{background:var(--paper);overflow-x:clip}body{background:var(--paper);color:var(--ink);margin:0;overflow-x:clip}button,input,select{font:inherit}button,select{cursor:pointer}button:disabled,select:disabled,input:disabled{cursor:not-allowed;opacity:.55}[hidden]{display:none!important}:focus-visible{outline:3px solid var(--accent)}@supports (color:color-mix(in lab, red, red)){:focus-visible{outline:3px solid color-mix(in srgb, var(--accent) 40%, transparent)}}:focus-visible{outline-offset:2px}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.app-shell{grid-template-columns:292px minmax(0,1fr);min-height:100vh;display:grid}.app-shell.workspace-app-active{height:100dvh;min-height:0;overflow:hidden}.desktop-sidebar{border-right:1px solid var(--line);background:var(--panel);flex-direction:column;min-width:0;height:100vh;display:flex;position:sticky;top:0}@supports (color:color-mix(in lab, red, red)){.desktop-sidebar{background:color-mix(in srgb, var(--panel) 94%, transparent)}}.brand{border-bottom:1px solid var(--line);align-items:center;gap:12px;height:78px;padding:18px 22px;display:flex}.brand img{width:38px;height:38px}.brand strong{color:var(--accent);letter-spacing:-.03em;font:600 22px Georgia,serif}.brand-version{color:var(--faint);letter-spacing:0;white-space:nowrap;font:600 10px ui-monospace,monospace}.desktop-sidebar footer{border-top:1px solid var(--line);color:var(--faint);padding:13px 18px;font-size:11px}.sidebar-inner{flex-direction:column;flex:1;min-height:0;padding:14px 12px;display:flex}.agent-search,.table-search{border:1px solid var(--line);background:var(--paper);color:var(--faint);border-radius:9px;align-items:center;gap:8px;display:flex}.agent-search{margin-bottom:12px;padding:9px 11px}.agent-search input,.table-search input{width:100%;min-width:0;color:var(--ink);background:0 0;border:0;outline:0}.agent-list{align-content:start;gap:6px;min-height:0;display:grid;overflow-y:auto}.agent-list-item{width:100%;min-width:0;color:var(--subtle);text-align:left;background:0 0;border:1px solid #0000;border-radius:11px;grid-template-columns:42px minmax(0,1fr) 16px;align-items:center;gap:10px;padding:10px;display:grid}.agent-list-item:hover{background:var(--ink)}@supports (color:color-mix(in lab, red, red)){.agent-list-item:hover{background:color-mix(in srgb, var(--ink) 4%, transparent)}}.agent-list-item:hover{color:var(--ink)}.agent-list-item.selected{border-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.agent-list-item.selected{border-color:color-mix(in srgb, var(--accent) 42%, var(--line))}}.agent-list-item.selected{background:var(--accent-soft);color:var(--ink)}.agent-list-copy{gap:1px;min-width:0;display:grid}.agent-list-copy strong,.agent-list-copy small,.status-line{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.agent-list-copy strong{font-size:13px}.agent-list-copy small{color:var(--faint);font-size:10.5px}.status-line{color:var(--subtle);align-items:center;gap:4px;font-size:10px;display:flex}.agent-avatar{border:1px solid var(--line);background:var(--accent);border-radius:50%;flex:none;place-items:center;width:42px;height:42px;display:grid;position:relative;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.agent-avatar{background:color-mix(in srgb, var(--accent) 12%, var(--panel))}}.agent-avatar{color:var(--accent);font:700 13px Georgia,serif}.agent-avatar img{object-fit:cover;width:100%;height:100%;display:block}.danger{color:var(--danger)}.workspace-shell{flex-direction:column;min-width:0;min-height:100vh;display:flex}.workspace-shell.workspace-route-active{height:100dvh;min-height:0;overflow:hidden}.workspace-route-active>.app-topbar,.workspace-route-active>.agent-header,.workspace-route-active>.agent-tabs,.workspace-route-active>.stale-banner,.workspace-route-active>.notice{flex:none}.app-topbar{z-index:20;border-bottom:1px solid var(--line);background:var(--paper);justify-content:space-between;align-items:center;gap:16px;height:64px;padding:0 28px;display:flex;position:sticky;top:0}@supports (color:color-mix(in lab, red, red)){.app-topbar{background:color-mix(in srgb, var(--paper) 94%, transparent)}}.app-topbar{-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.app-topbar>div{align-items:center;gap:10px;min-width:0;display:flex}.app-topbar>div>strong{color:var(--accent);font:600 19px Georgia,serif;display:none}.app-topbar>div>span{color:var(--subtle);font-size:12px}.mobile-menu{display:none!important}.ui-button{border:1px solid var(--line);background:var(--panel);min-height:34px;color:var(--ink);border-radius:8px;justify-content:center;align-items:center;gap:7px;padding:7px 11px;font-size:12px;font-weight:600;display:inline-flex}.ui-button:hover{border-color:var(--accent);background:var(--accent-soft)}.ui-button.primary{border-color:var(--accent);background:var(--accent);color:#fff}.ui-button.icon-button{width:36px;padding:0}.ui-badge{border:1px solid var(--line);color:var(--subtle);background:var(--ink);border-radius:999px;align-items:center;padding:2px 8px;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.ui-badge{background:color-mix(in srgb, var(--ink) 3%, var(--panel))}}.ui-badge{white-space:nowrap;font-size:10.5px;font-weight:600}.ui-badge.success{border-color:var(--success)}@supports (color:color-mix(in lab, red, red)){.ui-badge.success{border-color:color-mix(in srgb, var(--success) 55%, var(--line))}}.ui-badge.success{color:var(--success)}.ui-badge.warning{border-color:var(--warning)}@supports (color:color-mix(in lab, red, red)){.ui-badge.warning{border-color:color-mix(in srgb, var(--warning) 55%, var(--line))}}.ui-badge.warning{color:var(--warning)}.ui-badge.accent,.ui-badge.danger-badge{border-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui-badge.accent,.ui-badge.danger-badge{border-color:color-mix(in srgb, var(--accent) 55%, var(--line))}}.ui-badge.accent,.ui-badge.danger-badge{color:var(--accent)}.stale-banner,.notice{border:1px solid var(--warning);align-items:center;gap:8px;margin:16px 28px 0;display:flex}@supports (color:color-mix(in lab, red, red)){.stale-banner,.notice{border:1px solid color-mix(in srgb, var(--warning) 50%, var(--line))}}.stale-banner,.notice{background:var(--warning);border-radius:9px;padding:8px 11px}@supports (color:color-mix(in lab, red, red)){.stale-banner,.notice{background:color-mix(in srgb, var(--warning) 7%, var(--panel))}}.stale-banner,.notice{color:var(--warning);font-size:12px}.stale-banner .ui-button,.notice .ui-button{min-height:28px;margin-left:auto;padding-block:4px}.notice{border-color:var(--line);background:var(--panel);color:var(--subtle)}.agent-header{z-index:15;background:var(--paper);justify-content:space-between;align-items:center;gap:28px;min-width:0;padding:24px 30px 18px;display:flex;position:sticky;top:64px}@supports (color:color-mix(in lab, red, red)){.agent-header{background:color-mix(in srgb, var(--paper) 96%, transparent)}}.agent-header{-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.agent-title{align-items:center;gap:13px;min-width:0;display:flex}.agent-title .agent-avatar{width:52px;height:52px}.agent-title div{min-width:0}.agent-title p{text-overflow:ellipsis;color:var(--faint);margin:0;font:10px ui-monospace,monospace;overflow:hidden}.agent-title h1{text-overflow:ellipsis;white-space:nowrap;letter-spacing:-.025em;margin:1px 0 0;font:600 clamp(23px,3vw,34px) Georgia,serif;overflow:hidden}.agent-header-summary{min-width:0;max-width:62%;color:var(--subtle);flex-wrap:wrap;justify-content:flex-end;gap:6px 14px;font-size:11.5px;display:flex}.agent-tabs{z-index:14;border-bottom:1px solid var(--line);background:var(--paper);gap:2px;padding:0 30px;display:flex;position:sticky;top:158px}@supports (color:color-mix(in lab, red, red)){.agent-tabs{background:color-mix(in srgb, var(--paper) 96%, transparent)}}.agent-tabs{scrollbar-width:thin;overflow-x:auto}.agent-tabs button{color:var(--subtle);background:0 0;border:0;border-bottom:2px solid #0000;flex:none;align-items:center;gap:7px;padding:11px 14px 9px;font-size:12px;font-weight:600;display:inline-flex}.agent-tabs button svg{width:14px;height:14px}.agent-tabs button:hover{color:var(--ink)}.agent-tabs button.active{color:var(--accent);border-bottom-color:var(--accent)}.agent-content{min-width:0;padding:24px 30px 64px}.agent-content>section{min-width:0}.agent-content.workspace-active{flex:1 1 0;height:auto;min-height:0;padding-bottom:24px;display:flex;overflow:hidden}.workspace-tab-panel{flex:1;min-height:0;display:flex}.overview-page{gap:28px;display:grid}.metrics-band{border-block:1px solid var(--line);grid-template-columns:repeat(4,minmax(0,1fr));min-width:0;display:grid}.metric-stat{flex-direction:column;min-width:0;min-height:126px;padding:16px 14px;display:flex}.metric-stat:not(:last-child){border-right:1px solid var(--line)}.metric-stat{border-bottom:1px solid var(--line)}.metric-stat:nth-child(4n){border-right:0}.metric-stat:nth-last-child(-n+3){border-bottom:0}.metric-stat>span{color:var(--faint);letter-spacing:.08em;text-transform:uppercase;margin-top:10px;font-size:9.5px;font-weight:700}.metric-stat>strong{text-overflow:ellipsis;margin-top:3px;font:600 18px Georgia,serif;overflow:hidden}.metric-stat>small{color:var(--subtle);margin-top:auto;font-size:10px}.metric-icon{width:20px;height:20px;color:var(--accent);place-items:center;display:grid}.metric-icon svg{width:15px;height:15px}.overview-sections{grid-template-columns:minmax(0,3fr) minmax(220px,1fr);gap:28px;min-width:0;display:grid}.content-section,.config-section,.group-policy-section,.logs-section{border-block:1px solid var(--line);min-width:0;padding:18px 0}.content-section h3,.config-section h3,.group-policy-section h3,.logs-section h3{margin:0;font:600 17px Georgia,serif}.section-heading{justify-content:space-between;align-items:flex-start;gap:16px;min-width:0;margin-bottom:14px;display:flex}.section-heading p{color:var(--subtle);margin:2px 0 0;font-size:11px}.facts{gap:0;margin:14px 0 0;display:grid}.facts div{border-top:1px solid var(--line);justify-content:space-between;align-items:baseline;gap:16px;padding:9px 0;display:flex}.facts dt{color:var(--subtle);font-size:11px}.facts dd{text-overflow:ellipsis;min-width:0;margin:0;font-size:12px;font-weight:600;overflow:hidden}.timeline{gap:0;margin:0;padding:0;list-style:none;display:grid}.timeline li{grid-template-columns:14px minmax(0,1fr);gap:10px;padding:8px 0;display:grid;position:relative}.timeline li:not(:last-child):before{content:"";background:var(--line);width:1px;position:absolute;top:21px;bottom:-7px;left:5px}.timeline-dot{z-index:1;border:2px solid var(--panel);background:var(--subtle);width:11px;height:11px;box-shadow:0 0 0 1px var(--line);border-radius:50%;margin-top:4px;position:relative}.timeline-dot.state-active{background:var(--warning)}.timeline-dot.state-idle{background:var(--success)}.timeline-dot.state-deliver{background:#3977b8}.timeline-dot.state-error{background:var(--danger)}.timeline-dot.state-unknown{background:var(--faint)}.timeline strong{font-size:11.5px}.timeline p{color:var(--subtle);overflow-wrap:anywhere;margin:1px 0;font-size:11px}.timeline time,.error-stack time{color:var(--faint);font-size:9.5px}.conversation-list,.reminder-list{border-top:1px solid var(--line);gap:0;max-width:900px;display:grid}.conversation-item,.reminder-list article{border:0;border-bottom:1px solid var(--line);background:0 0;padding:14px 10px}.conversation-item.outbound{border-left:3px solid var(--accent)}.conversation-item.inbound{border-left:3px solid var(--success)}.conversation-item.listening{border-left-color:var(--warning)}.conversation-item header{justify-content:space-between;gap:16px;display:flex}.conversation-item header>div{align-items:center;gap:8px;display:flex}.conversation-item header strong{font-size:12px}.conversation-item time,.conversation-item small{color:var(--faint);font-size:10px}.conversation-item p{white-space:pre-wrap;overflow-wrap:anywhere;margin:7px 0 0;font:13px/1.65 Georgia,serif}.reminder-list article{grid-template-columns:20px minmax(0,1fr) auto;align-items:center;gap:10px;display:grid}.reminder-list article.status-scheduled,.reminder-list article.status-pending{border-left:3px solid var(--warning)}.reminder-list article.status-fired{border-left:3px solid var(--success)}.reminder-list article.status-canceled{border-left:3px solid var(--faint)}.reminder-list article.status-failed{border-left:3px solid var(--danger)}.reminder-list article.done{opacity:.7;text-decoration:line-through}.reminder-list strong{font-size:12px}.reminder-list p{color:var(--subtle);margin:2px 0 0;font-size:10.5px}.configuration-page{gap:18px;display:grid}.cli-guidance{border-left:2px solid var(--accent);color:var(--subtle);margin:0 0 14px;padding:6px 10px;font-size:11px;line-height:1.55}.cli-guidance code{color:var(--ink);overflow-wrap:anywhere;font:600 10.5px ui-monospace,monospace}.config-grid{grid-template-columns:repeat(4,minmax(120px,1fr));gap:12px;display:grid}.config-grid label,.settings-form label,.chat-add label{min-width:0;color:var(--subtle);gap:5px;font-size:11px;font-weight:600;display:grid}.config-grid select,.config-grid input,.settings-form select,.chat-add select,.chat-add input,.policy-table select{border:1px solid var(--line);background:var(--paper);width:100%;min-width:0;min-height:37px;color:var(--ink);border-radius:8px;padding:7px 9px;font-size:12px}.config-grid input:disabled{color:var(--subtle)}.form-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:16px;display:flex}.feedback{color:var(--subtle);margin:10px 0 0;font-size:11px}.table-search{min-width:min(300px,42vw);padding:7px 9px}.policy-table-wrap{width:100%;overflow-x:auto}.policy-table{border-collapse:collapse;width:100%;font-size:11px}.policy-table th{color:var(--faint);text-align:left;letter-spacing:.06em;text-transform:uppercase;font-size:9.5px}.policy-table th,.policy-table td{border-bottom:1px solid var(--line);vertical-align:middle;min-width:120px;padding:10px 8px}.policy-table th:first-child,.policy-table td:first-child{min-width:190px}.policy-table th:last-child,.policy-table td:last-child{min-width:150px}.policy-table td strong,.policy-table td code{display:block}.policy-table td code{color:var(--faint);margin-top:2px;font-size:9.5px}.policy-table td small{color:var(--faint);margin-top:2px;font-size:9.5px;display:block}.chat-add{grid-template-columns:minmax(190px,1fr) 140px auto;align-items:end;gap:9px;margin-top:14px;display:grid}.workspace-browser{border-block:1px solid var(--line);background:0 0;flex:1;grid-template-columns:minmax(240px,32%) minmax(0,1fr);min-width:0;min-height:0;display:grid;overflow:hidden}.workspace-side{border-right:1px solid var(--line);flex-direction:column;min-width:0;min-height:0;display:flex}.breadcrumbs{border-bottom:1px solid var(--line);white-space:nowrap;align-items:center;gap:3px;min-height:42px;padding:7px 10px;display:flex;overflow-x:auto}.breadcrumbs span{align-items:center;display:inline-flex}.breadcrumbs button{color:var(--accent);background:0 0;border:0;font-size:10.5px}.workspace-list{overscroll-behavior:contain;scrollbar-gutter:stable;flex:1 1 0;min-height:0;padding:6px;overflow:auto}.workspace-list>button{width:100%;min-width:0;color:var(--ink);text-align:left;background:0 0;border:0;border-radius:7px;grid-template-columns:18px minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 8px;font-size:11.5px;display:grid}.workspace-list>button:hover{background:var(--accent-soft)}.workspace-list>button span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.workspace-list>button small{color:var(--faint);font-size:9.5px}.workspace-preview{overscroll-behavior:contain;scrollbar-gutter:stable;min-width:0;min-height:0;overflow:auto}.workspace-preview>header{border-bottom:1px solid var(--line);background:var(--panel);justify-content:space-between;gap:12px;padding:11px 14px;display:flex;position:sticky;top:0}.workspace-preview header small{color:var(--faint)}.workspace-preview pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:16px;font:11.5px/1.65 ui-monospace,monospace}.logs-section{max-width:920px}.error-stack{border-top:1px solid var(--line);margin-top:18px;padding-top:14px}.error-stack h4{margin:0 0 8px;font-size:12px}.error-stack>div{color:var(--danger);grid-template-columns:16px minmax(0,1fr) auto;align-items:center;gap:8px;padding:7px 0;font-size:11px;display:grid}.empty-state{min-width:0;min-height:120px;color:var(--subtle);text-align:center;align-content:center;place-items:center;padding:20px;display:grid}.empty-state strong{color:var(--ink);font:600 15px Georgia,serif}.empty-state p{max-width:460px;margin:4px 0 0;font-size:11px}.page-loading{min-height:50vh;color:var(--subtle);justify-content:center;align-items:center;gap:10px;display:flex}.spin{animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.sheet-overlay{z-index:50;background:#14100d7a;animation:.15s ease-out fade-in;position:fixed;inset:0}.sheet-content{z-index:51;border-left:1px solid var(--line);background:var(--panel);width:min(440px,94vw);max-width:100%;color:var(--ink);box-shadow:var(--shadow);padding:22px;animation:.2s ease-out slide-in;position:fixed;inset:0 0 0 auto;overflow-y:auto}.sheet-head{border-bottom:1px solid var(--line);justify-content:space-between;align-items:flex-start;gap:16px;padding-bottom:18px;display:flex}.sheet-head h2{margin:0;font:600 24px Georgia,serif}.sheet-head p{color:var(--subtle);margin:4px 0 0;font-size:11.5px}.settings-form{padding-top:18px}.field-help{color:var(--subtle);font-size:11px}.compact-sidebar{padding:16px 0}@keyframes fade-in{0%{opacity:0}}@keyframes slide-in{0%{opacity:.5;transform:translate(24px)}}@media (width>=1600px){.metrics-band{grid-template-columns:repeat(7,minmax(0,1fr))}.metrics-band .metric-stat{border-right:1px solid var(--line);border-bottom:0}.metrics-band .metric-stat:last-child{border-right:0}}@media (width<=1100px){.config-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=760px){.app-shell{display:block}.app-shell.workspace-app-active{height:auto;min-height:100vh;overflow:visible}.desktop-sidebar{display:none}.mobile-menu{display:inline-flex!important}.app-topbar{height:58px;padding:0 14px}.app-topbar>div>strong{display:inline}.agent-header{padding:17px 16px 12px;display:block;top:58px}.agent-header-summary{justify-content:flex-start;max-width:none;margin-top:12px}.agent-tabs{padding:0 8px;top:150px}.agent-tabs button{padding-inline:10px}.agent-content{padding:16px 14px 48px}.metrics-band{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics-band .metric-stat{border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-height:116px;padding:13px 10px}.metric-stat:nth-child(2n){border-right:0}.metric-stat:nth-last-child(-n+1){border-bottom:0}.overview-sections{grid-template-columns:1fr;gap:22px}.config-grid{grid-template-columns:1fr}.section-heading{display:block}.section-heading>.ui-badge,.section-heading>.table-search{margin-top:9px}.table-search{width:100%;min-width:0}.policy-table{display:block}.policy-table thead{clip:rect(0, 0, 0, 0);width:1px;height:1px;position:absolute;overflow:hidden}.policy-table tbody{border-top:1px solid var(--line);gap:0;display:grid}.policy-table tr{border:0;border-bottom:1px solid var(--line);gap:8px;min-width:0;padding:12px 0;display:grid}.policy-table td,.policy-table td:first-child,.policy-table td:last-child{border:0;min-width:0;padding:0}.policy-table td:before{color:var(--faint);text-transform:uppercase;margin-bottom:3px;font-size:9px;display:block}.policy-table td:nth-child(2):before{content:"消息策略"}.policy-table td:nth-child(3):before{content:"操作"}.chat-add{grid-template-columns:1fr}.workspace-shell,.workspace-shell.workspace-route-active{height:auto;min-height:100vh;display:block;overflow:visible}.agent-content.workspace-active{height:auto;min-height:0;display:block;overflow:visible}.workspace-tab-panel{display:block}.workspace-browser{grid-template-columns:1fr;height:auto;min-height:0;overflow:hidden}.workspace-side{border-right:0;border-bottom:1px solid var(--line);height:min(300px,42dvh)}.workspace-preview{height:min(520px,58dvh);min-height:280px}.workspace-list,.workspace-preview{overscroll-behavior:auto}.stale-banner,.notice{margin-inline:14px}}@media (width<=420px){.metrics-band{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics-band .metric-stat{min-height:94px}.metrics-band .metric-stat:last-child{border-bottom:0;border-right:0;grid-column:1/-1}.agent-header-summary span:nth-of-type(n+4),.app-topbar>div>span{display:none}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}
2
+ @layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.hidden{display:none}}:root{--paper:#f4f0e8;--panel:#fbf8f1;--ink:#201c18;--subtle:#6e675e;--faint:#989084;--line:#ddd5c7;--accent:#a3402c;--accent-soft:#f2e3dc;--success:#2d6a4f;--warning:#9a6700;--danger:#a3402c;--shadow:0 14px 44px #33281f14;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;font-synthesis:none;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}@media (prefers-color-scheme:dark){:root{--paper:#181513;--panel:#211d1a;--ink:#eee7dc;--subtle:#aaa195;--faint:#766d63;--line:#37302a;--accent:#d17862;--accent-soft:#3a2420;--success:#75b798;--warning:#e0ad55;--shadow:0 14px 44px #0000003d;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}}*{box-sizing:border-box}html,body,#root{min-height:100%}html{background:var(--paper);overflow-x:clip}body{background:var(--paper);color:var(--ink);margin:0;overflow-x:clip}button,input,select{font:inherit}button,select{cursor:pointer}button:disabled,select:disabled,input:disabled{cursor:not-allowed;opacity:.55}[hidden]{display:none!important}:focus-visible{outline:3px solid var(--accent)}@supports (color:color-mix(in lab, red, red)){:focus-visible{outline:3px solid color-mix(in srgb, var(--accent) 40%, transparent)}}:focus-visible{outline-offset:2px}.sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.app-shell{grid-template-columns:292px minmax(0,1fr);min-height:100vh;display:grid}.app-shell.workspace-app-active{height:100dvh;min-height:0;overflow:hidden}.desktop-sidebar{border-right:1px solid var(--line);background:var(--panel);flex-direction:column;min-width:0;height:100vh;display:flex;position:sticky;top:0}@supports (color:color-mix(in lab, red, red)){.desktop-sidebar{background:color-mix(in srgb, var(--panel) 94%, transparent)}}.brand{border-bottom:1px solid var(--line);align-items:center;gap:12px;height:78px;padding:18px 22px;display:flex}.brand img{width:38px;height:38px}.brand strong{color:var(--accent);letter-spacing:-.03em;font:600 22px Georgia,serif}.brand-version{color:var(--faint);letter-spacing:0;white-space:nowrap;font:600 10px ui-monospace,monospace}.desktop-sidebar footer{border-top:1px solid var(--line);color:var(--faint);padding:13px 18px;font-size:11px}.sidebar-inner{flex-direction:column;flex:1;min-height:0;padding:14px 12px;display:flex}.agent-search,.table-search{border:1px solid var(--line);background:var(--paper);color:var(--faint);border-radius:9px;align-items:center;gap:8px;display:flex}.agent-search{margin-bottom:12px;padding:9px 11px}.agent-search input,.table-search input{width:100%;min-width:0;color:var(--ink);background:0 0;border:0;outline:0}.agent-list{align-content:start;gap:6px;min-height:0;display:grid;overflow-y:auto}.agent-list-item{width:100%;min-width:0;color:var(--subtle);text-align:left;background:0 0;border:1px solid #0000;border-radius:11px;grid-template-columns:42px minmax(0,1fr) 16px;align-items:center;gap:10px;padding:10px;display:grid}.agent-list-item:hover{background:var(--ink)}@supports (color:color-mix(in lab, red, red)){.agent-list-item:hover{background:color-mix(in srgb, var(--ink) 4%, transparent)}}.agent-list-item:hover{color:var(--ink)}.agent-list-item.selected{border-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.agent-list-item.selected{border-color:color-mix(in srgb, var(--accent) 42%, var(--line))}}.agent-list-item.selected{background:var(--accent-soft);color:var(--ink)}.agent-list-copy{gap:1px;min-width:0;display:grid}.agent-list-copy strong,.agent-list-copy small,.status-line{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.agent-list-copy strong{font-size:13px}.agent-list-copy small{color:var(--faint);font-size:10.5px}.status-line{color:var(--subtle);align-items:center;gap:4px;font-size:10px;display:flex}.agent-avatar{border:1px solid var(--line);background:var(--accent);border-radius:50%;flex:none;place-items:center;width:42px;height:42px;display:grid;position:relative;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.agent-avatar{background:color-mix(in srgb, var(--accent) 12%, var(--panel))}}.agent-avatar{color:var(--accent);font:700 13px Georgia,serif}.agent-avatar img{object-fit:cover;width:100%;height:100%;display:block}.danger{color:var(--danger)}.workspace-shell{flex-direction:column;min-width:0;min-height:100vh;display:flex}.workspace-shell.workspace-route-active{height:100dvh;min-height:0;overflow:hidden}.workspace-route-active>.app-topbar,.workspace-route-active>.agent-header,.workspace-route-active>.agent-tabs,.workspace-route-active>.stale-banner,.workspace-route-active>.notice{flex:none}.app-topbar{z-index:20;border-bottom:1px solid var(--line);background:var(--paper);justify-content:space-between;align-items:center;gap:16px;height:64px;padding:0 28px;display:flex;position:sticky;top:0}@supports (color:color-mix(in lab, red, red)){.app-topbar{background:color-mix(in srgb, var(--paper) 94%, transparent)}}.app-topbar{-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.app-topbar>div{align-items:center;gap:10px;min-width:0;display:flex}.app-topbar>div>strong{color:var(--accent);font:600 19px Georgia,serif;display:none}.app-topbar>div>span{color:var(--subtle);font-size:12px}.mobile-menu{display:none!important}.ui-button{border:1px solid var(--line);background:var(--panel);min-height:34px;color:var(--ink);border-radius:8px;justify-content:center;align-items:center;gap:7px;padding:7px 11px;font-size:12px;font-weight:600;display:inline-flex}.ui-button:hover{border-color:var(--accent);background:var(--accent-soft)}.ui-button.primary{border-color:var(--accent);background:var(--accent);color:#fff}.ui-button.icon-button{width:36px;padding:0}.ui-badge{border:1px solid var(--line);color:var(--subtle);background:var(--ink);border-radius:999px;align-items:center;padding:2px 8px;display:inline-flex}@supports (color:color-mix(in lab, red, red)){.ui-badge{background:color-mix(in srgb, var(--ink) 3%, var(--panel))}}.ui-badge{white-space:nowrap;font-size:10.5px;font-weight:600}.ui-badge.success{border-color:var(--success)}@supports (color:color-mix(in lab, red, red)){.ui-badge.success{border-color:color-mix(in srgb, var(--success) 55%, var(--line))}}.ui-badge.success{color:var(--success)}.ui-badge.warning{border-color:var(--warning)}@supports (color:color-mix(in lab, red, red)){.ui-badge.warning{border-color:color-mix(in srgb, var(--warning) 55%, var(--line))}}.ui-badge.warning{color:var(--warning)}.ui-badge.accent,.ui-badge.danger-badge{border-color:var(--accent)}@supports (color:color-mix(in lab, red, red)){.ui-badge.accent,.ui-badge.danger-badge{border-color:color-mix(in srgb, var(--accent) 55%, var(--line))}}.ui-badge.accent,.ui-badge.danger-badge{color:var(--accent)}.stale-banner,.notice{border:1px solid var(--warning);align-items:center;gap:8px;margin:16px 28px 0;display:flex}@supports (color:color-mix(in lab, red, red)){.stale-banner,.notice{border:1px solid color-mix(in srgb, var(--warning) 50%, var(--line))}}.stale-banner,.notice{background:var(--warning);border-radius:9px;padding:8px 11px}@supports (color:color-mix(in lab, red, red)){.stale-banner,.notice{background:color-mix(in srgb, var(--warning) 7%, var(--panel))}}.stale-banner,.notice{color:var(--warning);font-size:12px}.stale-banner .ui-button,.notice .ui-button{min-height:28px;margin-left:auto;padding-block:4px}.notice{border-color:var(--line);background:var(--panel);color:var(--subtle)}.agent-header{z-index:15;background:var(--paper);justify-content:space-between;align-items:center;gap:28px;min-width:0;padding:24px 30px 18px;display:flex;position:sticky;top:64px}@supports (color:color-mix(in lab, red, red)){.agent-header{background:color-mix(in srgb, var(--paper) 96%, transparent)}}.agent-header{-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}.agent-title{align-items:center;gap:13px;min-width:0;display:flex}.agent-title .agent-avatar{width:52px;height:52px}.agent-title div{min-width:0}.agent-title p{text-overflow:ellipsis;color:var(--faint);margin:0;font:10px ui-monospace,monospace;overflow:hidden}.agent-title h1{text-overflow:ellipsis;white-space:nowrap;letter-spacing:-.025em;margin:1px 0 0;font:600 clamp(23px,3vw,34px) Georgia,serif;overflow:hidden}.agent-header-summary{min-width:0;max-width:62%;color:var(--subtle);flex-wrap:wrap;justify-content:flex-end;gap:6px 14px;font-size:11.5px;display:flex}.agent-tabs{z-index:14;border-bottom:1px solid var(--line);background:var(--paper);gap:2px;padding:0 30px;display:flex;position:sticky;top:158px}@supports (color:color-mix(in lab, red, red)){.agent-tabs{background:color-mix(in srgb, var(--paper) 96%, transparent)}}.agent-tabs{scrollbar-width:thin;overflow-x:auto}.agent-tabs button{color:var(--subtle);background:0 0;border:0;border-bottom:2px solid #0000;flex:none;align-items:center;gap:7px;padding:11px 14px 9px;font-size:12px;font-weight:600;display:inline-flex}.agent-tabs button svg{width:14px;height:14px}.agent-tabs button:hover{color:var(--ink)}.agent-tabs button.active{color:var(--accent);border-bottom-color:var(--accent)}.agent-content{min-width:0;padding:24px 30px 64px}.agent-content>section{min-width:0}.agent-content.workspace-active{flex:1 1 0;height:auto;min-height:0;padding-bottom:24px;display:flex;overflow:hidden}.workspace-tab-panel{flex:1;min-height:0;display:flex}.overview-page{gap:28px;display:grid}.metrics-band{border-block:1px solid var(--line);grid-template-columns:repeat(4,minmax(0,1fr));min-width:0;display:grid}.metric-stat{flex-direction:column;min-width:0;min-height:126px;padding:16px 14px;display:flex}.metric-stat:not(:last-child){border-right:1px solid var(--line)}.metric-stat{border-bottom:1px solid var(--line)}.metric-stat:nth-child(4n){border-right:0}.metric-stat:nth-last-child(-n+3){border-bottom:0}.metric-stat>span{color:var(--faint);letter-spacing:.08em;text-transform:uppercase;margin-top:10px;font-size:9.5px;font-weight:700}.metric-stat>strong{text-overflow:ellipsis;margin-top:3px;font:600 18px Georgia,serif;overflow:hidden}.metric-stat>small{color:var(--subtle);margin-top:auto;font-size:10px}.metric-icon{width:20px;height:20px;color:var(--accent);place-items:center;display:grid}.metric-icon svg{width:15px;height:15px}.overview-sections{grid-template-columns:minmax(0,3fr) minmax(220px,1fr);gap:28px;min-width:0;display:grid}.content-section,.config-section,.group-policy-section,.logs-section{border-block:1px solid var(--line);min-width:0;padding:18px 0}.content-section h3,.config-section h3,.group-policy-section h3,.logs-section h3{margin:0;font:600 17px Georgia,serif}.section-heading{justify-content:space-between;align-items:flex-start;gap:16px;min-width:0;margin-bottom:14px;display:flex}.section-heading p{color:var(--subtle);margin:2px 0 0;font-size:11px}.facts{gap:0;margin:14px 0 0;display:grid}.facts div{border-top:1px solid var(--line);justify-content:space-between;align-items:baseline;gap:16px;padding:9px 0;display:flex}.facts dt{color:var(--subtle);font-size:11px}.facts dd{text-overflow:ellipsis;min-width:0;margin:0;font-size:12px;font-weight:600;overflow:hidden}.timeline{gap:0;margin:0;padding:0;list-style:none;display:grid}.timeline li{grid-template-columns:14px minmax(0,1fr);gap:10px;padding:8px 0;display:grid;position:relative}.timeline li:not(:last-child):before{content:"";background:var(--line);width:1px;position:absolute;top:21px;bottom:-7px;left:5px}.timeline-dot{z-index:1;border:2px solid var(--panel);background:var(--subtle);width:11px;height:11px;box-shadow:0 0 0 1px var(--line);border-radius:50%;margin-top:4px;position:relative}.timeline-dot.state-active{background:var(--warning)}.timeline-dot.state-idle{background:var(--success)}.timeline-dot.state-deliver{background:#3977b8}.timeline-dot.state-error{background:var(--danger)}.timeline-dot.state-unknown{background:var(--faint)}.timeline strong{font-size:11.5px}.timeline p{color:var(--subtle);overflow-wrap:anywhere;margin:1px 0;font-size:11px}.timeline time,.error-stack time{color:var(--faint);font-size:9.5px}.conversation-list,.reminder-list{border-top:1px solid var(--line);gap:0;max-width:900px;display:grid}.conversation-item,.reminder-list article{border:0;border-bottom:1px solid var(--line);background:0 0;padding:14px 10px}.conversation-item.outbound{border-left:3px solid var(--accent)}.conversation-item.inbound{border-left:3px solid var(--success)}.conversation-item.listening{border-left-color:var(--warning)}.conversation-item header{justify-content:space-between;gap:16px;display:flex}.conversation-item header>div{align-items:center;gap:8px;display:flex}.conversation-item header strong{font-size:12px}.conversation-item time,.conversation-item small{color:var(--faint);font-size:10px}.conversation-item p{white-space:pre-wrap;overflow-wrap:anywhere;margin:7px 0 0;font:13px/1.65 Georgia,serif}.reminder-list article{grid-template-columns:20px minmax(0,1fr) auto;align-items:center;gap:10px;display:grid}.reminder-list article.status-scheduled,.reminder-list article.status-pending{border-left:3px solid var(--warning)}.reminder-list article.status-fired{border-left:3px solid var(--success)}.reminder-list article.status-canceled{border-left:3px solid var(--faint)}.reminder-list article.status-failed{border-left:3px solid var(--danger)}.reminder-list article.done{opacity:.7;text-decoration:line-through}.reminder-list strong{font-size:12px}.reminder-list p{color:var(--subtle);margin:2px 0 0;font-size:10.5px}.configuration-page{gap:18px;display:grid}.cli-guidance{border-left:2px solid var(--accent);color:var(--subtle);margin:0 0 14px;padding:6px 10px;font-size:11px;line-height:1.55}.cli-guidance code{color:var(--ink);overflow-wrap:anywhere;font:600 10.5px ui-monospace,monospace}.config-grid{grid-template-columns:repeat(4,minmax(120px,1fr));gap:12px;display:grid}.provider-grid{grid-template-columns:repeat(2,minmax(160px,1fr))}.provider-status{color:var(--subtle);margin:0 0 12px;font-size:11px;line-height:1.5}.provider-logout{margin-top:12px}.config-grid label,.settings-form label,.chat-add label{min-width:0;color:var(--subtle);gap:5px;font-size:11px;font-weight:600;display:grid}.config-grid select,.config-grid input,.settings-form select,.chat-add select,.chat-add input,.policy-table select{border:1px solid var(--line);background:var(--paper);width:100%;min-width:0;min-height:37px;color:var(--ink);border-radius:8px;padding:7px 9px;font-size:12px}.config-grid input:disabled{color:var(--subtle)}.form-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:16px;display:flex}.feedback{color:var(--subtle);margin:10px 0 0;font-size:11px}.table-search{min-width:min(300px,42vw);padding:7px 9px}.policy-table-wrap{width:100%;overflow-x:auto}.policy-table{border-collapse:collapse;width:100%;font-size:11px}.policy-table th{color:var(--faint);text-align:left;letter-spacing:.06em;text-transform:uppercase;font-size:9.5px}.policy-table th,.policy-table td{border-bottom:1px solid var(--line);vertical-align:middle;min-width:120px;padding:10px 8px}.policy-table th:first-child,.policy-table td:first-child{min-width:190px}.policy-table th:last-child,.policy-table td:last-child{min-width:150px}.policy-table td strong,.policy-table td code{display:block}.policy-table td code{color:var(--faint);margin-top:2px;font-size:9.5px}.policy-table td small{color:var(--faint);margin-top:2px;font-size:9.5px;display:block}.chat-add{grid-template-columns:minmax(190px,1fr) 140px auto;align-items:end;gap:9px;margin-top:14px;display:grid}.workspace-browser{border-block:1px solid var(--line);background:0 0;flex:1;grid-template-columns:minmax(240px,32%) minmax(0,1fr);min-width:0;min-height:0;display:grid;overflow:hidden}.workspace-side{border-right:1px solid var(--line);flex-direction:column;min-width:0;min-height:0;display:flex}.breadcrumbs{border-bottom:1px solid var(--line);white-space:nowrap;align-items:center;gap:3px;min-height:42px;padding:7px 10px;display:flex;overflow-x:auto}.breadcrumbs span{align-items:center;display:inline-flex}.breadcrumbs button{color:var(--accent);background:0 0;border:0;font-size:10.5px}.workspace-list{overscroll-behavior:contain;scrollbar-gutter:stable;flex:1 1 0;min-height:0;padding:6px;overflow:auto}.workspace-list>button{width:100%;min-width:0;color:var(--ink);text-align:left;background:0 0;border:0;border-radius:7px;grid-template-columns:18px minmax(0,1fr) auto;align-items:center;gap:7px;padding:7px 8px;font-size:11.5px;display:grid}.workspace-list>button:hover{background:var(--accent-soft)}.workspace-list>button span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.workspace-list>button small{color:var(--faint);font-size:9.5px}.workspace-preview{overscroll-behavior:contain;scrollbar-gutter:stable;min-width:0;min-height:0;overflow:auto}.workspace-preview>header{border-bottom:1px solid var(--line);background:var(--panel);justify-content:space-between;gap:12px;padding:11px 14px;display:flex;position:sticky;top:0}.workspace-preview header small{color:var(--faint)}.workspace-preview pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:16px;font:11.5px/1.65 ui-monospace,monospace}.logs-section{max-width:920px}.error-stack{border-top:1px solid var(--line);margin-top:18px;padding-top:14px}.error-stack h4{margin:0 0 8px;font-size:12px}.error-stack>div{color:var(--danger);grid-template-columns:16px minmax(0,1fr) auto;align-items:center;gap:8px;padding:7px 0;font-size:11px;display:grid}.empty-state{min-width:0;min-height:120px;color:var(--subtle);text-align:center;align-content:center;place-items:center;padding:20px;display:grid}.empty-state strong{color:var(--ink);font:600 15px Georgia,serif}.empty-state p{max-width:460px;margin:4px 0 0;font-size:11px}.page-loading{min-height:50vh;color:var(--subtle);justify-content:center;align-items:center;gap:10px;display:flex}.spin{animation:1s linear infinite spin}@keyframes spin{to{transform:rotate(360deg)}}.sheet-overlay{z-index:50;background:#14100d7a;animation:.15s ease-out fade-in;position:fixed;inset:0}.sheet-content{z-index:51;border-left:1px solid var(--line);background:var(--panel);width:min(440px,94vw);max-width:100%;color:var(--ink);box-shadow:var(--shadow);padding:22px;animation:.2s ease-out slide-in;position:fixed;inset:0 0 0 auto;overflow-y:auto}.sheet-head{border-bottom:1px solid var(--line);justify-content:space-between;align-items:flex-start;gap:16px;padding-bottom:18px;display:flex}.sheet-head h2{margin:0;font:600 24px Georgia,serif}.sheet-head p{color:var(--subtle);margin:4px 0 0;font-size:11.5px}.settings-form{padding-top:18px}.field-help{color:var(--subtle);font-size:11px}.compact-sidebar{padding:16px 0}@keyframes fade-in{0%{opacity:0}}@keyframes slide-in{0%{opacity:.5;transform:translate(24px)}}@media (width>=1600px){.metrics-band{grid-template-columns:repeat(7,minmax(0,1fr))}.metrics-band .metric-stat{border-right:1px solid var(--line);border-bottom:0}.metrics-band .metric-stat:last-child{border-right:0}}@media (width<=1100px){.config-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=760px){.app-shell{display:block}.app-shell.workspace-app-active{height:auto;min-height:100vh;overflow:visible}.desktop-sidebar{display:none}.mobile-menu{display:inline-flex!important}.app-topbar{height:58px;padding:0 14px}.app-topbar>div>strong{display:inline}.agent-header{padding:17px 16px 12px;display:block;top:58px}.agent-header-summary{justify-content:flex-start;max-width:none;margin-top:12px}.agent-tabs{padding:0 8px;top:150px}.agent-tabs button{padding-inline:10px}.agent-content{padding:16px 14px 48px}.metrics-band{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics-band .metric-stat{border-right:1px solid var(--line);border-bottom:1px solid var(--line);min-height:116px;padding:13px 10px}.metric-stat:nth-child(2n){border-right:0}.metric-stat:nth-last-child(-n+1){border-bottom:0}.overview-sections{grid-template-columns:1fr;gap:22px}.config-grid{grid-template-columns:1fr}.section-heading{display:block}.section-heading>.ui-badge,.section-heading>.table-search{margin-top:9px}.table-search{width:100%;min-width:0}.policy-table{display:block}.policy-table thead{clip:rect(0, 0, 0, 0);width:1px;height:1px;position:absolute;overflow:hidden}.policy-table tbody{border-top:1px solid var(--line);gap:0;display:grid}.policy-table tr{border:0;border-bottom:1px solid var(--line);gap:8px;min-width:0;padding:12px 0;display:grid}.policy-table td,.policy-table td:first-child,.policy-table td:last-child{border:0;min-width:0;padding:0}.policy-table td:before{color:var(--faint);text-transform:uppercase;margin-bottom:3px;font-size:9px;display:block}.policy-table td:nth-child(2):before{content:"消息策略"}.policy-table td:nth-child(3):before{content:"操作"}.chat-add{grid-template-columns:1fr}.workspace-shell,.workspace-shell.workspace-route-active{height:auto;min-height:100vh;display:block;overflow:visible}.agent-content.workspace-active{height:auto;min-height:0;display:block;overflow:visible}.workspace-tab-panel{display:block}.workspace-browser{grid-template-columns:1fr;height:auto;min-height:0;overflow:hidden}.workspace-side{border-right:0;border-bottom:1px solid var(--line);height:min(300px,42dvh)}.workspace-preview{height:min(520px,58dvh);min-height:280px}.workspace-list,.workspace-preview{overscroll-behavior:auto}.stale-banner,.notice{margin-inline:14px}}@media (width<=420px){.metrics-band{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics-band .metric-stat{min-height:94px}.metrics-band .metric-stat:last-child{border-bottom:0;border-right:0;grid-column:1/-1}.agent-header-summary span:nth-of-type(n+4),.app-topbar>div>span{display:none}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}
3
3
  /*$vite$:1*/